apify-test-tools 0.10.1-beta.2 → 0.10.1-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/actions/checkout-restore-dependencies/action.yaml +4 -3
- package/.github/workflows/public_platform-tests-claude-investigate-and-fix.yaml +7 -2
- package/.github/workflows/public_platform-tests.yaml +0 -1
- package/.github/workflows-package-version +1 -1
- package/CHANGELOG.md +6 -0
- package/CONTRIBUTING.md +13 -3
- package/README.md +2 -2
- package/bin/build-from-local.ts +8 -7
- package/bin/build.ts +22 -21
- package/bin/diff-changes.ts +11 -10
- package/bin/diff-json-schema.ts +14 -12
- package/bin/git.ts +89 -47
- package/bin/logger.ts +23 -0
- package/bin/main.ts +44 -38
- package/bin/output.ts +6 -0
- package/bin/slack.ts +8 -7
- package/bin/test-report.ts +22 -21
- package/bin/types.ts +0 -1
- package/bin/utils/config/actor-json.ts +84 -0
- package/bin/utils/config/load-config.ts +46 -46
- package/bin/utils/config/parser.ts +33 -28
- package/bin/utils/config/structures/base.ts +7 -0
- package/bin/utils/json-file.ts +11 -14
- package/bin/utils/path/repo-relative.ts +156 -0
- package/bin/utils/path/schema.ts +18 -0
- package/bin/utils.ts +63 -44
- package/dist/bin/build-from-local.d.ts +1 -1
- package/dist/bin/build-from-local.d.ts.map +1 -1
- package/dist/bin/build-from-local.js +8 -7
- package/dist/bin/build-from-local.js.map +1 -1
- package/dist/bin/build.d.ts.map +1 -1
- package/dist/bin/build.js +22 -21
- package/dist/bin/build.js.map +1 -1
- package/dist/bin/diff-changes.d.ts +1 -1
- package/dist/bin/diff-changes.d.ts.map +1 -1
- package/dist/bin/diff-changes.js +9 -8
- package/dist/bin/diff-changes.js.map +1 -1
- package/dist/bin/diff-json-schema.d.ts +1 -1
- package/dist/bin/diff-json-schema.d.ts.map +1 -1
- package/dist/bin/diff-json-schema.js +13 -12
- package/dist/bin/diff-json-schema.js.map +1 -1
- package/dist/bin/git.d.ts +10 -10
- package/dist/bin/git.d.ts.map +1 -1
- package/dist/bin/git.js +78 -37
- package/dist/bin/git.js.map +1 -1
- package/dist/bin/logger.d.ts +11 -0
- package/dist/bin/logger.d.ts.map +1 -0
- package/dist/bin/logger.js +20 -0
- package/dist/bin/logger.js.map +1 -0
- package/dist/bin/main.d.ts +1 -1
- package/dist/bin/main.js +44 -38
- package/dist/bin/main.js.map +1 -1
- package/dist/bin/output.d.ts +3 -0
- package/dist/bin/output.d.ts.map +1 -0
- package/dist/bin/output.js +6 -0
- package/dist/bin/output.js.map +1 -0
- package/dist/bin/slack.d.ts.map +1 -1
- package/dist/bin/slack.js +8 -7
- package/dist/bin/slack.js.map +1 -1
- package/dist/bin/test-report.d.ts.map +1 -1
- package/dist/bin/test-report.js +22 -21
- package/dist/bin/test-report.js.map +1 -1
- package/dist/bin/types.d.ts +0 -1
- package/dist/bin/types.d.ts.map +1 -1
- package/dist/bin/utils/config/actor-json.d.ts +14 -0
- package/dist/bin/utils/config/actor-json.d.ts.map +1 -0
- package/dist/bin/utils/config/actor-json.js +60 -0
- package/dist/bin/utils/config/actor-json.js.map +1 -0
- package/dist/bin/utils/config/load-config.d.ts +12 -2
- package/dist/bin/utils/config/load-config.d.ts.map +1 -1
- package/dist/bin/utils/config/load-config.js +32 -31
- package/dist/bin/utils/config/load-config.js.map +1 -1
- package/dist/bin/utils/config/parser.d.ts +4 -12
- package/dist/bin/utils/config/parser.d.ts.map +1 -1
- package/dist/bin/utils/config/parser.js +24 -23
- package/dist/bin/utils/config/parser.js.map +1 -1
- package/dist/bin/utils/config/structures/base.d.ts +5 -0
- package/dist/bin/utils/config/structures/base.d.ts.map +1 -1
- package/dist/bin/utils/config/structures/base.js.map +1 -1
- package/dist/bin/utils/json-file.d.ts +3 -2
- package/dist/bin/utils/json-file.d.ts.map +1 -1
- package/dist/bin/utils/json-file.js +8 -13
- package/dist/bin/utils/json-file.js.map +1 -1
- package/dist/bin/utils/path/repo-relative.d.ts +35 -0
- package/dist/bin/utils/path/repo-relative.d.ts.map +1 -0
- package/dist/bin/utils/path/repo-relative.js +137 -0
- package/dist/bin/utils/path/repo-relative.js.map +1 -0
- package/dist/bin/utils/path/schema.d.ts +7 -0
- package/dist/bin/utils/path/schema.d.ts.map +1 -0
- package/dist/bin/utils/path/schema.js +16 -0
- package/dist/bin/utils/path/schema.js.map +1 -0
- package/dist/bin/utils.d.ts +14 -6
- package/dist/bin/utils.d.ts.map +1 -1
- package/dist/bin/utils.js +52 -36
- package/dist/bin/utils.js.map +1 -1
- package/dist/test/unit/bin/build-from-local.test.js +4 -40
- package/dist/test/unit/bin/build-from-local.test.js.map +1 -1
- package/dist/test/unit/bin/diff-changes.test.js +97 -96
- package/dist/test/unit/bin/diff-changes.test.js.map +1 -1
- package/dist/test/unit/bin/diff.test.js +52 -48
- package/dist/test/unit/bin/diff.test.js.map +1 -1
- package/dist/test/unit/bin/git-command.test.d.ts +2 -0
- package/dist/test/unit/bin/git-command.test.d.ts.map +1 -0
- package/dist/test/unit/bin/git-command.test.js +54 -0
- package/dist/test/unit/bin/git-command.test.js.map +1 -0
- package/dist/test/unit/bin/git.test.js +211 -94
- package/dist/test/unit/bin/git.test.js.map +1 -1
- package/dist/test/unit/bin/logger.test.d.ts +2 -0
- package/dist/test/unit/bin/logger.test.d.ts.map +1 -0
- package/dist/test/unit/bin/logger.test.js +44 -0
- package/dist/test/unit/bin/logger.test.js.map +1 -0
- package/dist/test/unit/bin/logging.test.d.ts +2 -0
- package/dist/test/unit/bin/logging.test.d.ts.map +1 -0
- package/dist/test/unit/bin/logging.test.js +43 -0
- package/dist/test/unit/bin/logging.test.js.map +1 -0
- package/dist/test/unit/bin/utils/config/load-config.test.js +47 -25
- package/dist/test/unit/bin/utils/config/load-config.test.js.map +1 -1
- package/dist/test/unit/bin/utils/config/parser.test.js +64 -19
- package/dist/test/unit/bin/utils/config/parser.test.js.map +1 -1
- package/dist/test/unit/bin/utils/files.test.js +16 -27
- package/dist/test/unit/bin/utils/files.test.js.map +1 -1
- package/dist/test/unit/bin/utils/path/repo-relative.test.d.ts +2 -0
- package/dist/test/unit/bin/utils/path/repo-relative.test.d.ts.map +1 -0
- package/dist/test/unit/bin/utils/path/repo-relative.test.js +196 -0
- package/dist/test/unit/bin/utils/path/repo-relative.test.js.map +1 -0
- package/dist/test/unit/bin/utils/path/schema.test.d.ts +2 -0
- package/dist/test/unit/bin/utils/path/schema.test.d.ts.map +1 -0
- package/dist/test/unit/bin/utils/path/schema.test.js +52 -0
- package/dist/test/unit/bin/utils/path/schema.test.js.map +1 -0
- package/dist/test/unit/should-built-and-test.test.js +40 -38
- package/dist/test/unit/should-built-and-test.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/eslint.config.mjs +9 -1
- package/package.json +5 -5
- package/test/unit/bin/build-from-local.test.ts +4 -54
- package/test/unit/bin/diff-changes.test.ts +97 -96
- package/test/unit/bin/diff.test.ts +53 -48
- package/test/unit/bin/git-command.test.ts +71 -0
- package/test/unit/bin/git.test.ts +246 -91
- package/test/unit/bin/logger.test.ts +55 -0
- package/test/unit/bin/logging.test.ts +52 -0
- package/test/unit/bin/utils/config/load-config.test.ts +63 -26
- package/test/unit/bin/utils/config/parser.test.ts +81 -27
- package/test/unit/bin/utils/files.test.ts +19 -33
- package/test/unit/bin/utils/path/repo-relative.test.ts +250 -0
- package/test/unit/bin/utils/path/schema.test.ts +65 -0
- package/test/unit/should-built-and-test.test.ts +40 -38
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
normalizeRepoUrl,
|
|
13
13
|
parseBaseCommit,
|
|
14
14
|
resolveReleaseBaseCommit,
|
|
15
|
+
resolveRepoUrl,
|
|
15
16
|
} from '../../../bin/git.js';
|
|
16
17
|
import * as Utils from '../../../bin/utils.js';
|
|
17
18
|
|
|
@@ -30,14 +31,12 @@ describe('getCommits', () => {
|
|
|
30
31
|
let gitCommandSpy: MockInstance;
|
|
31
32
|
|
|
32
33
|
beforeEach(() => {
|
|
33
|
-
gitCommandSpy = vi
|
|
34
|
-
.spyOn(Utils, 'spawnCommandInGhWorkspace')
|
|
35
|
-
.mockReturnValue(`${commit3}\n${commit2}\n${commit1}`);
|
|
34
|
+
gitCommandSpy = vi.spyOn(Utils, 'runGitCommand').mockResolvedValue(`${commit3}\n${commit2}\n${commit1}`);
|
|
36
35
|
});
|
|
37
36
|
|
|
38
|
-
it('should return commits between source and target branches', () => {
|
|
37
|
+
it('should return commits between source and target branches', async () => {
|
|
39
38
|
// Act
|
|
40
|
-
const commits = getCommits({ sourceBranch, targetBranch });
|
|
39
|
+
const commits = await getCommits({ sourceBranch, targetBranch });
|
|
41
40
|
|
|
42
41
|
// Assert
|
|
43
42
|
expect(commits).toStrictEqual([
|
|
@@ -48,13 +47,14 @@ describe('getCommits', () => {
|
|
|
48
47
|
|
|
49
48
|
expect(gitCommandSpy).toHaveBeenCalledTimes(1);
|
|
50
49
|
expect(gitCommandSpy).toHaveBeenCalledWith(
|
|
51
|
-
|
|
50
|
+
['log', '--pretty=format:%H»¦«%aN<%aE>»¦«%aD»¦«%s', 'main..feature-branch'],
|
|
51
|
+
{ operation: 'list commits reachable from the source ref but absent from the target ref' },
|
|
52
52
|
);
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
-
it('should return commits after the base commit if provided', () => {
|
|
55
|
+
it('should return commits after the base commit if provided', async () => {
|
|
56
56
|
// Act
|
|
57
|
-
const commits = getCommits({ sourceBranch, targetBranch, baseCommit: sha1 });
|
|
57
|
+
const commits = await getCommits({ sourceBranch, targetBranch, baseCommit: sha1 });
|
|
58
58
|
|
|
59
59
|
// Assert
|
|
60
60
|
expect(commits).toStrictEqual([
|
|
@@ -64,13 +64,14 @@ describe('getCommits', () => {
|
|
|
64
64
|
|
|
65
65
|
expect(gitCommandSpy).toHaveBeenCalledTimes(1);
|
|
66
66
|
expect(gitCommandSpy).toHaveBeenCalledWith(
|
|
67
|
-
|
|
67
|
+
['log', '--pretty=format:%H»¦«%aN<%aE>»¦«%aD»¦«%s', 'main..feature-branch'],
|
|
68
|
+
{ operation: 'list commits reachable from the source ref but absent from the target ref' },
|
|
68
69
|
);
|
|
69
70
|
});
|
|
70
71
|
|
|
71
|
-
it('should ignore the base commit and return all commits when it is the branch HEAD (rerun or force push)', () => {
|
|
72
|
+
it('should ignore the base commit and return all commits when it is the branch HEAD (rerun or force push)', async () => {
|
|
72
73
|
// Act
|
|
73
|
-
const commits = getCommits({ sourceBranch, targetBranch, baseCommit: sha3 });
|
|
74
|
+
const commits = await getCommits({ sourceBranch, targetBranch, baseCommit: sha3 });
|
|
74
75
|
|
|
75
76
|
// Assert
|
|
76
77
|
expect(commits).toStrictEqual([
|
|
@@ -80,9 +81,9 @@ describe('getCommits', () => {
|
|
|
80
81
|
]);
|
|
81
82
|
});
|
|
82
83
|
|
|
83
|
-
it('should return all commits if base commit is not found', () => {
|
|
84
|
+
it('should return all commits if base commit is not found', async () => {
|
|
84
85
|
// Act
|
|
85
|
-
const commits = getCommits({ sourceBranch, targetBranch, baseCommit: 'a'.repeat(40) });
|
|
86
|
+
const commits = await getCommits({ sourceBranch, targetBranch, baseCommit: 'a'.repeat(40) });
|
|
86
87
|
|
|
87
88
|
// Assert
|
|
88
89
|
expect(commits).toStrictEqual([
|
|
@@ -93,7 +94,8 @@ describe('getCommits', () => {
|
|
|
93
94
|
|
|
94
95
|
expect(gitCommandSpy).toHaveBeenCalledTimes(1);
|
|
95
96
|
expect(gitCommandSpy).toHaveBeenCalledWith(
|
|
96
|
-
|
|
97
|
+
['log', '--pretty=format:%H»¦«%aN<%aE>»¦«%aD»¦«%s', 'main..feature-branch'],
|
|
98
|
+
{ operation: 'list commits reachable from the source ref but absent from the target ref' },
|
|
97
99
|
);
|
|
98
100
|
});
|
|
99
101
|
});
|
|
@@ -102,10 +104,10 @@ describe('getChangedFiles', () => {
|
|
|
102
104
|
let gitCommandSpy: MockInstance;
|
|
103
105
|
|
|
104
106
|
beforeEach(() => {
|
|
105
|
-
gitCommandSpy = vi.spyOn(Utils, '
|
|
107
|
+
gitCommandSpy = vi.spyOn(Utils, 'runGitCommand').mockResolvedValue('file1.txt\nfolder/file2.txt');
|
|
106
108
|
});
|
|
107
109
|
|
|
108
|
-
it('should return changed files between commits', () => {
|
|
110
|
+
it('should return changed files between commits', async () => {
|
|
109
111
|
// Arrange
|
|
110
112
|
const firstSha = '1'.repeat(40);
|
|
111
113
|
const lastSha = '3'.repeat(40);
|
|
@@ -115,46 +117,50 @@ describe('getChangedFiles', () => {
|
|
|
115
117
|
];
|
|
116
118
|
|
|
117
119
|
// Act
|
|
118
|
-
const changedFiles = getChangedFiles(commits);
|
|
120
|
+
const changedFiles = await getChangedFiles(commits);
|
|
119
121
|
|
|
120
122
|
// Assert
|
|
121
123
|
expect(changedFiles).toStrictEqual(['file1.txt', 'folder/file2.txt']);
|
|
122
124
|
|
|
123
125
|
expect(gitCommandSpy).toHaveBeenCalledTimes(1);
|
|
124
|
-
expect(gitCommandSpy).toHaveBeenCalledWith(
|
|
126
|
+
expect(gitCommandSpy).toHaveBeenCalledWith(['diff', '--name-only', `${firstSha}~..${lastSha}`], {
|
|
127
|
+
operation: 'get the list of changed files between commits',
|
|
128
|
+
});
|
|
125
129
|
});
|
|
126
130
|
|
|
127
|
-
it('should throw without running git when the commit list is empty', () => {
|
|
131
|
+
it('should throw without running git when the commit list is empty', async () => {
|
|
128
132
|
// Act & Assert
|
|
129
|
-
expect(
|
|
133
|
+
await expect(getChangedFiles([])).rejects.toThrow('Cannot get changed files: the commit list is empty');
|
|
130
134
|
expect(gitCommandSpy).not.toHaveBeenCalled();
|
|
131
135
|
});
|
|
132
136
|
|
|
133
|
-
it('should handle only one commit', () => {
|
|
137
|
+
it('should handle only one commit', async () => {
|
|
134
138
|
// Arrange
|
|
135
139
|
const onlySha = '1'.repeat(40);
|
|
136
140
|
const commits = [{ sha: onlySha, author: '', date: '', message: '' }];
|
|
137
141
|
|
|
138
142
|
// Act
|
|
139
|
-
const changedFiles = getChangedFiles(commits);
|
|
143
|
+
const changedFiles = await getChangedFiles(commits);
|
|
140
144
|
|
|
141
145
|
// Assert
|
|
142
146
|
expect(changedFiles).toStrictEqual(['file1.txt', 'folder/file2.txt']);
|
|
143
147
|
|
|
144
148
|
expect(gitCommandSpy).toHaveBeenCalledTimes(1);
|
|
145
|
-
expect(gitCommandSpy).toHaveBeenCalledWith(
|
|
149
|
+
expect(gitCommandSpy).toHaveBeenCalledWith(['diff', '--name-only', `${onlySha}~..${onlySha}`], {
|
|
150
|
+
operation: 'get the list of changed files between commits',
|
|
151
|
+
});
|
|
146
152
|
});
|
|
147
153
|
|
|
148
|
-
it('should return an empty list when the net diff is empty (e.g. a commit and its revert)', () => {
|
|
154
|
+
it('should return an empty list when the net diff is empty (e.g. a commit and its revert)', async () => {
|
|
149
155
|
// Arrange
|
|
150
|
-
gitCommandSpy.
|
|
156
|
+
gitCommandSpy.mockResolvedValue('');
|
|
151
157
|
const commits = [
|
|
152
158
|
{ sha: '1'.repeat(40), author: '', date: '', message: '' },
|
|
153
159
|
{ sha: '2'.repeat(40), author: '', date: '', message: '' },
|
|
154
160
|
];
|
|
155
161
|
|
|
156
162
|
// Act
|
|
157
|
-
const changedFiles = getChangedFiles(commits);
|
|
163
|
+
const changedFiles = await getChangedFiles(commits);
|
|
158
164
|
|
|
159
165
|
// Assert
|
|
160
166
|
expect(changedFiles).toStrictEqual([]);
|
|
@@ -167,49 +173,86 @@ describe('hasMergeFromTarget', () => {
|
|
|
167
173
|
const mergeSha = 'f'.repeat(40);
|
|
168
174
|
const branchParentSha = 'b'.repeat(40);
|
|
169
175
|
const targetParentSha = 't'.repeat(40);
|
|
176
|
+
const differentMergeBase = '0'.repeat(40);
|
|
170
177
|
|
|
171
178
|
let gitCommandSpy: MockInstance;
|
|
172
179
|
|
|
173
180
|
beforeEach(() => {
|
|
174
|
-
gitCommandSpy = vi.spyOn(Utils, '
|
|
181
|
+
gitCommandSpy = vi.spyOn(Utils, 'runGitCommand');
|
|
175
182
|
});
|
|
176
183
|
|
|
177
|
-
it('should return false when there are no merge commits on the branch', () => {
|
|
178
|
-
gitCommandSpy.mockImplementation((
|
|
179
|
-
if (
|
|
184
|
+
it('should return false when there are no merge commits on the branch', async () => {
|
|
185
|
+
gitCommandSpy.mockImplementation(async (args: string[]) => {
|
|
186
|
+
if (args.includes('--merges')) return '';
|
|
180
187
|
return '';
|
|
181
188
|
});
|
|
182
189
|
|
|
183
|
-
expect(hasMergeFromTarget(sourceBranch, targetBranch)).toBe(false);
|
|
190
|
+
await expect(hasMergeFromTarget(sourceBranch, targetBranch)).resolves.toBe(false);
|
|
184
191
|
expect(gitCommandSpy).toHaveBeenCalledWith(
|
|
185
|
-
|
|
192
|
+
['log', '--merges', '--pretty=format:%H', `${targetBranch}..${sourceBranch}`],
|
|
193
|
+
{ operation: 'find merge commits on the source branch that are absent from the target branch' },
|
|
186
194
|
);
|
|
187
195
|
});
|
|
188
196
|
|
|
189
|
-
it('should return true when a merge commit has a parent reachable from targetBranch', () => {
|
|
190
|
-
gitCommandSpy.mockImplementation((
|
|
191
|
-
if (
|
|
192
|
-
if (
|
|
193
|
-
if (
|
|
194
|
-
if (
|
|
197
|
+
it('should return true when a merge commit has a parent reachable from targetBranch', async () => {
|
|
198
|
+
gitCommandSpy.mockImplementation(async (args: string[]) => {
|
|
199
|
+
if (args.includes('--merges')) return mergeSha;
|
|
200
|
+
if (args.includes('--pretty=format:%P')) return `${branchParentSha} ${targetParentSha}`;
|
|
201
|
+
if (args[0] === 'merge-base' && args[1] === branchParentSha) return differentMergeBase;
|
|
202
|
+
if (args[0] === 'merge-base' && args[1] === targetParentSha) return targetParentSha;
|
|
195
203
|
return '';
|
|
196
204
|
});
|
|
197
205
|
|
|
198
|
-
expect(hasMergeFromTarget(sourceBranch, targetBranch)).toBe(true);
|
|
206
|
+
await expect(hasMergeFromTarget(sourceBranch, targetBranch)).resolves.toBe(true);
|
|
207
|
+
expect(gitCommandSpy).toHaveBeenCalledWith(['merge-base', targetParentSha, targetBranch], {
|
|
208
|
+
operation: 'check whether a merge parent is reachable from the target branch',
|
|
209
|
+
});
|
|
199
210
|
});
|
|
200
211
|
|
|
201
|
-
it('should return false when the merge commit parent is not reachable from targetBranch (unrelated branch merge)', () => {
|
|
212
|
+
it('should return false when the merge commit parent is not reachable from targetBranch (unrelated branch merge)', async () => {
|
|
202
213
|
const unrelatedSha = 'e'.repeat(40);
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if (
|
|
206
|
-
if (cmd.includes('--pretty=format:%P')) return `${branchParentSha} ${unrelatedSha}`;
|
|
214
|
+
gitCommandSpy.mockImplementation(async (args: string[]) => {
|
|
215
|
+
if (args.includes('--merges')) return mergeSha;
|
|
216
|
+
if (args.includes('--pretty=format:%P')) return `${branchParentSha} ${unrelatedSha}`;
|
|
207
217
|
// merge-base returns something other than the parent — not an ancestor
|
|
208
|
-
if (
|
|
218
|
+
if (args[0] === 'merge-base') return differentMergeBase;
|
|
209
219
|
return '';
|
|
210
220
|
});
|
|
211
221
|
|
|
212
|
-
expect(hasMergeFromTarget(sourceBranch, targetBranch)).toBe(false);
|
|
222
|
+
await expect(hasMergeFromTarget(sourceBranch, targetBranch)).resolves.toBe(false);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('continues after merge-base reports unrelated histories', async () => {
|
|
226
|
+
gitCommandSpy.mockImplementation(async (args: string[]) => {
|
|
227
|
+
if (args.includes('--merges')) return mergeSha;
|
|
228
|
+
if (args.includes('--pretty=format:%P')) return `${branchParentSha} ${targetParentSha}`;
|
|
229
|
+
if (args[0] === 'merge-base' && args[1] === branchParentSha) {
|
|
230
|
+
throw new Utils.GitCommandError(args, Object.assign(new Error('no common ancestor'), { code: 1 }));
|
|
231
|
+
}
|
|
232
|
+
if (args[0] === 'merge-base' && args[1] === targetParentSha) return targetParentSha;
|
|
233
|
+
return '';
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
await expect(hasMergeFromTarget(sourceBranch, targetBranch)).resolves.toBe(true);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('propagates unexpected Git failures', async () => {
|
|
240
|
+
gitCommandSpy.mockImplementation(
|
|
241
|
+
async (args: string[], options?: Parameters<typeof Utils.runGitCommand>[1]) => {
|
|
242
|
+
if (args.includes('--merges')) return mergeSha;
|
|
243
|
+
if (args.includes('--pretty=format:%P')) return `${branchParentSha} ${targetParentSha}`;
|
|
244
|
+
throw new Utils.GitCommandError(
|
|
245
|
+
args,
|
|
246
|
+
Object.assign(new Error('Git failed'), { code: 128 }),
|
|
247
|
+
options?.operation,
|
|
248
|
+
);
|
|
249
|
+
},
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
await expect(hasMergeFromTarget(sourceBranch, targetBranch)).rejects.toThrow(
|
|
253
|
+
'Failed to check whether a merge parent is reachable from the target branch.\n' +
|
|
254
|
+
'Command: git "merge-base"',
|
|
255
|
+
);
|
|
213
256
|
});
|
|
214
257
|
});
|
|
215
258
|
|
|
@@ -220,24 +263,25 @@ describe('getBranchOnlyChangedFiles', () => {
|
|
|
220
263
|
let gitCommandSpy: MockInstance;
|
|
221
264
|
|
|
222
265
|
beforeEach(() => {
|
|
223
|
-
gitCommandSpy = vi.spyOn(Utils, '
|
|
266
|
+
gitCommandSpy = vi.spyOn(Utils, 'runGitCommand');
|
|
224
267
|
});
|
|
225
268
|
|
|
226
|
-
it('should return files touched by non-merge commits', () => {
|
|
227
|
-
gitCommandSpy.
|
|
269
|
+
it('should return files touched by non-merge commits', async () => {
|
|
270
|
+
gitCommandSpy.mockResolvedValue('README.md\n\nactors/foo_bar/src/main.ts\n');
|
|
228
271
|
|
|
229
|
-
const result = getBranchOnlyChangedFiles(sourceBranch, targetBranch);
|
|
272
|
+
const result = await getBranchOnlyChangedFiles(sourceBranch, targetBranch);
|
|
230
273
|
|
|
231
274
|
expect(result).toStrictEqual(['README.md', 'actors/foo_bar/src/main.ts']);
|
|
232
275
|
expect(gitCommandSpy).toHaveBeenCalledWith(
|
|
233
|
-
|
|
276
|
+
['log', '--no-merges', '--name-only', '--pretty=format:', `${targetBranch}..${sourceBranch}`],
|
|
277
|
+
{ operation: 'list files changed by non-merge commits on the source branch' },
|
|
234
278
|
);
|
|
235
279
|
});
|
|
236
280
|
|
|
237
|
-
it('should return empty array when there are no non-merge commits', () => {
|
|
238
|
-
gitCommandSpy.
|
|
281
|
+
it('should return empty array when there are no non-merge commits', async () => {
|
|
282
|
+
gitCommandSpy.mockResolvedValue('');
|
|
239
283
|
|
|
240
|
-
expect(getBranchOnlyChangedFiles(sourceBranch, targetBranch)).toStrictEqual([]);
|
|
284
|
+
await expect(getBranchOnlyChangedFiles(sourceBranch, targetBranch)).resolves.toStrictEqual([]);
|
|
241
285
|
});
|
|
242
286
|
});
|
|
243
287
|
|
|
@@ -272,14 +316,43 @@ describe('parseBaseCommit', () => {
|
|
|
272
316
|
});
|
|
273
317
|
|
|
274
318
|
describe('getCurrentBranch', () => {
|
|
275
|
-
it('should return the checked-out branch', () => {
|
|
276
|
-
vi.spyOn(Utils, '
|
|
277
|
-
expect(getCurrentBranch()).toBe('master');
|
|
319
|
+
it('should return the checked-out branch', async () => {
|
|
320
|
+
const gitCommandSpy = vi.spyOn(Utils, 'runGitCommand').mockResolvedValue('master');
|
|
321
|
+
await expect(getCurrentBranch()).resolves.toBe('master');
|
|
322
|
+
expect(gitCommandSpy).toHaveBeenCalledWith(['rev-parse', '--abbrev-ref', 'HEAD'], {
|
|
323
|
+
operation: 'determine the current branch for release',
|
|
324
|
+
});
|
|
278
325
|
});
|
|
279
326
|
|
|
280
|
-
it('should throw on a detached HEAD', () => {
|
|
281
|
-
vi.spyOn(Utils, '
|
|
282
|
-
expect(
|
|
327
|
+
it('should throw on a detached HEAD', async () => {
|
|
328
|
+
vi.spyOn(Utils, 'runGitCommand').mockResolvedValue('HEAD');
|
|
329
|
+
await expect(getCurrentBranch()).rejects.toThrow('HEAD is detached');
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
describe('resolveRepoUrl', () => {
|
|
334
|
+
it('reads and rewrites the origin URL when no URL is supplied', async () => {
|
|
335
|
+
const gitCommandSpy = vi
|
|
336
|
+
.spyOn(Utils, 'runGitCommand')
|
|
337
|
+
.mockResolvedValue('https://github.com/apify/example.git');
|
|
338
|
+
|
|
339
|
+
await expect(resolveRepoUrl(undefined)).resolves.toStrictEqual({
|
|
340
|
+
repoUrl: 'git@github.com:apify/example.git',
|
|
341
|
+
shouldVerifyRepoUrl: true,
|
|
342
|
+
});
|
|
343
|
+
expect(gitCommandSpy).toHaveBeenCalledWith(['remote', 'get-url', 'origin'], {
|
|
344
|
+
operation: 'read the origin repository URL',
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it('uses an explicit URL without reading Git', async () => {
|
|
349
|
+
const gitCommandSpy = vi.spyOn(Utils, 'runGitCommand');
|
|
350
|
+
|
|
351
|
+
await expect(resolveRepoUrl('git@github.com:other/example.git')).resolves.toStrictEqual({
|
|
352
|
+
repoUrl: 'git@github.com:other/example.git',
|
|
353
|
+
shouldVerifyRepoUrl: false,
|
|
354
|
+
});
|
|
355
|
+
expect(gitCommandSpy).not.toHaveBeenCalled();
|
|
283
356
|
});
|
|
284
357
|
});
|
|
285
358
|
|
|
@@ -312,37 +385,94 @@ describe('getRepoName', () => {
|
|
|
312
385
|
describe('resolveReleaseBaseCommit', () => {
|
|
313
386
|
const baseSha = 'b'.repeat(40);
|
|
314
387
|
|
|
315
|
-
it('should return the base commit when it is an ancestor of HEAD', () => {
|
|
316
|
-
vi.spyOn(Utils, '
|
|
317
|
-
if (
|
|
318
|
-
if (
|
|
388
|
+
it('should return the base commit when it is an ancestor of HEAD', async () => {
|
|
389
|
+
const gitCommandSpy = vi.spyOn(Utils, 'runGitCommand').mockImplementation(async (args: string[]) => {
|
|
390
|
+
if (args[0] === 'rev-parse') return baseSha;
|
|
391
|
+
if (args[0] === 'merge-base') return baseSha;
|
|
319
392
|
return '';
|
|
320
393
|
});
|
|
321
|
-
expect(resolveReleaseBaseCommit(baseSha)).toBe(baseSha);
|
|
394
|
+
await expect(resolveReleaseBaseCommit(baseSha)).resolves.toBe(baseSha);
|
|
395
|
+
expect(gitCommandSpy).toHaveBeenCalledWith(['rev-parse', '--verify', '--quiet', `${baseSha}^{commit}`], {
|
|
396
|
+
operation: 'verify the release base commit in local Git history',
|
|
397
|
+
});
|
|
398
|
+
expect(gitCommandSpy).toHaveBeenCalledWith(['merge-base', baseSha, 'HEAD'], {
|
|
399
|
+
operation: 'check whether the release base commit is an ancestor of HEAD',
|
|
400
|
+
});
|
|
322
401
|
});
|
|
323
402
|
|
|
324
|
-
it('should throw on the all-zeros SHA of a newly created branch', () => {
|
|
325
|
-
const spy = vi.spyOn(Utils, '
|
|
326
|
-
expect(
|
|
403
|
+
it('should throw on the all-zeros SHA of a newly created branch', async () => {
|
|
404
|
+
const spy = vi.spyOn(Utils, 'runGitCommand');
|
|
405
|
+
await expect(resolveReleaseBaseCommit('0'.repeat(40))).rejects.toThrow('the branch was just created');
|
|
327
406
|
expect(spy).not.toHaveBeenCalled();
|
|
328
407
|
});
|
|
329
408
|
|
|
330
|
-
it('should throw when the base commit is missing from the local history', () => {
|
|
331
|
-
vi
|
|
332
|
-
|
|
409
|
+
it('should throw when the base commit is missing from the local history', async () => {
|
|
410
|
+
const spy = vi
|
|
411
|
+
.spyOn(Utils, 'runGitCommand')
|
|
412
|
+
.mockRejectedValue(
|
|
413
|
+
new Utils.GitCommandError(
|
|
414
|
+
['rev-parse', '--verify', '--quiet', `${baseSha}^{commit}`],
|
|
415
|
+
Object.assign(new Error('missing'), { code: 1 }),
|
|
416
|
+
),
|
|
417
|
+
);
|
|
418
|
+
await expect(resolveReleaseBaseCommit(baseSha)).rejects.toThrow('is not in the local git history');
|
|
419
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
it('should throw when rev-parse returns an empty result', async () => {
|
|
423
|
+
const spy = vi.spyOn(Utils, 'runGitCommand').mockResolvedValue('');
|
|
424
|
+
await expect(resolveReleaseBaseCommit(baseSha)).rejects.toThrow('is not in the local git history');
|
|
425
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
333
426
|
});
|
|
334
427
|
|
|
335
|
-
it('should throw when the base commit is not an ancestor of HEAD (force push)', () => {
|
|
336
|
-
vi.spyOn(Utils, '
|
|
337
|
-
if (
|
|
338
|
-
if (
|
|
428
|
+
it('should throw when the base commit is not an ancestor of HEAD (force push)', async () => {
|
|
429
|
+
vi.spyOn(Utils, 'runGitCommand').mockImplementation(async (args: string[]) => {
|
|
430
|
+
if (args[0] === 'rev-parse') return baseSha;
|
|
431
|
+
if (args[0] === 'merge-base') return 'c'.repeat(40);
|
|
339
432
|
return '';
|
|
340
433
|
});
|
|
341
|
-
expect(
|
|
434
|
+
await expect(resolveReleaseBaseCommit(baseSha)).rejects.toThrow('is not an ancestor of HEAD');
|
|
342
435
|
});
|
|
343
436
|
|
|
344
|
-
it('should throw
|
|
345
|
-
|
|
437
|
+
it('should throw when merge-base reports no common ancestor', async () => {
|
|
438
|
+
vi.spyOn(Utils, 'runGitCommand').mockImplementation(async (args: string[]) => {
|
|
439
|
+
if (args[0] === 'rev-parse') return baseSha;
|
|
440
|
+
throw new Utils.GitCommandError(args, Object.assign(new Error('no common ancestor'), { code: 1 }));
|
|
441
|
+
});
|
|
442
|
+
await expect(resolveReleaseBaseCommit(baseSha)).rejects.toThrow('is not an ancestor of HEAD');
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
it('explains an unexpected merge-base failure', async () => {
|
|
446
|
+
vi.spyOn(Utils, 'runGitCommand').mockImplementation(async (args, options) => {
|
|
447
|
+
if (args[0] === 'rev-parse') return baseSha;
|
|
448
|
+
throw new Utils.GitCommandError(
|
|
449
|
+
args,
|
|
450
|
+
Object.assign(new Error('Git failed'), { code: 128 }),
|
|
451
|
+
options?.operation,
|
|
452
|
+
);
|
|
453
|
+
});
|
|
454
|
+
await expect(resolveReleaseBaseCommit(baseSha)).rejects.toThrow(
|
|
455
|
+
'Failed to check whether the release base commit is an ancestor of HEAD.\n' +
|
|
456
|
+
`Command: git "merge-base" "${baseSha}" "HEAD"\nGit error: Git failed`,
|
|
457
|
+
);
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
it('should propagate unexpected Git failures', async () => {
|
|
461
|
+
vi.spyOn(Utils, 'runGitCommand').mockRejectedValue(
|
|
462
|
+
new Utils.GitCommandError(
|
|
463
|
+
['rev-parse', '--verify', '--quiet', `${baseSha}^{commit}`],
|
|
464
|
+
Object.assign(new Error('Git failed'), { code: 128 }),
|
|
465
|
+
'verify the release base commit in local Git history',
|
|
466
|
+
),
|
|
467
|
+
);
|
|
468
|
+
await expect(resolveReleaseBaseCommit(baseSha)).rejects.toThrow(
|
|
469
|
+
'Failed to verify the release base commit in local Git history.\n' +
|
|
470
|
+
`Command: git "rev-parse" "--verify" "--quiet" "${baseSha}^{commit}"\nGit error: Git failed`,
|
|
471
|
+
);
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
it('should throw on an invalid SHA', async () => {
|
|
475
|
+
await expect(resolveReleaseBaseCommit('not-a-sha')).rejects.toThrow('Invalid base commit SHA');
|
|
346
476
|
});
|
|
347
477
|
});
|
|
348
478
|
|
|
@@ -352,21 +482,21 @@ describe('getReleaseChanges', () => {
|
|
|
352
482
|
const mergedBranchCommit = `${'1'.repeat(40)}»¦«Dev<dev@example.com>»¦«Date1»¦«feat: branch change`;
|
|
353
483
|
const mergeCommit = `${headSha}»¦«Dev<dev@example.com>»¦«Date2»¦«Merge pull request #1`;
|
|
354
484
|
|
|
355
|
-
let
|
|
485
|
+
let gitRunSpy: MockInstance;
|
|
356
486
|
|
|
357
487
|
beforeEach(() => {
|
|
358
|
-
|
|
359
|
-
if (
|
|
360
|
-
if (
|
|
361
|
-
if (
|
|
362
|
-
if (
|
|
488
|
+
gitRunSpy = vi.spyOn(Utils, 'runGitCommand').mockImplementation(async (args: string[]) => {
|
|
489
|
+
if (args[0] === 'rev-parse') return headSha;
|
|
490
|
+
if (args[0] === 'log') return `${mergeCommit}\n${mergedBranchCommit}`;
|
|
491
|
+
if (args.includes('--name-only')) return 'actors/foo/src/main.ts\nCHANGELOG.md';
|
|
492
|
+
if (args[0] === 'diff')
|
|
363
493
|
return 'diff --git a/CHANGELOG.md b/CHANGELOG.md\n--- a/CHANGELOG.md\n+++ b/CHANGELOG.md\n@@ -1 +1,2 @@\n+- Added foo\n # Changelog';
|
|
364
494
|
return '';
|
|
365
495
|
});
|
|
366
496
|
});
|
|
367
497
|
|
|
368
|
-
it('should diff the range directly and return the commits oldest first', () => {
|
|
369
|
-
const changes = getReleaseChanges(baseSha);
|
|
498
|
+
it('should diff the range directly and return the commits oldest first', async () => {
|
|
499
|
+
const changes = await getReleaseChanges(baseSha);
|
|
370
500
|
|
|
371
501
|
expect(changes).toStrictEqual({
|
|
372
502
|
commits: [
|
|
@@ -377,13 +507,38 @@ describe('getReleaseChanges', () => {
|
|
|
377
507
|
changelog: '- Added foo',
|
|
378
508
|
});
|
|
379
509
|
// Not from the parent of the oldest commit, which may predate the base commit for merge commits
|
|
380
|
-
expect(
|
|
381
|
-
|
|
382
|
-
|
|
510
|
+
expect(gitRunSpy).toHaveBeenCalledWith(['rev-parse', 'HEAD'], {
|
|
511
|
+
operation: 'identify the current release commit',
|
|
512
|
+
});
|
|
513
|
+
expect(gitRunSpy).toHaveBeenCalledWith(['diff', '--name-only', baseSha, 'HEAD'], {
|
|
514
|
+
operation: 'list files changed since the last release',
|
|
515
|
+
});
|
|
516
|
+
expect(gitRunSpy).toHaveBeenCalledWith(
|
|
517
|
+
['log', '--pretty=format:%H»¦«%aN<%aE>»¦«%aD»¦«%s', `${baseSha}..HEAD`],
|
|
518
|
+
{ operation: 'list commits reachable from the source ref but absent from the target ref' },
|
|
383
519
|
);
|
|
520
|
+
expect(gitRunSpy).toHaveBeenCalledWith(['diff', baseSha, 'HEAD', '--', 'CHANGELOG.md'], {
|
|
521
|
+
operation: 'read changelog changes since the last release',
|
|
522
|
+
});
|
|
384
523
|
});
|
|
385
524
|
|
|
386
|
-
it('should return null when HEAD is the base commit', () => {
|
|
387
|
-
expect(getReleaseChanges(headSha)).toBeNull();
|
|
525
|
+
it('should return null when HEAD is the base commit', async () => {
|
|
526
|
+
await expect(getReleaseChanges(headSha)).resolves.toBeNull();
|
|
527
|
+
expect(gitRunSpy).toHaveBeenCalledOnce();
|
|
528
|
+
expect(gitRunSpy).toHaveBeenCalledWith(['rev-parse', 'HEAD'], {
|
|
529
|
+
operation: 'identify the current release commit',
|
|
530
|
+
});
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
it('skips the changelog diff when CHANGELOG.md did not change', async () => {
|
|
534
|
+
gitRunSpy.mockImplementation(async (args: string[]) => {
|
|
535
|
+
if (args[0] === 'rev-parse') return headSha;
|
|
536
|
+
if (args[0] === 'log') return `${mergeCommit}\n${mergedBranchCommit}`;
|
|
537
|
+
if (args.includes('--name-only')) return 'actors/foo/src/main.ts';
|
|
538
|
+
return '';
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
await expect(getReleaseChanges(baseSha)).resolves.toMatchObject({ changelog: null });
|
|
542
|
+
expect(gitRunSpy).not.toHaveBeenCalledWith(['diff', baseSha, 'HEAD', '--', 'CHANGELOG.md']);
|
|
388
543
|
});
|
|
389
544
|
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import process from 'node:process';
|
|
2
|
+
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
|
|
5
|
+
import { logger, logLevels } from '../../../bin/logger.js';
|
|
6
|
+
import { writeJson } from '../../../bin/output.js';
|
|
7
|
+
|
|
8
|
+
describe('logger and CLI output', () => {
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
logger.setLevel('info');
|
|
11
|
+
vi.spyOn(process.stderr, 'write').mockReturnValue(true);
|
|
12
|
+
vi.spyOn(process.stdout, 'write').mockReturnValue(true);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
logger.setLevel('info');
|
|
17
|
+
vi.restoreAllMocks();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it.each(logLevels)('filters diagnostics at the %s level without writing to stdout', (level) => {
|
|
21
|
+
logger.setLevel(level);
|
|
22
|
+
logger.debug('debug');
|
|
23
|
+
logger.info('info');
|
|
24
|
+
logger.warn('warn');
|
|
25
|
+
logger.error('error');
|
|
26
|
+
|
|
27
|
+
expect(vi.mocked(process.stderr.write).mock.calls.map(([message]) => message)).toEqual(
|
|
28
|
+
logLevels.slice(logLevels.indexOf(level), -1).map((message) => `${message}\n`),
|
|
29
|
+
);
|
|
30
|
+
expect(process.stdout.write).not.toHaveBeenCalled();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('defaults to info and preserves formatting, multiple arguments and blank lines', () => {
|
|
34
|
+
logger.debug('hidden');
|
|
35
|
+
logger.info('actor %s: %d', 'owner/name', 3);
|
|
36
|
+
logger.warn('warning', { actor: 'owner/name' });
|
|
37
|
+
logger.error(new Error('failed'));
|
|
38
|
+
logger.info();
|
|
39
|
+
|
|
40
|
+
expect(process.stderr.write).toHaveBeenCalledTimes(4);
|
|
41
|
+
expect(process.stderr.write).toHaveBeenNthCalledWith(1, 'actor owner/name: 3\n');
|
|
42
|
+
expect(process.stderr.write).toHaveBeenNthCalledWith(2, "warning { actor: 'owner/name' }\n");
|
|
43
|
+
expect(process.stderr.write).toHaveBeenNthCalledWith(3, expect.stringContaining('Error: failed'));
|
|
44
|
+
expect(process.stderr.write).toHaveBeenNthCalledWith(4, '\n');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('writes newline-terminated JSON to stdout even when logging is silent', () => {
|
|
48
|
+
logger.setLevel('silent');
|
|
49
|
+
logger.error('hidden');
|
|
50
|
+
writeJson([{ actor: 'owner/name' }]);
|
|
51
|
+
|
|
52
|
+
expect(process.stdout.write).toHaveBeenCalledExactlyOnceWith('[{"actor":"owner/name"}]\n');
|
|
53
|
+
expect(process.stderr.write).not.toHaveBeenCalled();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { execFileSync, spawnSync } from 'node:child_process';
|
|
2
|
+
import { chmod, mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import process from 'node:process';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
|
|
8
|
+
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
|
9
|
+
|
|
10
|
+
const projectRoot = fileURLToPath(new URL('../../../', import.meta.url));
|
|
11
|
+
const compilerPath = fileURLToPath(new URL('../../../node_modules/typescript/bin/tsc', import.meta.url));
|
|
12
|
+
let cliPath: string;
|
|
13
|
+
let buildDir: string | undefined;
|
|
14
|
+
let gitStubDir: string | undefined;
|
|
15
|
+
const sha = '1'.repeat(40);
|
|
16
|
+
|
|
17
|
+
describe('CLI logging', () => {
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
buildDir = await mkdtemp(path.join(projectRoot, '.cli-test-'));
|
|
20
|
+
execFileSync(process.execPath, [compilerPath, '--outDir', buildDir], { cwd: projectRoot });
|
|
21
|
+
cliPath = path.join(buildDir, 'bin/main.js');
|
|
22
|
+
await chmod(cliPath, 0o755);
|
|
23
|
+
gitStubDir = await mkdtemp(path.join(os.tmpdir(), 'apify-cli-logging-'));
|
|
24
|
+
const gitStub = path.join(gitStubDir, 'git');
|
|
25
|
+
await writeFile(
|
|
26
|
+
gitStub,
|
|
27
|
+
`#!/bin/sh\nprintf '%s' '${sha}»¦«Test<test@example.com>»¦«Tue, 06 Oct 2026 00:00:00 +0000»¦«feature'\n`,
|
|
28
|
+
);
|
|
29
|
+
await chmod(gitStub, 0o755);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
afterAll(async () => {
|
|
33
|
+
if (buildDir) await rm(buildDir, { recursive: true, force: true });
|
|
34
|
+
if (gitStubDir) await rm(gitStubDir, { recursive: true, force: true });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it.each(['info', 'silent'])('keeps JSON results intact with --log-level %s', (level) => {
|
|
38
|
+
const result = spawnSync(
|
|
39
|
+
cliPath,
|
|
40
|
+
['get-commits', '--source-branch', 'feature', '--target-branch', 'base', '--log-level', level],
|
|
41
|
+
{
|
|
42
|
+
encoding: 'utf8',
|
|
43
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
44
|
+
env: { ...process.env, PATH: `${gitStubDir}${path.delimiter}${process.env.PATH ?? ''}` },
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
expect(result.status, result.stderr).toBe(0);
|
|
48
|
+
expect(result.stderr).toEqual(level === 'silent' ? '' : expect.stringContaining('Commits being returned:'));
|
|
49
|
+
expect(JSON.parse(result.stdout)).toMatchObject([{ sha, date: expect.any(String), message: 'feature' }]);
|
|
50
|
+
expect(result.stdout.endsWith('\n')).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
});
|