github-issue-tower-defence-management 2.7.0 → 2.9.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.
Files changed (80) hide show
  1. package/.github/workflows/umino-project.yml +0 -8
  2. package/CHANGELOG.md +14 -0
  3. package/README.md +1 -1
  4. package/bin/adapter/entry-points/cli/index.js +3 -1
  5. package/bin/adapter/entry-points/cli/index.js.map +1 -1
  6. package/bin/adapter/entry-points/console/consoleAirplaneSnapshotApi.js +347 -0
  7. package/bin/adapter/entry-points/console/consoleAirplaneSnapshotApi.js.map +1 -0
  8. package/bin/adapter/entry-points/console/consoleOperationApi.js +12 -0
  9. package/bin/adapter/entry-points/console/consoleOperationApi.js.map +1 -1
  10. package/bin/adapter/entry-points/console/ui-dist/assets/index-BSa2JTna.js +88 -0
  11. package/bin/adapter/entry-points/console/ui-dist/index.html +1 -1
  12. package/bin/adapter/entry-points/console/webServer.js +28 -0
  13. package/bin/adapter/entry-points/console/webServer.js.map +1 -1
  14. package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js +5 -1
  15. package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js.map +1 -1
  16. package/bin/adapter/repositories/projectFieldDefinition.js +1 -0
  17. package/bin/adapter/repositories/projectFieldDefinition.js.map +1 -1
  18. package/jest.config.js +1 -0
  19. package/package.json +1 -1
  20. package/src/adapter/entry-points/cli/index.ts +4 -1
  21. package/src/adapter/entry-points/console/consoleAirplaneSnapshotApi.test.ts +658 -0
  22. package/src/adapter/entry-points/console/consoleAirplaneSnapshotApi.ts +482 -0
  23. package/src/adapter/entry-points/console/consoleOperationApi.test.ts +84 -0
  24. package/src/adapter/entry-points/console/consoleOperationApi.ts +17 -0
  25. package/src/adapter/entry-points/console/ui/e2e/consoleScenario.spec.ts +6 -0
  26. package/src/adapter/entry-points/console/ui/e2e/consoleTestHarness.ts +5 -1
  27. package/src/adapter/entry-points/console/ui/jest-globals.cjs +26 -0
  28. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFileDiff.test.tsx +10 -0
  29. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFileDiff.tsx +23 -16
  30. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleAirplaneModeButton.stories.tsx +56 -0
  31. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleAirplaneModeButton.test.tsx +111 -0
  32. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleAirplaneModeButton.tsx +92 -0
  33. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTabList.stories.tsx +15 -0
  34. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTabList.test.tsx +19 -0
  35. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTabList.tsx +30 -1
  36. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleErrorToast.stories.tsx +10 -0
  37. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleNextActionDateActions.test.tsx +7 -1
  38. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleNextActionDateActions.tsx +14 -0
  39. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleUndoToast.test.tsx +1 -3
  40. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleUndoToast.tsx +8 -7
  41. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useAirplaneMode.test.ts +304 -0
  42. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useAirplaneMode.ts +167 -0
  43. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleActionQueue.test.ts +15 -0
  44. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleActionQueue.ts +6 -1
  45. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleCaches.test.ts +77 -0
  46. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleCaches.ts +48 -3
  47. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleFeaturesConfig.test.ts +51 -0
  48. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleFeaturesConfig.ts +39 -0
  49. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleTabData.test.ts +215 -0
  50. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleTabData.ts +20 -1
  51. package/src/adapter/entry-points/console/ui/src/features/console/lib/airplaneSnapshot.test.ts +361 -0
  52. package/src/adapter/entry-points/console/ui/src/features/console/lib/airplaneSnapshot.ts +401 -0
  53. package/src/adapter/entry-points/console/ui/src/features/console/lib/resourceCache.ts +4 -0
  54. package/src/adapter/entry-points/console/ui/src/features/console/logic/actionToast.test.ts +22 -0
  55. package/src/adapter/entry-points/console/ui/src/features/console/logic/actionToast.ts +6 -0
  56. package/src/adapter/entry-points/console/ui/src/features/console/logic/operations.ts +5 -1
  57. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.test.tsx +209 -0
  58. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.tsx +27 -2
  59. package/src/adapter/entry-points/console/ui-dist/assets/index-BSa2JTna.js +88 -0
  60. package/src/adapter/entry-points/console/ui-dist/index.html +1 -1
  61. package/src/adapter/entry-points/console/webServer.test.ts +189 -0
  62. package/src/adapter/entry-points/console/webServer.ts +38 -0
  63. package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.test.ts +63 -8
  64. package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.ts +7 -1
  65. package/src/adapter/repositories/projectFieldDefinition.test.ts +1 -0
  66. package/src/adapter/repositories/projectFieldDefinition.ts +1 -0
  67. package/src/domain/entities/Project.ts +1 -0
  68. package/src/domain/usecases/ClearPastNextActionDateHourUseCase.test.ts +1 -0
  69. package/types/adapter/entry-points/cli/index.d.ts.map +1 -1
  70. package/types/adapter/entry-points/console/consoleAirplaneSnapshotApi.d.ts +90 -0
  71. package/types/adapter/entry-points/console/consoleAirplaneSnapshotApi.d.ts.map +1 -0
  72. package/types/adapter/entry-points/console/consoleOperationApi.d.ts.map +1 -1
  73. package/types/adapter/entry-points/console/webServer.d.ts +1 -0
  74. package/types/adapter/entry-points/console/webServer.d.ts.map +1 -1
  75. package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts.map +1 -1
  76. package/types/adapter/repositories/projectFieldDefinition.d.ts.map +1 -1
  77. package/types/domain/entities/Project.d.ts +1 -0
  78. package/types/domain/entities/Project.d.ts.map +1 -1
  79. package/bin/adapter/entry-points/console/ui-dist/assets/index-CaxYBHiT.js +0 -86
  80. package/src/adapter/entry-points/console/ui-dist/assets/index-CaxYBHiT.js +0 -86
@@ -0,0 +1,361 @@
1
+ import {
2
+ parseAirplaneSnapshot,
3
+ readAirplaneModeFlag,
4
+ storeAirplaneSnapshot,
5
+ writeAirplaneModeFlag,
6
+ } from './airplaneSnapshot';
7
+
8
+ const minimalSnapshot = () => ({
9
+ capturedAt: '2026-01-01T00:00:00Z',
10
+ tabs: {},
11
+ items: {},
12
+ failures: [],
13
+ });
14
+
15
+ describe('airplaneSnapshot', () => {
16
+ describe('parseAirplaneSnapshot', () => {
17
+ it('returns null for non-object input', () => {
18
+ expect(parseAirplaneSnapshot(null)).toBeNull();
19
+ expect(parseAirplaneSnapshot('string')).toBeNull();
20
+ expect(parseAirplaneSnapshot(42)).toBeNull();
21
+ });
22
+
23
+ it('parses a minimal valid snapshot', () => {
24
+ const raw = {
25
+ capturedAt: '2026-01-01T00:00:00Z',
26
+ tabs: {},
27
+ items: {},
28
+ failures: [],
29
+ };
30
+ const result = parseAirplaneSnapshot(raw);
31
+ expect(result).not.toBeNull();
32
+ expect(result?.capturedAt).toBe('2026-01-01T00:00:00Z');
33
+ expect(result?.failures).toEqual([]);
34
+ });
35
+
36
+ it('parses tab snapshots with items', () => {
37
+ const raw = {
38
+ capturedAt: '2026-01-01T00:00:00Z',
39
+ tabs: {
40
+ acme: {
41
+ prs: {
42
+ generatedAt: '2026-01-01T00:00:00Z',
43
+ statusOptions: [{ id: 'x', name: 'Open', color: 'GREEN' }],
44
+ storyOptions: [],
45
+ storyColors: {},
46
+ items: [
47
+ {
48
+ number: 1,
49
+ title: 'Fix bug',
50
+ url: 'https://github.com/o/r/pull/1',
51
+ repo: 'o/r',
52
+ nameWithOwner: 'o/r',
53
+ projectItemId: 'PVTI_1',
54
+ itemId: 'PVTI_1',
55
+ isPr: true,
56
+ story: 'regular',
57
+ status: 'In Progress',
58
+ nextActionDate: null,
59
+ nextActionHour: null,
60
+ dependedIssueUrls: [],
61
+ labels: ['bug'],
62
+ createdAt: '2026-01-01T00:00:00Z',
63
+ relatedOpenPullRequestUrls: [],
64
+ },
65
+ ],
66
+ },
67
+ },
68
+ },
69
+ items: {},
70
+ failures: [],
71
+ };
72
+
73
+ const result = parseAirplaneSnapshot(raw);
74
+ expect(result?.tabs.acme?.prs.items).toHaveLength(1);
75
+ expect(result?.tabs.acme?.prs.items[0].title).toBe('Fix bug');
76
+ expect(result?.tabs.acme?.prs.statusOptions).toHaveLength(1);
77
+ expect(result?.tabs.acme?.prs.fromCache).toBe(false);
78
+ expect(result?.tabs.acme?.prs.storyOrder).toEqual([]);
79
+ });
80
+
81
+ it('parses storyOrder and fromCache from a tab snapshot', () => {
82
+ const raw = {
83
+ capturedAt: '2026-01-01T00:00:00Z',
84
+ tabs: {
85
+ acme: {
86
+ prs: {
87
+ generatedAt: '2026-01-01T00:00:00Z',
88
+ statusOptions: [],
89
+ storyOptions: [],
90
+ storyColors: {},
91
+ items: [],
92
+ stories: [],
93
+ defaultNameWithOwner: null,
94
+ storyOrder: ['regular', 'chore'],
95
+ },
96
+ },
97
+ },
98
+ items: {},
99
+ failures: [],
100
+ };
101
+ const result = parseAirplaneSnapshot(raw);
102
+ expect(result?.tabs.acme?.prs.storyOrder).toEqual(['regular', 'chore']);
103
+ expect(result?.tabs.acme?.prs.fromCache).toBe(false);
104
+ });
105
+
106
+ it('parses item snapshots with PR fields', () => {
107
+ const url = 'https://github.com/o/r/pull/1';
108
+ const raw = {
109
+ capturedAt: '2026-01-01T00:00:00Z',
110
+ tabs: {},
111
+ failures: [],
112
+ items: {
113
+ [url]: {
114
+ body: 'PR body text',
115
+ comments: [
116
+ {
117
+ author: 'alice',
118
+ body: 'LGTM',
119
+ createdAt: '2026-01-01T01:00:00Z',
120
+ },
121
+ ],
122
+ state: {
123
+ state: 'open',
124
+ merged: false,
125
+ isPullRequest: true,
126
+ title: 'PR',
127
+ },
128
+ files: [
129
+ {
130
+ path: 'src/foo.ts',
131
+ additions: 5,
132
+ deletions: 2,
133
+ status: 'modified',
134
+ patch: '@@ diff @@',
135
+ rawUrl: null,
136
+ },
137
+ ],
138
+ commits: [
139
+ {
140
+ sha: 'abc',
141
+ message: 'fix',
142
+ author: 'alice',
143
+ authoredAt: '2026-01-01T00:00:00Z',
144
+ },
145
+ ],
146
+ prStatus: {
147
+ found: true,
148
+ isConflicted: false,
149
+ mergeableStatus: 'MERGEABLE',
150
+ isPassedAllCiJob: true,
151
+ isCiStateSuccess: true,
152
+ isBranchOutOfDate: false,
153
+ missingRequiredCheckNames: [],
154
+ },
155
+ relatedPrs: null,
156
+ },
157
+ },
158
+ };
159
+
160
+ const result = parseAirplaneSnapshot(raw);
161
+ const item = result?.items[url];
162
+ expect(item?.body).toBe('PR body text');
163
+ expect(item?.comments).toHaveLength(1);
164
+ expect(item?.comments[0].author).toBe('alice');
165
+ expect(item?.state.isPullRequest).toBe(true);
166
+ expect(item?.files).toHaveLength(1);
167
+ expect(item?.files?.[0].path).toBe('src/foo.ts');
168
+ expect(item?.commits).toHaveLength(1);
169
+ expect(item?.prStatus?.isPassedAllCiJob).toBe(true);
170
+ expect(item?.prStatus?.mergeableStatus).toBe('MERGEABLE');
171
+ expect(item?.relatedPrs).toBeNull();
172
+ });
173
+
174
+ it('parses item snapshots with issue fields', () => {
175
+ const url = 'https://github.com/o/r/issues/5';
176
+ const raw = {
177
+ capturedAt: '2026-01-01T00:00:00Z',
178
+ tabs: {},
179
+ failures: [],
180
+ items: {
181
+ [url]: {
182
+ body: 'Issue body',
183
+ comments: [],
184
+ state: {
185
+ state: 'open',
186
+ merged: false,
187
+ isPullRequest: false,
188
+ title: 'Issue',
189
+ },
190
+ files: null,
191
+ commits: null,
192
+ prStatus: null,
193
+ relatedPrs: [
194
+ {
195
+ url: 'https://github.com/o/r/pull/10',
196
+ branchName: 'fix-branch',
197
+ createdAt: '2026-01-01T00:00:00Z',
198
+ isDraft: false,
199
+ isConflicted: false,
200
+ mergeableStatus: 'MERGEABLE',
201
+ isPassedAllCiJob: true,
202
+ isCiStateSuccess: true,
203
+ isResolvedAllReviewComments: true,
204
+ isBranchOutOfDate: false,
205
+ missingRequiredCheckNames: [],
206
+ summary: null,
207
+ },
208
+ ],
209
+ },
210
+ },
211
+ };
212
+
213
+ const result = parseAirplaneSnapshot(raw);
214
+ const item = result?.items[url];
215
+ expect(item?.files).toBeNull();
216
+ expect(item?.commits).toBeNull();
217
+ expect(item?.prStatus).toBeNull();
218
+ expect(item?.relatedPrs).toHaveLength(1);
219
+ expect(item?.relatedPrs?.[0].url).toBe('https://github.com/o/r/pull/10');
220
+ });
221
+
222
+ it('handles unknown mergeableStatus gracefully', () => {
223
+ const url = 'https://github.com/o/r/pull/2';
224
+ const raw = {
225
+ capturedAt: '',
226
+ tabs: {},
227
+ failures: [],
228
+ items: {
229
+ [url]: {
230
+ body: '',
231
+ comments: [],
232
+ state: {
233
+ state: 'open',
234
+ merged: false,
235
+ isPullRequest: true,
236
+ title: '',
237
+ },
238
+ files: [],
239
+ commits: [],
240
+ prStatus: {
241
+ found: true,
242
+ isConflicted: false,
243
+ mergeableStatus: 'UNKNOWN',
244
+ isPassedAllCiJob: false,
245
+ isCiStateSuccess: false,
246
+ isBranchOutOfDate: false,
247
+ missingRequiredCheckNames: [],
248
+ },
249
+ relatedPrs: null,
250
+ },
251
+ },
252
+ };
253
+ const result = parseAirplaneSnapshot(raw);
254
+ expect(result?.items[url]?.prStatus?.mergeableStatus).toBe('UNKNOWN');
255
+ });
256
+
257
+ it('records failures from the raw payload', () => {
258
+ const raw = {
259
+ capturedAt: '',
260
+ tabs: {},
261
+ items: {},
262
+ failures: ['https://github.com/o/r/issues/99'],
263
+ };
264
+ const result = parseAirplaneSnapshot(raw);
265
+ expect(result?.failures).toContain('https://github.com/o/r/issues/99');
266
+ });
267
+
268
+ it('parses stories and defaultNameWithOwner from a tab snapshot', () => {
269
+ const raw = {
270
+ capturedAt: '2026-01-01T00:00:00Z',
271
+ tabs: {
272
+ acme: {
273
+ stories: {
274
+ generatedAt: '2026-01-01T00:00:00Z',
275
+ statusOptions: [],
276
+ storyOptions: [],
277
+ storyColors: {},
278
+ items: [],
279
+ stories: [
280
+ {
281
+ storyName: 'regular',
282
+ storyOptionId: 'opt-1',
283
+ color: 'GREEN',
284
+ openItemCount: 3,
285
+ },
286
+ ],
287
+ defaultNameWithOwner: 'owner/repo',
288
+ },
289
+ },
290
+ },
291
+ items: {},
292
+ failures: [],
293
+ };
294
+ const result = parseAirplaneSnapshot(raw);
295
+ const storiesTab = result?.tabs.acme?.stories;
296
+ expect(storiesTab?.stories).toHaveLength(1);
297
+ expect(storiesTab?.stories[0].storyName).toBe('regular');
298
+ expect(storiesTab?.defaultNameWithOwner).toBe('owner/repo');
299
+ });
300
+ });
301
+
302
+ describe('readAirplaneModeFlag / writeAirplaneModeFlag', () => {
303
+ beforeEach(() => {
304
+ localStorage.clear();
305
+ });
306
+
307
+ it('returns false when the flag has never been written', () => {
308
+ expect(readAirplaneModeFlag()).toBe(false);
309
+ });
310
+
311
+ it('returns true after writeAirplaneModeFlag(true)', () => {
312
+ writeAirplaneModeFlag(true);
313
+ expect(readAirplaneModeFlag()).toBe(true);
314
+ });
315
+
316
+ it('returns false after writeAirplaneModeFlag(false)', () => {
317
+ writeAirplaneModeFlag(true);
318
+ writeAirplaneModeFlag(false);
319
+ expect(readAirplaneModeFlag()).toBe(false);
320
+ });
321
+ });
322
+
323
+ describe('storeAirplaneSnapshot', () => {
324
+ it('rejects when Cache API is unavailable', async () => {
325
+ const originalCaches = globalThis.caches;
326
+ Object.defineProperty(globalThis, 'caches', {
327
+ configurable: true,
328
+ writable: true,
329
+ value: undefined,
330
+ });
331
+ await expect(storeAirplaneSnapshot(minimalSnapshot())).rejects.toThrow(
332
+ 'Cache API unavailable',
333
+ );
334
+ Object.defineProperty(globalThis, 'caches', {
335
+ configurable: true,
336
+ writable: true,
337
+ value: originalCaches,
338
+ });
339
+ });
340
+
341
+ it('rejects with a user-readable message when cache.put throws QuotaExceededError', async () => {
342
+ const quotaError = new DOMException(
343
+ 'QuotaExceededError',
344
+ 'QuotaExceededError',
345
+ );
346
+ const mockCache = {
347
+ put: jest.fn().mockRejectedValue(quotaError),
348
+ };
349
+ Object.defineProperty(globalThis, 'caches', {
350
+ configurable: true,
351
+ writable: true,
352
+ value: {
353
+ open: jest.fn().mockResolvedValue(mockCache),
354
+ },
355
+ });
356
+ await expect(storeAirplaneSnapshot(minimalSnapshot())).rejects.toThrow(
357
+ 'browser storage',
358
+ );
359
+ });
360
+ });
361
+ });