github-issue-tower-defence-management 1.143.0 → 1.143.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/bin/adapter/entry-points/console/ui-dist/assets/index-BWy8G--c.js +81 -0
- package/bin/adapter/entry-points/console/ui-dist/index.html +1 -1
- package/package.json +1 -1
- package/src/adapter/entry-points/console/ui/e2e/consoleScenario.spec.ts +36 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/overlay.test.ts +55 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/overlay.ts +17 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.test.tsx +68 -1
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.tsx +12 -2
- package/src/adapter/entry-points/console/ui-dist/assets/index-BWy8G--c.js +81 -0
- package/src/adapter/entry-points/console/ui-dist/index.html +1 -1
- package/bin/adapter/entry-points/console/ui-dist/assets/index-C1KKZ_a1.js +0 -81
- package/src/adapter/entry-points/console/ui-dist/assets/index-C1KKZ_a1.js +0 -81
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>TDPM Console</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-BWy8G--c.js"></script>
|
|
8
8
|
<link rel="stylesheet" crossorigin href="/assets/index-Bp-p-XOs.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
package/package.json
CHANGED
|
@@ -234,3 +234,39 @@ test('collects an inline comment on a related pull request diff without hover on
|
|
|
234
234
|
|
|
235
235
|
await touchContext.close();
|
|
236
236
|
});
|
|
237
|
+
|
|
238
|
+
test('lists a still-open item and keeps its tab visible when the browser overlay marked it done before the served snapshot was generated', async ({
|
|
239
|
+
page,
|
|
240
|
+
}) => {
|
|
241
|
+
await page.goto(harness.appRootUrl);
|
|
242
|
+
|
|
243
|
+
await expect(tabByLabel(page, 'Todo by human')).toBeVisible();
|
|
244
|
+
|
|
245
|
+
await page.evaluate(() => {
|
|
246
|
+
localStorage.setItem(
|
|
247
|
+
'pv_overlay_umino',
|
|
248
|
+
JSON.stringify({
|
|
249
|
+
PVTI_lADOABCD1234zgTODO00869: {
|
|
250
|
+
done: true,
|
|
251
|
+
ts: Date.parse('2026-06-18T00:30:00.000Z'),
|
|
252
|
+
mode: 'todo-by-human',
|
|
253
|
+
},
|
|
254
|
+
}),
|
|
255
|
+
);
|
|
256
|
+
});
|
|
257
|
+
await page.reload();
|
|
258
|
+
await page.locator('.console-tabbar').screenshot({
|
|
259
|
+
path: '/tmp/console-tabbar-after-regeneration.png',
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
await expect(tabByLabel(page, 'Todo by human')).toBeVisible();
|
|
263
|
+
await expect(tabBadge(page, 'Todo by human')).toHaveText('1');
|
|
264
|
+
|
|
265
|
+
await tabByLabel(page, 'Todo by human').click();
|
|
266
|
+
await expect(
|
|
267
|
+
itemRowByText(
|
|
268
|
+
page,
|
|
269
|
+
'Auto-advance to the next non-empty console tab when one empties',
|
|
270
|
+
),
|
|
271
|
+
).toBeVisible();
|
|
272
|
+
});
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
countPendingItems,
|
|
3
3
|
filterPendingItems,
|
|
4
4
|
isOverlayEntryActed,
|
|
5
|
+
overlayEntriesActedSinceSnapshot,
|
|
5
6
|
overlayKeyForItem,
|
|
6
7
|
overlayStorageKey,
|
|
7
8
|
writeOverlayEntry,
|
|
@@ -149,6 +150,60 @@ describe('workflow-blocker items are filtered by the done overlay like every oth
|
|
|
149
150
|
});
|
|
150
151
|
});
|
|
151
152
|
|
|
153
|
+
describe('overlayEntriesActedSinceSnapshot', () => {
|
|
154
|
+
it('drops an entry written before the snapshot was generated so the item is pending again', () => {
|
|
155
|
+
const overlay: ConsoleOverlay = {
|
|
156
|
+
PVTI_1: {
|
|
157
|
+
ts: Date.parse('2026-08-04T00:20:00.000Z'),
|
|
158
|
+
mode: 'todo-by-human',
|
|
159
|
+
done: true,
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
const acted = overlayEntriesActedSinceSnapshot(
|
|
163
|
+
overlay,
|
|
164
|
+
'2026-08-04T00:26:06Z',
|
|
165
|
+
);
|
|
166
|
+
expect(acted).toEqual({});
|
|
167
|
+
expect(countPendingItems([item(1)], acted)).toBe(1);
|
|
168
|
+
expect(filterPendingItems([item(1)], acted)).toEqual([item(1)]);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('keeps an entry written after the snapshot was generated so the item stays hidden', () => {
|
|
172
|
+
const overlay: ConsoleOverlay = {
|
|
173
|
+
PVTI_1: {
|
|
174
|
+
ts: Date.parse('2026-08-04T00:30:00.000Z'),
|
|
175
|
+
mode: 'todo-by-human',
|
|
176
|
+
done: true,
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
const acted = overlayEntriesActedSinceSnapshot(
|
|
180
|
+
overlay,
|
|
181
|
+
'2026-08-04T00:26:06Z',
|
|
182
|
+
);
|
|
183
|
+
expect(countPendingItems([item(1)], acted)).toBe(0);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('keeps an entry written at the exact snapshot generation time', () => {
|
|
187
|
+
const overlay: ConsoleOverlay = {
|
|
188
|
+
PVTI_1: {
|
|
189
|
+
ts: Date.parse('2026-08-04T00:26:06Z'),
|
|
190
|
+
mode: 'todo-by-human',
|
|
191
|
+
done: true,
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
expect(
|
|
195
|
+
overlayEntriesActedSinceSnapshot(overlay, '2026-08-04T00:26:06Z'),
|
|
196
|
+
).toEqual(overlay);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('suppresses nothing when the snapshot carries no parsable generation time', () => {
|
|
200
|
+
const overlay: ConsoleOverlay = {
|
|
201
|
+
PVTI_1: { ts: 500, mode: 'todo-by-human', done: true },
|
|
202
|
+
};
|
|
203
|
+
expect(overlayEntriesActedSinceSnapshot(overlay, '')).toEqual({});
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
152
207
|
describe('writeOverlayEntry', () => {
|
|
153
208
|
it('stamps the timestamp and mode on write', () => {
|
|
154
209
|
const next = writeOverlayEntry({}, 'PVTI_1', { done: true }, 'prs', 1234);
|
|
@@ -30,6 +30,23 @@ export const filterPendingItems = (
|
|
|
30
30
|
(item) => !isOverlayEntryActed(overlay[overlayKeyForItem(item)]),
|
|
31
31
|
);
|
|
32
32
|
|
|
33
|
+
export const overlayEntriesActedSinceSnapshot = (
|
|
34
|
+
overlay: ConsoleOverlay,
|
|
35
|
+
snapshotGeneratedAt: string,
|
|
36
|
+
): ConsoleOverlay => {
|
|
37
|
+
const snapshotGeneratedAtMs = Date.parse(snapshotGeneratedAt);
|
|
38
|
+
if (Number.isNaN(snapshotGeneratedAtMs)) {
|
|
39
|
+
return {};
|
|
40
|
+
}
|
|
41
|
+
const acted: ConsoleOverlay = {};
|
|
42
|
+
for (const [key, entry] of Object.entries(overlay)) {
|
|
43
|
+
if (entry.ts >= snapshotGeneratedAtMs) {
|
|
44
|
+
acted[key] = entry;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return acted;
|
|
48
|
+
};
|
|
49
|
+
|
|
33
50
|
export const writeOverlayEntry = (
|
|
34
51
|
overlay: ConsoleOverlay,
|
|
35
52
|
key: string,
|
|
@@ -310,7 +310,11 @@ describe('ConsolePage', () => {
|
|
|
310
310
|
localStorage.setItem(
|
|
311
311
|
'pv_overlay_umino',
|
|
312
312
|
JSON.stringify({
|
|
313
|
-
PVTI_B1: {
|
|
313
|
+
PVTI_B1: {
|
|
314
|
+
ts: Date.parse('2026-06-19T00:05:00.000Z'),
|
|
315
|
+
mode: 'workflow-blocker',
|
|
316
|
+
done: true,
|
|
317
|
+
},
|
|
314
318
|
}),
|
|
315
319
|
);
|
|
316
320
|
window.history.replaceState(
|
|
@@ -333,6 +337,69 @@ describe('ConsolePage', () => {
|
|
|
333
337
|
);
|
|
334
338
|
});
|
|
335
339
|
|
|
340
|
+
it('lists an item again when the overlay marked it done before the served snapshot was generated', async () => {
|
|
341
|
+
const blockerItems = [
|
|
342
|
+
{
|
|
343
|
+
number: 701,
|
|
344
|
+
title: 'Blocked deployment task',
|
|
345
|
+
url: 'https://github.com/o/r/issues/701',
|
|
346
|
+
repo: 'o/r',
|
|
347
|
+
nameWithOwner: 'o/r',
|
|
348
|
+
projectItemId: 'PVTI_B1',
|
|
349
|
+
itemId: 'PVTI_B1',
|
|
350
|
+
isPr: false,
|
|
351
|
+
story: 'TDPM Console port',
|
|
352
|
+
status: 'In Progress',
|
|
353
|
+
nextActionDate: null,
|
|
354
|
+
nextActionHour: null,
|
|
355
|
+
dependedIssueUrls: [],
|
|
356
|
+
labels: [],
|
|
357
|
+
createdAt: '2026-06-17T00:00:00.000Z',
|
|
358
|
+
},
|
|
359
|
+
];
|
|
360
|
+
const fetchMock = jest.fn(async (url: string) => {
|
|
361
|
+
const listMatch = url.match(/\/projects\/[^/]+\/([^/]+)\/list\.json/);
|
|
362
|
+
if (listMatch !== null) {
|
|
363
|
+
return {
|
|
364
|
+
ok: true,
|
|
365
|
+
status: 200,
|
|
366
|
+
json: async () =>
|
|
367
|
+
listMatch[1] === 'workflow-blocker'
|
|
368
|
+
? { ...listPayload('workflow-blocker'), items: blockerItems }
|
|
369
|
+
: listPayload(listMatch[1]),
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
return { ok: true, status: 200, json: async () => ({ body: '# body' }) };
|
|
373
|
+
});
|
|
374
|
+
global.fetch = fetchMock as unknown as typeof fetch;
|
|
375
|
+
localStorage.setItem(
|
|
376
|
+
'pv_overlay_umino',
|
|
377
|
+
JSON.stringify({
|
|
378
|
+
PVTI_B1: {
|
|
379
|
+
ts: Date.parse('2026-06-18T23:00:00.000Z'),
|
|
380
|
+
mode: 'workflow-blocker',
|
|
381
|
+
done: true,
|
|
382
|
+
},
|
|
383
|
+
}),
|
|
384
|
+
);
|
|
385
|
+
window.history.replaceState(
|
|
386
|
+
{},
|
|
387
|
+
'',
|
|
388
|
+
'/projects/umino/workflow-blocker?k=token',
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
const { getByText } = render(<ConsolePage />);
|
|
392
|
+
await waitFor(() => {
|
|
393
|
+
expect(getByText('Blocked deployment task')).toBeInTheDocument();
|
|
394
|
+
});
|
|
395
|
+
const blockerTab = within(tabBar())
|
|
396
|
+
.getByText('Workflow Blocker')
|
|
397
|
+
.closest('a');
|
|
398
|
+
expect(blockerTab?.querySelector('.console-tab-badge')?.textContent).toBe(
|
|
399
|
+
'1',
|
|
400
|
+
);
|
|
401
|
+
});
|
|
402
|
+
|
|
336
403
|
it('renders the failure toast in English without any Japanese characters', async () => {
|
|
337
404
|
const fetchMock = jest.fn(
|
|
338
405
|
async (url: string, init?: { method?: string }) => {
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
import {
|
|
29
29
|
countPendingItems,
|
|
30
30
|
filterPendingItems,
|
|
31
|
+
overlayEntriesActedSinceSnapshot,
|
|
31
32
|
overlayKeyForItem,
|
|
32
33
|
} from '../logic/overlay';
|
|
33
34
|
import type { ConsoleSwipeDirection } from '../logic/swipe';
|
|
@@ -67,7 +68,10 @@ export const ConsolePage = () => {
|
|
|
67
68
|
}
|
|
68
69
|
result[tab.name] = countPendingItems(
|
|
69
70
|
snapshot.items,
|
|
70
|
-
|
|
71
|
+
overlayEntriesActedSinceSnapshot(
|
|
72
|
+
overlayState.overlay,
|
|
73
|
+
snapshot.generatedAt,
|
|
74
|
+
),
|
|
71
75
|
);
|
|
72
76
|
}
|
|
73
77
|
return result;
|
|
@@ -92,7 +96,13 @@ export const ConsolePage = () => {
|
|
|
92
96
|
if (activeSnapshot === null) {
|
|
93
97
|
return [];
|
|
94
98
|
}
|
|
95
|
-
return filterPendingItems(
|
|
99
|
+
return filterPendingItems(
|
|
100
|
+
activeSnapshot.items,
|
|
101
|
+
overlayEntriesActedSinceSnapshot(
|
|
102
|
+
overlayState.overlay,
|
|
103
|
+
activeSnapshot.generatedAt,
|
|
104
|
+
),
|
|
105
|
+
);
|
|
96
106
|
}, [activeSnapshot, overlayState.overlay]);
|
|
97
107
|
|
|
98
108
|
const orderedPendingKeys = useMemo(
|