atl-fetch 1.0.0
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/LICENSE +21 -0
- package/README.md +113 -0
- package/dist/cli/cli.d.ts +61 -0
- package/dist/cli/cli.js +131 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.js +4 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +13 -0
- package/dist/ports/file/file-port.d.ts +89 -0
- package/dist/ports/file/file-port.js +155 -0
- package/dist/ports/file/index.d.ts +1 -0
- package/dist/ports/file/index.js +1 -0
- package/dist/ports/http/http-port.d.ts +107 -0
- package/dist/ports/http/http-port.js +238 -0
- package/dist/ports/http/index.d.ts +1 -0
- package/dist/ports/http/index.js +1 -0
- package/dist/services/auth/auth-service.d.ts +79 -0
- package/dist/services/auth/auth-service.js +158 -0
- package/dist/services/auth/index.d.ts +1 -0
- package/dist/services/auth/index.js +1 -0
- package/dist/services/confluence/confluence-service.d.ts +152 -0
- package/dist/services/confluence/confluence-service.js +510 -0
- package/dist/services/confluence/index.d.ts +1 -0
- package/dist/services/confluence/index.js +1 -0
- package/dist/services/diff/diff-service.d.ts +84 -0
- package/dist/services/diff/diff-service.js +881 -0
- package/dist/services/diff/index.d.ts +1 -0
- package/dist/services/diff/index.js +1 -0
- package/dist/services/fetch/fetch-service.d.ts +112 -0
- package/dist/services/fetch/fetch-service.js +302 -0
- package/dist/services/fetch/index.d.ts +1 -0
- package/dist/services/fetch/index.js +1 -0
- package/dist/services/jira/index.d.ts +1 -0
- package/dist/services/jira/index.js +1 -0
- package/dist/services/jira/jira-service.d.ts +100 -0
- package/dist/services/jira/jira-service.js +354 -0
- package/dist/services/output/index.d.ts +4 -0
- package/dist/services/output/index.js +4 -0
- package/dist/services/output/output-service.d.ts +67 -0
- package/dist/services/output/output-service.js +228 -0
- package/dist/services/storage/index.d.ts +6 -0
- package/dist/services/storage/index.js +6 -0
- package/dist/services/storage/storage-service.d.ts +77 -0
- package/dist/services/storage/storage-service.js +738 -0
- package/dist/services/text-converter/index.d.ts +1 -0
- package/dist/services/text-converter/index.js +1 -0
- package/dist/services/text-converter/text-converter.d.ts +35 -0
- package/dist/services/text-converter/text-converter.js +681 -0
- package/dist/services/url-parser/index.d.ts +1 -0
- package/dist/services/url-parser/index.js +1 -0
- package/dist/services/url-parser/url-parser.d.ts +43 -0
- package/dist/services/url-parser/url-parser.js +283 -0
- package/dist/types/auth.d.ts +25 -0
- package/dist/types/auth.js +1 -0
- package/dist/types/confluence.d.ts +68 -0
- package/dist/types/confluence.js +1 -0
- package/dist/types/diff.d.ts +77 -0
- package/dist/types/diff.js +7 -0
- package/dist/types/fetch.d.ts +65 -0
- package/dist/types/fetch.js +1 -0
- package/dist/types/file.d.ts +22 -0
- package/dist/types/file.js +1 -0
- package/dist/types/http.d.ts +45 -0
- package/dist/types/http.js +1 -0
- package/dist/types/jira.d.ts +90 -0
- package/dist/types/jira.js +1 -0
- package/dist/types/output.d.ts +55 -0
- package/dist/types/output.js +7 -0
- package/dist/types/result.d.ts +104 -0
- package/dist/types/result.js +119 -0
- package/dist/types/storage.d.ts +209 -0
- package/dist/types/storage.js +6 -0
- package/dist/types/url-parser.d.ts +46 -0
- package/dist/types/url-parser.js +1 -0
- package/package.json +106 -0
|
@@ -0,0 +1,881 @@
|
|
|
1
|
+
import { structuredPatch } from 'diff';
|
|
2
|
+
/** ANSI カラーコード */
|
|
3
|
+
const ANSI_COLORS = {
|
|
4
|
+
green: '\x1b[32m',
|
|
5
|
+
red: '\x1b[31m',
|
|
6
|
+
reset: '\x1b[0m',
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* 差分ハンクから差分行を抽出する
|
|
10
|
+
*
|
|
11
|
+
* diff ライブラリの StructuredPatchHunk から DiffLine 配列に変換する。
|
|
12
|
+
* 行のプレフィックス(+, -, 空白)から行の種別を判定する。
|
|
13
|
+
*
|
|
14
|
+
* @param hunk - diff ライブラリの StructuredPatchHunk
|
|
15
|
+
* @returns 差分行の配列
|
|
16
|
+
*/
|
|
17
|
+
function extractDiffLines(hunk) {
|
|
18
|
+
return hunk.lines.map((line) => {
|
|
19
|
+
const prefix = line.charAt(0);
|
|
20
|
+
let type;
|
|
21
|
+
let content;
|
|
22
|
+
switch (prefix) {
|
|
23
|
+
case '+':
|
|
24
|
+
type = 'add';
|
|
25
|
+
content = line.substring(1);
|
|
26
|
+
break;
|
|
27
|
+
case '-':
|
|
28
|
+
type = 'remove';
|
|
29
|
+
content = line.substring(1);
|
|
30
|
+
break;
|
|
31
|
+
default:
|
|
32
|
+
type = 'context';
|
|
33
|
+
content = prefix === ' ' ? line.substring(1) : line;
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
return { content, type };
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* diff ライブラリの StructuredPatchHunk を DiffHunk に変換する
|
|
41
|
+
*
|
|
42
|
+
* @param hunk - diff ライブラリの StructuredPatchHunk
|
|
43
|
+
* @returns DiffHunk
|
|
44
|
+
*/
|
|
45
|
+
function convertToDiffHunk(hunk) {
|
|
46
|
+
return {
|
|
47
|
+
lines: extractDiffLines(hunk),
|
|
48
|
+
newLines: hunk.newLines,
|
|
49
|
+
newStart: hunk.newStart,
|
|
50
|
+
oldLines: hunk.oldLines,
|
|
51
|
+
oldStart: hunk.oldStart,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* ハンクから差分統計を計算する
|
|
56
|
+
*
|
|
57
|
+
* @param hunks - DiffHunk の配列
|
|
58
|
+
* @returns DiffStats
|
|
59
|
+
*/
|
|
60
|
+
function calculateStats(hunks) {
|
|
61
|
+
let additions = 0;
|
|
62
|
+
let deletions = 0;
|
|
63
|
+
for (const hunk of hunks) {
|
|
64
|
+
for (const line of hunk.lines) {
|
|
65
|
+
if (line.type === 'add') {
|
|
66
|
+
additions++;
|
|
67
|
+
}
|
|
68
|
+
else if (line.type === 'remove') {
|
|
69
|
+
deletions++;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
additions,
|
|
75
|
+
changes: hunks.length,
|
|
76
|
+
deletions,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* ハンクを unified diff 形式の文字列にフォーマットする
|
|
81
|
+
*
|
|
82
|
+
* @param hunks - DiffHunk の配列
|
|
83
|
+
* @returns unified diff 形式の文字列
|
|
84
|
+
*/
|
|
85
|
+
function formatUnifiedDiff(hunks) {
|
|
86
|
+
if (hunks.length === 0) {
|
|
87
|
+
return '';
|
|
88
|
+
}
|
|
89
|
+
const lines = [];
|
|
90
|
+
for (const hunk of hunks) {
|
|
91
|
+
// ハンクヘッダー
|
|
92
|
+
lines.push(`@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`);
|
|
93
|
+
// 差分行
|
|
94
|
+
for (const line of hunk.lines) {
|
|
95
|
+
switch (line.type) {
|
|
96
|
+
case 'add':
|
|
97
|
+
lines.push(`+${line.content}`);
|
|
98
|
+
break;
|
|
99
|
+
case 'remove':
|
|
100
|
+
lines.push(`-${line.content}`);
|
|
101
|
+
break;
|
|
102
|
+
case 'context':
|
|
103
|
+
lines.push(` ${line.content}`);
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return lines.join('\n');
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* 2つのテキスト間の差分を計算する
|
|
112
|
+
*
|
|
113
|
+
* unified diff 形式で差分を出力し、差分統計を計算する。
|
|
114
|
+
* diff ライブラリを使用して行単位の差分を検出する。
|
|
115
|
+
*
|
|
116
|
+
* @param oldText - 変更前のテキスト
|
|
117
|
+
* @param newText - 変更後のテキスト
|
|
118
|
+
* @param options - 差分オプション
|
|
119
|
+
* @returns 差分結果
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```typescript
|
|
123
|
+
* // 基本的な使用例
|
|
124
|
+
* const result = diffText('Hello', 'Goodbye', { colorEnabled: false });
|
|
125
|
+
* console.log(result.formatted);
|
|
126
|
+
* // @@ -1,1 +1,1 @@
|
|
127
|
+
* // -Hello
|
|
128
|
+
* // +Goodbye
|
|
129
|
+
* ```
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```typescript
|
|
133
|
+
* // 差分統計の確認
|
|
134
|
+
* const result = diffText('A\nB\nC', 'A\nX\nC', { colorEnabled: false });
|
|
135
|
+
* console.log(result.stats.additions); // 1
|
|
136
|
+
* console.log(result.stats.deletions); // 1
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
export function diffText(oldText, newText, options) {
|
|
140
|
+
const contextLines = options.contextLines ?? 3;
|
|
141
|
+
// diff ライブラリで差分を計算
|
|
142
|
+
const patch = structuredPatch('old', 'new', oldText, newText, undefined, undefined, {
|
|
143
|
+
context: contextLines,
|
|
144
|
+
});
|
|
145
|
+
// ハンクを変換
|
|
146
|
+
const hunks = patch.hunks.map(convertToDiffHunk);
|
|
147
|
+
// 統計を計算
|
|
148
|
+
const stats = calculateStats(hunks);
|
|
149
|
+
// フォーマット
|
|
150
|
+
const formatted = formatUnifiedDiff(hunks);
|
|
151
|
+
return {
|
|
152
|
+
formatted,
|
|
153
|
+
hunks,
|
|
154
|
+
stats,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* null 値を「(なし)」に変換する
|
|
159
|
+
*
|
|
160
|
+
* @param value - 値
|
|
161
|
+
* @returns 値または「(なし)」
|
|
162
|
+
*/
|
|
163
|
+
function formatNullableValue(value) {
|
|
164
|
+
return value ?? '(なし)';
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* 削除行(変更前の値)をフォーマットする
|
|
168
|
+
*
|
|
169
|
+
* @param field - フィールド名
|
|
170
|
+
* @param value - 値
|
|
171
|
+
* @param colorEnabled - カラー出力を有効にするか
|
|
172
|
+
* @returns フォーマットされた文字列
|
|
173
|
+
*/
|
|
174
|
+
function formatRemoveLine(field, value, colorEnabled) {
|
|
175
|
+
const formattedValue = formatNullableValue(value);
|
|
176
|
+
const line = `- ${field}: ${formattedValue}`;
|
|
177
|
+
if (colorEnabled) {
|
|
178
|
+
return `${ANSI_COLORS.red}${line}${ANSI_COLORS.reset}`;
|
|
179
|
+
}
|
|
180
|
+
return line;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* 追加行(変更後の値)をフォーマットする
|
|
184
|
+
*
|
|
185
|
+
* @param field - フィールド名
|
|
186
|
+
* @param value - 値
|
|
187
|
+
* @param colorEnabled - カラー出力を有効にするか
|
|
188
|
+
* @returns フォーマットされた文字列
|
|
189
|
+
*/
|
|
190
|
+
function formatAddLine(field, value, colorEnabled) {
|
|
191
|
+
const formattedValue = formatNullableValue(value);
|
|
192
|
+
const line = `+ ${field}: ${formattedValue}`;
|
|
193
|
+
if (colorEnabled) {
|
|
194
|
+
return `${ANSI_COLORS.green}${line}${ANSI_COLORS.reset}`;
|
|
195
|
+
}
|
|
196
|
+
return line;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Jira changelog を差分形式でフォーマットする
|
|
200
|
+
*
|
|
201
|
+
* 各変更のフィールド名、変更前の値、変更後の値を表示する。
|
|
202
|
+
* カラー出力が有効な場合、追加行を緑、削除行を赤でハイライトする。
|
|
203
|
+
*
|
|
204
|
+
* @param changelog - Jira の変更履歴
|
|
205
|
+
* @param options - 差分オプション
|
|
206
|
+
* @returns フォーマットされた差分文字列
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* ```typescript
|
|
210
|
+
* const changelog = [{
|
|
211
|
+
* id: '12345',
|
|
212
|
+
* author: 'John Doe',
|
|
213
|
+
* created: '2024-01-15T10:30:00.000+0900',
|
|
214
|
+
* items: [{ field: 'status', fromString: 'Open', toString: 'In Progress' }]
|
|
215
|
+
* }];
|
|
216
|
+
* const result = formatJiraChangelog(changelog, { colorEnabled: false });
|
|
217
|
+
* // Changelog #12345 by John Doe at 2024-01-15T10:30:00.000+0900
|
|
218
|
+
* // [status]
|
|
219
|
+
* // - status: Open
|
|
220
|
+
* // + status: In Progress
|
|
221
|
+
* ```
|
|
222
|
+
*/
|
|
223
|
+
export function formatJiraChangelog(changelog, options) {
|
|
224
|
+
if (changelog.length === 0) {
|
|
225
|
+
return '';
|
|
226
|
+
}
|
|
227
|
+
const lines = [];
|
|
228
|
+
for (const entry of changelog) {
|
|
229
|
+
// ヘッダー行
|
|
230
|
+
lines.push(`Changelog #${entry.id} by ${entry.author} at ${entry.created}`);
|
|
231
|
+
// 変更アイテム
|
|
232
|
+
for (const item of entry.items) {
|
|
233
|
+
lines.push(` [${item.field}]`);
|
|
234
|
+
lines.push(` ${formatRemoveLine(item.field, item.fromString, options.colorEnabled)}`);
|
|
235
|
+
lines.push(` ${formatAddLine(item.field, item.toString, options.colorEnabled)}`);
|
|
236
|
+
}
|
|
237
|
+
lines.push(''); // 空行で区切る
|
|
238
|
+
}
|
|
239
|
+
// 末尾の空行を削除
|
|
240
|
+
while (lines.length > 0 && lines[lines.length - 1] === '') {
|
|
241
|
+
lines.pop();
|
|
242
|
+
}
|
|
243
|
+
return lines.join('\n');
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* 差分行をカラー付きでフォーマットする
|
|
247
|
+
*
|
|
248
|
+
* @param line - 差分行(プレフィックス付き)
|
|
249
|
+
* @param type - 行の種別
|
|
250
|
+
* @param colorEnabled - カラー出力を有効にするか
|
|
251
|
+
* @returns フォーマットされた文字列
|
|
252
|
+
*/
|
|
253
|
+
function formatDiffLineWithColor(line, type, colorEnabled) {
|
|
254
|
+
if (!colorEnabled) {
|
|
255
|
+
return line;
|
|
256
|
+
}
|
|
257
|
+
switch (type) {
|
|
258
|
+
case 'add':
|
|
259
|
+
return `${ANSI_COLORS.green}${line}${ANSI_COLORS.reset}`;
|
|
260
|
+
case 'remove':
|
|
261
|
+
return `${ANSI_COLORS.red}${line}${ANSI_COLORS.reset}`;
|
|
262
|
+
default:
|
|
263
|
+
return line;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* DiffLine をプレフィックス付きの行文字列に変換する
|
|
268
|
+
*
|
|
269
|
+
* @param line - 差分行
|
|
270
|
+
* @returns プレフィックス付きの行文字列
|
|
271
|
+
*/
|
|
272
|
+
function diffLineToString(line) {
|
|
273
|
+
switch (line.type) {
|
|
274
|
+
case 'add':
|
|
275
|
+
return `+${line.content}`;
|
|
276
|
+
case 'remove':
|
|
277
|
+
return `-${line.content}`;
|
|
278
|
+
case 'context':
|
|
279
|
+
return ` ${line.content}`;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* 差分ハンクをカラー付きの文字列配列に変換する
|
|
284
|
+
*
|
|
285
|
+
* @param hunks - 差分ハンクの配列
|
|
286
|
+
* @param colorEnabled - カラー出力を有効にするか
|
|
287
|
+
* @returns フォーマットされた行の配列
|
|
288
|
+
*/
|
|
289
|
+
function formatHunksWithColor(hunks, colorEnabled) {
|
|
290
|
+
const lines = [];
|
|
291
|
+
for (const hunk of hunks) {
|
|
292
|
+
lines.push(`@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`);
|
|
293
|
+
for (const line of hunk.lines) {
|
|
294
|
+
const lineStr = diffLineToString(line);
|
|
295
|
+
lines.push(formatDiffLineWithColor(lineStr, line.type, colorEnabled));
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return lines;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Confluence バージョンヘッダーを生成する
|
|
302
|
+
*
|
|
303
|
+
* @param oldVersion - 変更前のバージョン
|
|
304
|
+
* @param newVersion - 変更後のバージョン
|
|
305
|
+
* @param stats - 差分統計(null の場合は統計なし)
|
|
306
|
+
* @returns ヘッダー行
|
|
307
|
+
*/
|
|
308
|
+
function buildVersionHeader(oldVersion, newVersion, stats) {
|
|
309
|
+
const baseHeader = `v${oldVersion.number} → v${newVersion.number} by ${newVersion.by}`;
|
|
310
|
+
if (stats === null) {
|
|
311
|
+
return baseHeader;
|
|
312
|
+
}
|
|
313
|
+
return `${baseHeader} (+${stats.additions}, -${stats.deletions})`;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Confluence バージョン間の差分をフォーマットする
|
|
317
|
+
*
|
|
318
|
+
* 2つの Confluence バージョン間の本文差分を計算し、unified diff 形式で出力する。
|
|
319
|
+
* ヘッダーにはバージョン番号、作成者、差分統計が含まれる。
|
|
320
|
+
* カラー出力が有効な場合、追加行を緑、削除行を赤でハイライトする。
|
|
321
|
+
*
|
|
322
|
+
* @param oldVersion - 変更前のバージョン
|
|
323
|
+
* @param newVersion - 変更後のバージョン
|
|
324
|
+
* @param options - 差分オプション
|
|
325
|
+
* @returns フォーマットされた差分文字列
|
|
326
|
+
*
|
|
327
|
+
* @example
|
|
328
|
+
* ```typescript
|
|
329
|
+
* const oldVersion = { number: 1, by: 'John', when: '2024-01-15', message: null, body: 'Hello' };
|
|
330
|
+
* const newVersion = { number: 2, by: 'Jane', when: '2024-01-16', message: 'Updated', body: 'Hello, World!' };
|
|
331
|
+
* const result = formatConfluenceVersionDiff(oldVersion, newVersion, { colorEnabled: false });
|
|
332
|
+
* // v1 → v2 by Jane (+1, -1)
|
|
333
|
+
* // Updated
|
|
334
|
+
* // @@ -1,1 +1,1 @@
|
|
335
|
+
* // -Hello
|
|
336
|
+
* // +Hello, World!
|
|
337
|
+
* ```
|
|
338
|
+
*/
|
|
339
|
+
export function formatConfluenceVersionDiff(oldVersion, newVersion, options) {
|
|
340
|
+
// 本文を取得(undefined の場合は空文字列として扱う)
|
|
341
|
+
const oldBody = oldVersion.body ?? '';
|
|
342
|
+
const newBody = newVersion.body ?? '';
|
|
343
|
+
// 差分を計算
|
|
344
|
+
const diffResult = diffText(oldBody, newBody, options);
|
|
345
|
+
// 差分がない場合
|
|
346
|
+
if (diffResult.hunks.length === 0) {
|
|
347
|
+
const lines = [buildVersionHeader(oldVersion, newVersion, null)];
|
|
348
|
+
if (newVersion.message !== null) {
|
|
349
|
+
lines.push(newVersion.message);
|
|
350
|
+
}
|
|
351
|
+
lines.push('(変更なし)');
|
|
352
|
+
return lines.join('\n');
|
|
353
|
+
}
|
|
354
|
+
// 出力を構築
|
|
355
|
+
const lines = [];
|
|
356
|
+
// ヘッダー
|
|
357
|
+
lines.push(buildVersionHeader(oldVersion, newVersion, diffResult.stats));
|
|
358
|
+
lines.push(` by ${oldVersion.by} → ${newVersion.by}`);
|
|
359
|
+
// バージョンメッセージ
|
|
360
|
+
if (newVersion.message !== null) {
|
|
361
|
+
lines.push(newVersion.message);
|
|
362
|
+
}
|
|
363
|
+
lines.push(''); // 空行
|
|
364
|
+
// 差分内容(カラー対応)
|
|
365
|
+
lines.push(...formatHunksWithColor(diffResult.hunks, options.colorEnabled));
|
|
366
|
+
return lines.join('\n');
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* In-source testing for private functions
|
|
370
|
+
*
|
|
371
|
+
* vitest の in-source testing 機能を使用して、
|
|
372
|
+
* プライベート関数のテストを実行する。
|
|
373
|
+
*/
|
|
374
|
+
if (import.meta.vitest) {
|
|
375
|
+
const { describe, it, expect } = import.meta.vitest;
|
|
376
|
+
/**
|
|
377
|
+
* extractDiffLines のテスト
|
|
378
|
+
*
|
|
379
|
+
* Note: extractDiffLines は StructuredPatchHunk を取る(lines は string[])
|
|
380
|
+
*/
|
|
381
|
+
describe('extractDiffLines (private)', () => {
|
|
382
|
+
it('Given 追加行、When 抽出すると、Then 追加タイプの DiffLine を返す', () => {
|
|
383
|
+
// Given: 追加行のハンク(StructuredPatchHunk 形式)
|
|
384
|
+
const hunk = {
|
|
385
|
+
lines: ['+added line'],
|
|
386
|
+
newLines: 1,
|
|
387
|
+
newStart: 1,
|
|
388
|
+
oldLines: 0,
|
|
389
|
+
oldStart: 1,
|
|
390
|
+
};
|
|
391
|
+
// When: 行を抽出する
|
|
392
|
+
const lines = extractDiffLines(hunk);
|
|
393
|
+
// Then: 追加タイプの DiffLine を返す
|
|
394
|
+
expect(lines).toHaveLength(1);
|
|
395
|
+
expect(lines[0]).toEqual({ content: 'added line', type: 'add' });
|
|
396
|
+
});
|
|
397
|
+
it('Given 削除行、When 抽出すると、Then 削除タイプの DiffLine を返す', () => {
|
|
398
|
+
// Given: 削除行のハンク
|
|
399
|
+
const hunk = {
|
|
400
|
+
lines: ['-removed line'],
|
|
401
|
+
newLines: 0,
|
|
402
|
+
newStart: 1,
|
|
403
|
+
oldLines: 1,
|
|
404
|
+
oldStart: 1,
|
|
405
|
+
};
|
|
406
|
+
// When: 行を抽出する
|
|
407
|
+
const lines = extractDiffLines(hunk);
|
|
408
|
+
// Then: 削除タイプの DiffLine を返す
|
|
409
|
+
expect(lines).toHaveLength(1);
|
|
410
|
+
expect(lines[0]).toEqual({ content: 'removed line', type: 'remove' });
|
|
411
|
+
});
|
|
412
|
+
it('Given コンテキスト行(スペース始まり)、When 抽出すると、Then コンテキストタイプを返す', () => {
|
|
413
|
+
// Given: コンテキスト行のハンク
|
|
414
|
+
const hunk = {
|
|
415
|
+
lines: [' context line'],
|
|
416
|
+
newLines: 1,
|
|
417
|
+
newStart: 1,
|
|
418
|
+
oldLines: 1,
|
|
419
|
+
oldStart: 1,
|
|
420
|
+
};
|
|
421
|
+
// When: 行を抽出する
|
|
422
|
+
const lines = extractDiffLines(hunk);
|
|
423
|
+
// Then: コンテキストタイプの DiffLine を返す
|
|
424
|
+
expect(lines).toHaveLength(1);
|
|
425
|
+
expect(lines[0]).toEqual({ content: 'context line', type: 'context' });
|
|
426
|
+
});
|
|
427
|
+
it('Given 空行、When 抽出すると、Then 空のコンテキスト行を返す', () => {
|
|
428
|
+
// Given: 空行のハンク
|
|
429
|
+
const hunk = {
|
|
430
|
+
lines: [''],
|
|
431
|
+
newLines: 1,
|
|
432
|
+
newStart: 1,
|
|
433
|
+
oldLines: 1,
|
|
434
|
+
oldStart: 1,
|
|
435
|
+
};
|
|
436
|
+
// When: 行を抽出する
|
|
437
|
+
const lines = extractDiffLines(hunk);
|
|
438
|
+
// Then: 空のコンテキスト行を返す
|
|
439
|
+
expect(lines).toHaveLength(1);
|
|
440
|
+
expect(lines[0]).toEqual({ content: '', type: 'context' });
|
|
441
|
+
});
|
|
442
|
+
it('Given 未知のプレフィックス、When 抽出すると、Then コンテキストタイプで行全体を保持', () => {
|
|
443
|
+
// Given: 未知のプレフィックスを持つハンク
|
|
444
|
+
const hunk = {
|
|
445
|
+
lines: ['?unknown prefix'],
|
|
446
|
+
newLines: 1,
|
|
447
|
+
newStart: 1,
|
|
448
|
+
oldLines: 1,
|
|
449
|
+
oldStart: 1,
|
|
450
|
+
};
|
|
451
|
+
// When: 行を抽出する
|
|
452
|
+
const lines = extractDiffLines(hunk);
|
|
453
|
+
// Then: コンテキストタイプとして処理され、行全体が保持される(default ケース)
|
|
454
|
+
// Note: prefix === ' ' でない場合、line.substring(1) ではなく line 全体が content になる
|
|
455
|
+
expect(lines).toHaveLength(1);
|
|
456
|
+
expect(lines[0]).toEqual({ content: '?unknown prefix', type: 'context' });
|
|
457
|
+
});
|
|
458
|
+
it('Given 複数行、When 抽出すると、Then 全ての行が正しく抽出される', () => {
|
|
459
|
+
// Given: 複数行のハンク
|
|
460
|
+
const hunk = {
|
|
461
|
+
lines: [' context', '-old', '+new'],
|
|
462
|
+
newLines: 3,
|
|
463
|
+
newStart: 1,
|
|
464
|
+
oldLines: 2,
|
|
465
|
+
oldStart: 1,
|
|
466
|
+
};
|
|
467
|
+
// When: 行を抽出する
|
|
468
|
+
const lines = extractDiffLines(hunk);
|
|
469
|
+
// Then: 全ての行が正しく抽出される
|
|
470
|
+
expect(lines).toHaveLength(3);
|
|
471
|
+
expect(lines[0]).toEqual({ content: 'context', type: 'context' });
|
|
472
|
+
expect(lines[1]).toEqual({ content: 'old', type: 'remove' });
|
|
473
|
+
expect(lines[2]).toEqual({ content: 'new', type: 'add' });
|
|
474
|
+
});
|
|
475
|
+
});
|
|
476
|
+
/**
|
|
477
|
+
* calculateStats のテスト
|
|
478
|
+
*
|
|
479
|
+
* Note: calculateStats は DiffHunk[] を取る(各 hunk は lines: DiffLine[] を持つ)
|
|
480
|
+
*/
|
|
481
|
+
describe('calculateStats (private)', () => {
|
|
482
|
+
it('Given 追加と削除がある DiffHunk、When 統計を計算すると、Then 正しい統計を返す', () => {
|
|
483
|
+
// Given: 追加と削除がある DiffHunk 配列
|
|
484
|
+
const hunks = [
|
|
485
|
+
{
|
|
486
|
+
lines: [
|
|
487
|
+
{ content: 'line1', type: 'add' },
|
|
488
|
+
{ content: 'line2', type: 'add' },
|
|
489
|
+
{ content: 'line3', type: 'remove' },
|
|
490
|
+
{ content: 'line4', type: 'context' },
|
|
491
|
+
],
|
|
492
|
+
newLines: 3,
|
|
493
|
+
newStart: 1,
|
|
494
|
+
oldLines: 2,
|
|
495
|
+
oldStart: 1,
|
|
496
|
+
},
|
|
497
|
+
];
|
|
498
|
+
// When: 統計を計算する
|
|
499
|
+
const stats = calculateStats(hunks);
|
|
500
|
+
// Then: 正しい統計を返す
|
|
501
|
+
expect(stats.additions).toBe(2);
|
|
502
|
+
expect(stats.deletions).toBe(1);
|
|
503
|
+
expect(stats.changes).toBe(1); // ハンク数
|
|
504
|
+
});
|
|
505
|
+
it('Given 空の DiffHunk 配列、When 統計を計算すると、Then ゼロの統計を返す', () => {
|
|
506
|
+
// Given: 空の DiffHunk 配列
|
|
507
|
+
const hunks = [];
|
|
508
|
+
// When: 統計を計算する
|
|
509
|
+
const stats = calculateStats(hunks);
|
|
510
|
+
// Then: ゼロの統計を返す
|
|
511
|
+
expect(stats.additions).toBe(0);
|
|
512
|
+
expect(stats.deletions).toBe(0);
|
|
513
|
+
expect(stats.changes).toBe(0);
|
|
514
|
+
});
|
|
515
|
+
it('Given コンテキスト行のみの DiffHunk、When 統計を計算すると、Then ゼロの統計を返す', () => {
|
|
516
|
+
// Given: コンテキスト行のみ
|
|
517
|
+
const hunks = [
|
|
518
|
+
{
|
|
519
|
+
lines: [
|
|
520
|
+
{ content: 'line1', type: 'context' },
|
|
521
|
+
{ content: 'line2', type: 'context' },
|
|
522
|
+
],
|
|
523
|
+
newLines: 2,
|
|
524
|
+
newStart: 1,
|
|
525
|
+
oldLines: 2,
|
|
526
|
+
oldStart: 1,
|
|
527
|
+
},
|
|
528
|
+
];
|
|
529
|
+
// When: 統計を計算する
|
|
530
|
+
const stats = calculateStats(hunks);
|
|
531
|
+
// Then: ゼロの統計を返す(コンテキスト行はカウントされない)
|
|
532
|
+
expect(stats.additions).toBe(0);
|
|
533
|
+
expect(stats.deletions).toBe(0);
|
|
534
|
+
expect(stats.changes).toBe(1); // ハンク数は1
|
|
535
|
+
});
|
|
536
|
+
it('Given 複数のハンク、When 統計を計算すると、Then 全ハンクの合計を返す', () => {
|
|
537
|
+
// Given: 複数のハンク
|
|
538
|
+
const hunks = [
|
|
539
|
+
{
|
|
540
|
+
lines: [{ content: 'add1', type: 'add' }],
|
|
541
|
+
newLines: 1,
|
|
542
|
+
newStart: 1,
|
|
543
|
+
oldLines: 1,
|
|
544
|
+
oldStart: 1,
|
|
545
|
+
},
|
|
546
|
+
{
|
|
547
|
+
lines: [
|
|
548
|
+
{ content: 'del1', type: 'remove' },
|
|
549
|
+
{ content: 'del2', type: 'remove' },
|
|
550
|
+
],
|
|
551
|
+
newLines: 1,
|
|
552
|
+
newStart: 10,
|
|
553
|
+
oldLines: 1,
|
|
554
|
+
oldStart: 10,
|
|
555
|
+
},
|
|
556
|
+
];
|
|
557
|
+
// When: 統計を計算する
|
|
558
|
+
const stats = calculateStats(hunks);
|
|
559
|
+
// Then: 全ハンクの合計を返す
|
|
560
|
+
expect(stats.additions).toBe(1);
|
|
561
|
+
expect(stats.deletions).toBe(2);
|
|
562
|
+
expect(stats.changes).toBe(2); // ハンク数
|
|
563
|
+
});
|
|
564
|
+
});
|
|
565
|
+
/**
|
|
566
|
+
* formatNullableValue のテスト
|
|
567
|
+
*/
|
|
568
|
+
describe('formatNullableValue (private)', () => {
|
|
569
|
+
it('Given null 値、When フォーマットすると、Then "(なし)" を返す', () => {
|
|
570
|
+
// Given: null 値
|
|
571
|
+
const value = null;
|
|
572
|
+
// When: フォーマットする
|
|
573
|
+
const result = formatNullableValue(value);
|
|
574
|
+
// Then: "(なし)" を返す
|
|
575
|
+
expect(result).toBe('(なし)');
|
|
576
|
+
});
|
|
577
|
+
it('Given 文字列値、When フォーマットすると、Then その値を返す', () => {
|
|
578
|
+
// Given: 文字列値
|
|
579
|
+
const value = 'test value';
|
|
580
|
+
// When: フォーマットする
|
|
581
|
+
const result = formatNullableValue(value);
|
|
582
|
+
// Then: その値を返す
|
|
583
|
+
expect(result).toBe('test value');
|
|
584
|
+
});
|
|
585
|
+
it('Given 空文字列、When フォーマットすると、Then 空文字列を返す', () => {
|
|
586
|
+
// Given: 空文字列
|
|
587
|
+
const value = '';
|
|
588
|
+
// When: フォーマットする
|
|
589
|
+
const result = formatNullableValue(value);
|
|
590
|
+
// Then: 空文字列を返す
|
|
591
|
+
expect(result).toBe('');
|
|
592
|
+
});
|
|
593
|
+
});
|
|
594
|
+
/**
|
|
595
|
+
* diffLineToString のテスト
|
|
596
|
+
*/
|
|
597
|
+
describe('diffLineToString (private)', () => {
|
|
598
|
+
it('Given 追加行、When 文字列に変換すると、Then "+" プレフィックスが付く', () => {
|
|
599
|
+
// Given: 追加行
|
|
600
|
+
const line = { content: 'new content', type: 'add' };
|
|
601
|
+
// When: 文字列に変換する
|
|
602
|
+
const result = diffLineToString(line);
|
|
603
|
+
// Then: "+" プレフィックスが付く
|
|
604
|
+
expect(result).toBe('+new content');
|
|
605
|
+
});
|
|
606
|
+
it('Given 削除行、When 文字列に変換すると、Then "-" プレフィックスが付く', () => {
|
|
607
|
+
// Given: 削除行
|
|
608
|
+
const line = { content: 'old content', type: 'remove' };
|
|
609
|
+
// When: 文字列に変換する
|
|
610
|
+
const result = diffLineToString(line);
|
|
611
|
+
// Then: "-" プレフィックスが付く
|
|
612
|
+
expect(result).toBe('-old content');
|
|
613
|
+
});
|
|
614
|
+
it('Given コンテキスト行、When 文字列に変換すると、Then " " プレフィックスが付く', () => {
|
|
615
|
+
// Given: コンテキスト行
|
|
616
|
+
const line = { content: 'unchanged', type: 'context' };
|
|
617
|
+
// When: 文字列に変換する
|
|
618
|
+
const result = diffLineToString(line);
|
|
619
|
+
// Then: " " プレフィックスが付く
|
|
620
|
+
expect(result).toBe(' unchanged');
|
|
621
|
+
});
|
|
622
|
+
});
|
|
623
|
+
/**
|
|
624
|
+
* formatDiffLineWithColor のテスト
|
|
625
|
+
*
|
|
626
|
+
* Note: formatDiffLineWithColor は (line: string, type: DiffLineType, colorEnabled: boolean) を取る
|
|
627
|
+
*/
|
|
628
|
+
describe('formatDiffLineWithColor (private)', () => {
|
|
629
|
+
it('Given 追加行とカラー有効、When フォーマットすると、Then 緑色の ANSI コードが付く', () => {
|
|
630
|
+
// Given: 追加行とカラー有効
|
|
631
|
+
const lineStr = '+new';
|
|
632
|
+
// When: カラー付きでフォーマットする
|
|
633
|
+
const result = formatDiffLineWithColor(lineStr, 'add', true);
|
|
634
|
+
// Then: 緑色の ANSI コードが付く
|
|
635
|
+
expect(result).toBe('\x1b[32m+new\x1b[0m');
|
|
636
|
+
});
|
|
637
|
+
it('Given 削除行とカラー有効、When フォーマットすると、Then 赤色の ANSI コードが付く', () => {
|
|
638
|
+
// Given: 削除行とカラー有効
|
|
639
|
+
const lineStr = '-old';
|
|
640
|
+
// When: カラー付きでフォーマットする
|
|
641
|
+
const result = formatDiffLineWithColor(lineStr, 'remove', true);
|
|
642
|
+
// Then: 赤色の ANSI コードが付く
|
|
643
|
+
expect(result).toBe('\x1b[31m-old\x1b[0m');
|
|
644
|
+
});
|
|
645
|
+
it('Given コンテキスト行とカラー有効、When フォーマットすると、Then ANSI コードなし', () => {
|
|
646
|
+
// Given: コンテキスト行とカラー有効
|
|
647
|
+
const lineStr = ' same';
|
|
648
|
+
// When: カラー付きでフォーマットする
|
|
649
|
+
const result = formatDiffLineWithColor(lineStr, 'context', true);
|
|
650
|
+
// Then: ANSI コードなし
|
|
651
|
+
expect(result).toBe(' same');
|
|
652
|
+
});
|
|
653
|
+
it('Given 追加行とカラー無効、When フォーマットすると、Then ANSI コードなし', () => {
|
|
654
|
+
// Given: 追加行とカラー無効
|
|
655
|
+
const lineStr = '+new';
|
|
656
|
+
// When: カラーなしでフォーマットする
|
|
657
|
+
const result = formatDiffLineWithColor(lineStr, 'add', false);
|
|
658
|
+
// Then: ANSI コードなし
|
|
659
|
+
expect(result).toBe('+new');
|
|
660
|
+
});
|
|
661
|
+
});
|
|
662
|
+
/**
|
|
663
|
+
* formatRemoveLine / formatAddLine のテスト
|
|
664
|
+
*
|
|
665
|
+
* Note: これらの関数は (field, value, colorEnabled) の3つのパラメータを取る
|
|
666
|
+
*/
|
|
667
|
+
describe('formatRemoveLine / formatAddLine (private)', () => {
|
|
668
|
+
it('Given フィールドと値とカラー有効、When formatRemoveLine すると、Then 赤色で "- field: value" 形式', () => {
|
|
669
|
+
// Given: フィールドと値とカラー有効
|
|
670
|
+
const field = 'status';
|
|
671
|
+
const value = 'Open';
|
|
672
|
+
// When: フォーマットする
|
|
673
|
+
const result = formatRemoveLine(field, value, true);
|
|
674
|
+
// Then: 赤色で "- field: value" 形式
|
|
675
|
+
expect(result).toBe('\x1b[31m- status: Open\x1b[0m');
|
|
676
|
+
});
|
|
677
|
+
it('Given フィールドと値とカラー無効、When formatRemoveLine すると、Then "- field: value" 形式のみ', () => {
|
|
678
|
+
// Given: フィールドと値とカラー無効
|
|
679
|
+
const field = 'status';
|
|
680
|
+
const value = 'Open';
|
|
681
|
+
// When: フォーマットする
|
|
682
|
+
const result = formatRemoveLine(field, value, false);
|
|
683
|
+
// Then: "- field: value" 形式のみ
|
|
684
|
+
expect(result).toBe('- status: Open');
|
|
685
|
+
});
|
|
686
|
+
it('Given フィールドと値とカラー有効、When formatAddLine すると、Then 緑色で "+ field: value" 形式', () => {
|
|
687
|
+
// Given: フィールドと値とカラー有効
|
|
688
|
+
const field = 'status';
|
|
689
|
+
const value = 'Closed';
|
|
690
|
+
// When: フォーマットする
|
|
691
|
+
const result = formatAddLine(field, value, true);
|
|
692
|
+
// Then: 緑色で "+ field: value" 形式
|
|
693
|
+
expect(result).toBe('\x1b[32m+ status: Closed\x1b[0m');
|
|
694
|
+
});
|
|
695
|
+
it('Given フィールドと値とカラー無効、When formatAddLine すると、Then "+ field: value" 形式のみ', () => {
|
|
696
|
+
// Given: フィールドと値とカラー無効
|
|
697
|
+
const field = 'status';
|
|
698
|
+
const value = 'Closed';
|
|
699
|
+
// When: フォーマットする
|
|
700
|
+
const result = formatAddLine(field, value, false);
|
|
701
|
+
// Then: "+ field: value" 形式のみ
|
|
702
|
+
expect(result).toBe('+ status: Closed');
|
|
703
|
+
});
|
|
704
|
+
it('Given null 値、When formatRemoveLine すると、Then "(なし)" が表示される', () => {
|
|
705
|
+
// Given: null 値
|
|
706
|
+
const field = 'assignee';
|
|
707
|
+
const value = null;
|
|
708
|
+
// When: フォーマットする
|
|
709
|
+
const result = formatRemoveLine(field, value, false);
|
|
710
|
+
// Then: "(なし)" が表示される
|
|
711
|
+
expect(result).toBe('- assignee: (なし)');
|
|
712
|
+
});
|
|
713
|
+
it('Given null 値、When formatAddLine すると、Then "(なし)" が表示される', () => {
|
|
714
|
+
// Given: null 値
|
|
715
|
+
const field = 'assignee';
|
|
716
|
+
const value = null;
|
|
717
|
+
// When: フォーマットする
|
|
718
|
+
const result = formatAddLine(field, value, false);
|
|
719
|
+
// Then: "(なし)" が表示される
|
|
720
|
+
expect(result).toBe('+ assignee: (なし)');
|
|
721
|
+
});
|
|
722
|
+
});
|
|
723
|
+
/**
|
|
724
|
+
* buildVersionHeader のテスト
|
|
725
|
+
*
|
|
726
|
+
* Note: 出力フォーマットは `v{old.number} → v{new.number} by {new.by}` 形式
|
|
727
|
+
*/
|
|
728
|
+
describe('buildVersionHeader (private)', () => {
|
|
729
|
+
it('Given stats が null、When ヘッダーを構築すると、Then 統計なしのヘッダーを返す', () => {
|
|
730
|
+
// Given: stats が null
|
|
731
|
+
const oldVersion = {
|
|
732
|
+
body: 'old body',
|
|
733
|
+
by: 'user1',
|
|
734
|
+
message: null,
|
|
735
|
+
number: 1,
|
|
736
|
+
when: '2024-01-01T00:00:00.000Z',
|
|
737
|
+
};
|
|
738
|
+
const newVersion = {
|
|
739
|
+
body: 'new body',
|
|
740
|
+
by: 'user2',
|
|
741
|
+
message: 'Updated',
|
|
742
|
+
number: 2,
|
|
743
|
+
when: '2024-01-02T00:00:00.000Z',
|
|
744
|
+
};
|
|
745
|
+
// When: ヘッダーを構築する
|
|
746
|
+
const result = buildVersionHeader(oldVersion, newVersion, null);
|
|
747
|
+
// Then: 統計なしのヘッダーを返す(v1 → v2 by user2 形式)
|
|
748
|
+
expect(result).toBe('v1 → v2 by user2');
|
|
749
|
+
});
|
|
750
|
+
it('Given stats がある、When ヘッダーを構築すると、Then 統計付きのヘッダーを返す', () => {
|
|
751
|
+
// Given: stats がある
|
|
752
|
+
const oldVersion = {
|
|
753
|
+
body: 'old body',
|
|
754
|
+
by: 'user1',
|
|
755
|
+
message: null,
|
|
756
|
+
number: 1,
|
|
757
|
+
when: '2024-01-01T00:00:00.000Z',
|
|
758
|
+
};
|
|
759
|
+
const newVersion = {
|
|
760
|
+
body: 'new body',
|
|
761
|
+
by: 'user2',
|
|
762
|
+
message: 'Updated',
|
|
763
|
+
number: 2,
|
|
764
|
+
when: '2024-01-02T00:00:00.000Z',
|
|
765
|
+
};
|
|
766
|
+
const stats = { additions: 5, changes: 1, deletions: 3 };
|
|
767
|
+
// When: ヘッダーを構築する
|
|
768
|
+
const result = buildVersionHeader(oldVersion, newVersion, stats);
|
|
769
|
+
// Then: 統計付きのヘッダーを返す(v1 → v2 by user2 (+5, -3) 形式)
|
|
770
|
+
expect(result).toBe('v1 → v2 by user2 (+5, -3)');
|
|
771
|
+
});
|
|
772
|
+
it('Given 同じユーザーによる複数バージョン、When ヘッダーを構築すると、Then 新バージョンの by を使用', () => {
|
|
773
|
+
// Given: 同じユーザーによる複数バージョン
|
|
774
|
+
const oldVersion = {
|
|
775
|
+
body: 'old body',
|
|
776
|
+
by: 'John Doe',
|
|
777
|
+
message: null,
|
|
778
|
+
number: 5,
|
|
779
|
+
when: '2024-01-01T00:00:00.000Z',
|
|
780
|
+
};
|
|
781
|
+
const newVersion = {
|
|
782
|
+
body: 'new body',
|
|
783
|
+
by: 'Jane Smith',
|
|
784
|
+
message: null,
|
|
785
|
+
number: 6,
|
|
786
|
+
when: '2024-01-02T00:00:00.000Z',
|
|
787
|
+
};
|
|
788
|
+
// When: ヘッダーを構築する
|
|
789
|
+
const result = buildVersionHeader(oldVersion, newVersion, null);
|
|
790
|
+
// Then: 新バージョンの by を使用
|
|
791
|
+
expect(result).toBe('v5 → v6 by Jane Smith');
|
|
792
|
+
});
|
|
793
|
+
});
|
|
794
|
+
/**
|
|
795
|
+
* formatUnifiedDiff のテスト
|
|
796
|
+
*
|
|
797
|
+
* Note: formatUnifiedDiff は hunks: readonly DiffHunk[] を取り、
|
|
798
|
+
* hunk.lines は DiffLine[] である(文字列ではない)
|
|
799
|
+
*/
|
|
800
|
+
describe('formatUnifiedDiff (private)', () => {
|
|
801
|
+
it('Given DiffHunk 配列、When フォーマットすると、Then unified diff 形式の文字列を返す', () => {
|
|
802
|
+
// Given: DiffHunk 配列(lines は DiffLine オブジェクト)
|
|
803
|
+
const hunks = [
|
|
804
|
+
{
|
|
805
|
+
lines: [
|
|
806
|
+
{ content: 'old', type: 'remove' },
|
|
807
|
+
{ content: 'new', type: 'add' },
|
|
808
|
+
],
|
|
809
|
+
newLines: 1,
|
|
810
|
+
newStart: 1,
|
|
811
|
+
oldLines: 1,
|
|
812
|
+
oldStart: 1,
|
|
813
|
+
},
|
|
814
|
+
];
|
|
815
|
+
// When: フォーマットする
|
|
816
|
+
const formatted = formatUnifiedDiff(hunks);
|
|
817
|
+
// Then: unified diff 形式の文字列を返す
|
|
818
|
+
expect(formatted).toContain('@@ -1,1 +1,1 @@');
|
|
819
|
+
expect(formatted).toContain('-old');
|
|
820
|
+
expect(formatted).toContain('+new');
|
|
821
|
+
});
|
|
822
|
+
it('Given 空のハンク配列、When フォーマットすると、Then 空文字列を返す', () => {
|
|
823
|
+
// Given: 空のハンク配列
|
|
824
|
+
const hunks = [];
|
|
825
|
+
// When: フォーマットする
|
|
826
|
+
const formatted = formatUnifiedDiff(hunks);
|
|
827
|
+
// Then: 空文字列を返す
|
|
828
|
+
expect(formatted).toBe('');
|
|
829
|
+
});
|
|
830
|
+
it('Given コンテキスト行を含むハンク、When フォーマットすると、Then 正しくフォーマットされる', () => {
|
|
831
|
+
// Given: コンテキスト行を含むハンク
|
|
832
|
+
const hunks = [
|
|
833
|
+
{
|
|
834
|
+
lines: [
|
|
835
|
+
{ content: 'unchanged1', type: 'context' },
|
|
836
|
+
{ content: 'old', type: 'remove' },
|
|
837
|
+
{ content: 'new', type: 'add' },
|
|
838
|
+
{ content: 'unchanged2', type: 'context' },
|
|
839
|
+
],
|
|
840
|
+
newLines: 3,
|
|
841
|
+
newStart: 1,
|
|
842
|
+
oldLines: 3,
|
|
843
|
+
oldStart: 1,
|
|
844
|
+
},
|
|
845
|
+
];
|
|
846
|
+
// When: フォーマットする
|
|
847
|
+
const formatted = formatUnifiedDiff(hunks);
|
|
848
|
+
// Then: 正しくフォーマットされる
|
|
849
|
+
expect(formatted).toContain(' unchanged1');
|
|
850
|
+
expect(formatted).toContain('-old');
|
|
851
|
+
expect(formatted).toContain('+new');
|
|
852
|
+
expect(formatted).toContain(' unchanged2');
|
|
853
|
+
});
|
|
854
|
+
it('Given 複数のハンク、When フォーマットすると、Then 全てのハンクが含まれる', () => {
|
|
855
|
+
// Given: 複数のハンク
|
|
856
|
+
const hunks = [
|
|
857
|
+
{
|
|
858
|
+
lines: [{ content: 'first', type: 'remove' }],
|
|
859
|
+
newLines: 1,
|
|
860
|
+
newStart: 1,
|
|
861
|
+
oldLines: 1,
|
|
862
|
+
oldStart: 1,
|
|
863
|
+
},
|
|
864
|
+
{
|
|
865
|
+
lines: [{ content: 'second', type: 'add' }],
|
|
866
|
+
newLines: 1,
|
|
867
|
+
newStart: 10,
|
|
868
|
+
oldLines: 1,
|
|
869
|
+
oldStart: 10,
|
|
870
|
+
},
|
|
871
|
+
];
|
|
872
|
+
// When: フォーマットする
|
|
873
|
+
const formatted = formatUnifiedDiff(hunks);
|
|
874
|
+
// Then: 全てのハンクが含まれる
|
|
875
|
+
expect(formatted).toContain('@@ -1,1 +1,1 @@');
|
|
876
|
+
expect(formatted).toContain('@@ -10,1 +10,1 @@');
|
|
877
|
+
expect(formatted).toContain('-first');
|
|
878
|
+
expect(formatted).toContain('+second');
|
|
879
|
+
});
|
|
880
|
+
});
|
|
881
|
+
}
|