opensoma 0.5.1 → 0.6.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/dist/package.json +5 -1
- package/dist/src/agent-browser-launcher.d.ts +43 -0
- package/dist/src/agent-browser-launcher.d.ts.map +1 -0
- package/dist/src/agent-browser-launcher.js +97 -0
- package/dist/src/agent-browser-launcher.js.map +1 -0
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +3 -2
- package/dist/src/cli.js.map +1 -1
- package/dist/src/client.d.ts +30 -7
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/client.js +218 -52
- package/dist/src/client.js.map +1 -1
- package/dist/src/commands/agent-browser.d.ts +3 -0
- package/dist/src/commands/agent-browser.d.ts.map +1 -0
- package/dist/src/commands/agent-browser.js +27 -0
- package/dist/src/commands/agent-browser.js.map +1 -0
- package/dist/src/commands/auth.d.ts +1 -1
- package/dist/src/commands/auth.d.ts.map +1 -1
- package/dist/src/commands/auth.js +4 -2
- package/dist/src/commands/auth.js.map +1 -1
- package/dist/src/commands/dashboard.d.ts +13 -0
- package/dist/src/commands/dashboard.d.ts.map +1 -1
- package/dist/src/commands/dashboard.js +10 -18
- package/dist/src/commands/dashboard.js.map +1 -1
- package/dist/src/commands/helpers.d.ts +1 -1
- package/dist/src/commands/helpers.d.ts.map +1 -1
- package/dist/src/commands/helpers.js +2 -2
- package/dist/src/commands/helpers.js.map +1 -1
- package/dist/src/commands/index.d.ts +2 -1
- package/dist/src/commands/index.d.ts.map +1 -1
- package/dist/src/commands/index.js +2 -1
- package/dist/src/commands/index.js.map +1 -1
- package/dist/src/commands/mentoring.d.ts.map +1 -1
- package/dist/src/commands/mentoring.js +54 -29
- package/dist/src/commands/mentoring.js.map +1 -1
- package/dist/src/commands/notice.d.ts.map +1 -1
- package/dist/src/commands/notice.js +2 -1
- package/dist/src/commands/notice.js.map +1 -1
- package/dist/src/commands/report.d.ts.map +1 -1
- package/dist/src/commands/report.js +4 -2
- package/dist/src/commands/report.js.map +1 -1
- package/dist/src/commands/room.d.ts.map +1 -1
- package/dist/src/commands/room.js +125 -2
- package/dist/src/commands/room.js.map +1 -1
- package/dist/src/commands/schedule.d.ts +3 -0
- package/dist/src/commands/schedule.d.ts.map +1 -0
- package/dist/src/commands/schedule.js +27 -0
- package/dist/src/commands/schedule.js.map +1 -0
- package/dist/src/commands/team.d.ts.map +1 -1
- package/dist/src/commands/team.js +55 -4
- package/dist/src/commands/team.js.map +1 -1
- package/dist/src/constants.d.ts +5 -5
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +20 -8
- package/dist/src/constants.js.map +1 -1
- package/dist/src/credential-manager.d.ts +9 -0
- package/dist/src/credential-manager.d.ts.map +1 -1
- package/dist/src/credential-manager.js +24 -0
- package/dist/src/credential-manager.js.map +1 -1
- package/dist/src/formatters.d.ts +11 -3
- package/dist/src/formatters.d.ts.map +1 -1
- package/dist/src/formatters.js +281 -52
- package/dist/src/formatters.js.map +1 -1
- package/dist/src/http.d.ts +8 -0
- package/dist/src/http.d.ts.map +1 -1
- package/dist/src/http.js +29 -1
- package/dist/src/http.js.map +1 -1
- package/dist/src/index.d.ts +4 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/shared/utils/swmaestro.d.ts +34 -1
- package/dist/src/shared/utils/swmaestro.d.ts.map +1 -1
- package/dist/src/shared/utils/swmaestro.js +102 -39
- package/dist/src/shared/utils/swmaestro.js.map +1 -1
- package/dist/src/shared/utils/team-action-params.d.ts +3 -0
- package/dist/src/shared/utils/team-action-params.d.ts.map +1 -0
- package/dist/src/shared/utils/team-action-params.js +10 -0
- package/dist/src/shared/utils/team-action-params.js.map +1 -0
- package/dist/src/shared/utils/team-params.d.ts +12 -0
- package/dist/src/shared/utils/team-params.d.ts.map +1 -0
- package/dist/src/shared/utils/team-params.js +38 -0
- package/dist/src/shared/utils/team-params.js.map +1 -0
- package/dist/src/types.d.ts +147 -10
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js +74 -6
- package/dist/src/types.js.map +1 -1
- package/package.json +5 -1
- package/src/agent-browser-launcher.test.ts +263 -0
- package/src/agent-browser-launcher.ts +159 -0
- package/src/cli.ts +4 -2
- package/src/client.test.ts +673 -30
- package/src/client.ts +277 -67
- package/src/commands/agent-browser.ts +33 -0
- package/src/commands/auth.test.ts +77 -26
- package/src/commands/auth.ts +5 -3
- package/src/commands/dashboard.test.ts +57 -0
- package/src/commands/dashboard.ts +22 -19
- package/src/commands/helpers.test.ts +72 -25
- package/src/commands/helpers.ts +3 -3
- package/src/commands/index.ts +2 -1
- package/src/commands/mentoring.ts +60 -29
- package/src/commands/notice.ts +2 -1
- package/src/commands/report.ts +4 -2
- package/src/commands/room.ts +160 -1
- package/src/commands/schedule.ts +32 -0
- package/src/commands/team.ts +73 -5
- package/src/constants.ts +20 -8
- package/src/credential-manager.test.ts +44 -0
- package/src/credential-manager.ts +27 -0
- package/src/formatters.test.ts +528 -33
- package/src/formatters.ts +309 -55
- package/src/http.test.ts +71 -2
- package/src/http.ts +41 -2
- package/src/index.ts +10 -1
- package/src/shared/utils/swmaestro.test.ts +245 -9
- package/src/shared/utils/swmaestro.ts +150 -47
- package/src/shared/utils/team-action-params.test.ts +32 -0
- package/src/shared/utils/team-action-params.ts +10 -0
- package/src/shared/utils/team-params.test.ts +141 -0
- package/src/shared/utils/team-params.ts +53 -0
- package/src/types.test.ts +26 -13
- package/src/types.ts +87 -7
- package/dist/src/commands/event.d.ts +0 -3
- package/dist/src/commands/event.d.ts.map +0 -1
- package/dist/src/commands/event.js +0 -58
- package/dist/src/commands/event.js.map +0 -1
- package/src/commands/event.ts +0 -73
package/src/formatters.test.ts
CHANGED
|
@@ -5,9 +5,10 @@ import {
|
|
|
5
5
|
parseCsrfToken,
|
|
6
6
|
parseDashboard,
|
|
7
7
|
parseApplicationHistory,
|
|
8
|
-
|
|
8
|
+
parseScheduleList,
|
|
9
9
|
parseMemberInfo,
|
|
10
10
|
parseMentoringDetail,
|
|
11
|
+
parseMentoringEditForm,
|
|
11
12
|
parseMentoringList,
|
|
12
13
|
parseNoticeDetail,
|
|
13
14
|
parseNoticeList,
|
|
@@ -15,6 +16,8 @@ import {
|
|
|
15
16
|
parseReportDetail,
|
|
16
17
|
parseReportList,
|
|
17
18
|
parseRoomList,
|
|
19
|
+
parseRoomReservationDetail,
|
|
20
|
+
parseRoomReservationList,
|
|
18
21
|
parseRoomSlots,
|
|
19
22
|
parseTeamInfo,
|
|
20
23
|
} from './formatters'
|
|
@@ -205,6 +208,146 @@ describe('formatters', () => {
|
|
|
205
208
|
])
|
|
206
209
|
})
|
|
207
210
|
|
|
211
|
+
it('parses a room reservation detail form into the mutable fields used by update.do', () => {
|
|
212
|
+
const html = `
|
|
213
|
+
<form id="frm" method="post">
|
|
214
|
+
<input type="hidden" name="pageQueryString" value="menuNo=200058&pageIndex=" />
|
|
215
|
+
<input type="hidden" name="rentId" value="18718" />
|
|
216
|
+
<input type="hidden" name="csrfToken" id="csrfToken" value="" />
|
|
217
|
+
<input type="hidden" name="menuNo" id="menuNo" value="200058" />
|
|
218
|
+
<input type="hidden" name="itemId" id="itemId" value="17" />
|
|
219
|
+
<input type="hidden" name="receiptStatCd" id="receiptStatCd" value="RS001" />
|
|
220
|
+
<input type="hidden" name="title" value="멘토링" />
|
|
221
|
+
<input type="hidden" name="rentDt" value="2026-05-31" />
|
|
222
|
+
<input type="hidden" name="rentBgnde" value="2026-05-31 21:00:00.0" />
|
|
223
|
+
<input type="hidden" name="rentEndde" value="2026-05-31 21:30:00.0" />
|
|
224
|
+
<input type="hidden" name="infoCn" value="리뷰 세션" />
|
|
225
|
+
<input type="hidden" name="rentNum" value="4" />
|
|
226
|
+
</form>
|
|
227
|
+
`
|
|
228
|
+
|
|
229
|
+
expect(parseRoomReservationDetail(html)).toEqual({
|
|
230
|
+
rentId: 18718,
|
|
231
|
+
itemId: 17,
|
|
232
|
+
title: '멘토링',
|
|
233
|
+
date: '2026-05-31',
|
|
234
|
+
startTime: '21:00',
|
|
235
|
+
endTime: '21:30',
|
|
236
|
+
attendees: 4,
|
|
237
|
+
notes: '리뷰 세션',
|
|
238
|
+
status: 'confirmed',
|
|
239
|
+
statusCode: 'RS001',
|
|
240
|
+
})
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
it('reports cancelled status and recovers the date from rentBgnde when rentDt is missing', () => {
|
|
244
|
+
const html = `
|
|
245
|
+
<form id="frm" method="post">
|
|
246
|
+
<input type="hidden" name="rentId" value="18715" />
|
|
247
|
+
<input type="hidden" name="itemId" value="17" />
|
|
248
|
+
<input type="hidden" name="receiptStatCd" value="RS002" />
|
|
249
|
+
<input type="hidden" name="title" value="취소된 예약" />
|
|
250
|
+
<input type="hidden" name="rentBgnde" value="2026-06-02 10:00:00.0" />
|
|
251
|
+
<input type="hidden" name="rentEndde" value="2026-06-02 10:30:00.0" />
|
|
252
|
+
<input type="hidden" name="infoCn" value="" />
|
|
253
|
+
<input type="hidden" name="rentNum" value="2" />
|
|
254
|
+
</form>
|
|
255
|
+
`
|
|
256
|
+
|
|
257
|
+
expect(parseRoomReservationDetail(html)).toEqual({
|
|
258
|
+
rentId: 18715,
|
|
259
|
+
itemId: 17,
|
|
260
|
+
title: '취소된 예약',
|
|
261
|
+
date: '2026-06-02',
|
|
262
|
+
startTime: '10:00',
|
|
263
|
+
endTime: '10:30',
|
|
264
|
+
attendees: 2,
|
|
265
|
+
notes: '',
|
|
266
|
+
status: 'cancelled',
|
|
267
|
+
statusCode: 'RS002',
|
|
268
|
+
})
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
it('parses the 7-column room reservation list table with rentId, venue, title, and time range', () => {
|
|
272
|
+
const html = `
|
|
273
|
+
<div class="bbs-list">
|
|
274
|
+
<ul class="bbs-total">
|
|
275
|
+
<li><strong>Total :</strong> 15</li>
|
|
276
|
+
<li><span>1</span>/2 Page</li>
|
|
277
|
+
</ul>
|
|
278
|
+
<table>
|
|
279
|
+
<thead>
|
|
280
|
+
<tr>
|
|
281
|
+
<th>NO.</th><th>회의실 명</th><th>제목</th><th>사용기간</th>
|
|
282
|
+
<th>작성자</th><th>상태</th><th>등록일</th>
|
|
283
|
+
</tr>
|
|
284
|
+
</thead>
|
|
285
|
+
<tbody>
|
|
286
|
+
<tr>
|
|
287
|
+
<td>15</td>
|
|
288
|
+
<td><a href="/sw/mypage/itemRent/view.do?rentId=18618&menuNo=200058">스페이스 M1</a></td>
|
|
289
|
+
<td class="tit">
|
|
290
|
+
<div class="rel">
|
|
291
|
+
<a href="/sw/mypage/itemRent/view.do?rentId=18618&menuNo=200058">멘토 특강</a>
|
|
292
|
+
<span class="ab">예약완료</span>
|
|
293
|
+
</div>
|
|
294
|
+
</td>
|
|
295
|
+
<td>2026.05.31 16:00 ~ 17:30</td>
|
|
296
|
+
<td>전수열</td>
|
|
297
|
+
<td><span class="label-state y">예약완료</span></td>
|
|
298
|
+
<td>2026.04.20</td>
|
|
299
|
+
</tr>
|
|
300
|
+
<tr>
|
|
301
|
+
<td>14</td>
|
|
302
|
+
<td><a href="/sw/mypage/itemRent/view.do?rentId=18616&menuNo=200058">스페이스 A3</a></td>
|
|
303
|
+
<td class="tit">
|
|
304
|
+
<div class="rel">
|
|
305
|
+
<a href="/sw/mypage/itemRent/view.do?rentId=18616&menuNo=200058">자유 멘토링</a>
|
|
306
|
+
<span class="ab">예약취소</span>
|
|
307
|
+
</div>
|
|
308
|
+
</td>
|
|
309
|
+
<td>2026.05.31 13:00 ~ 14:30</td>
|
|
310
|
+
<td>전수열</td>
|
|
311
|
+
<td><span class="label-state n">예약취소</span></td>
|
|
312
|
+
<td>2026.04.20</td>
|
|
313
|
+
</tr>
|
|
314
|
+
</tbody>
|
|
315
|
+
</table>
|
|
316
|
+
</div>
|
|
317
|
+
`
|
|
318
|
+
|
|
319
|
+
expect(parseRoomReservationList(html)).toEqual([
|
|
320
|
+
{
|
|
321
|
+
rentId: 18618,
|
|
322
|
+
venue: '스페이스 M1',
|
|
323
|
+
title: '멘토 특강',
|
|
324
|
+
date: '2026-05-31',
|
|
325
|
+
startTime: '16:00',
|
|
326
|
+
endTime: '17:30',
|
|
327
|
+
author: '전수열',
|
|
328
|
+
status: 'confirmed',
|
|
329
|
+
statusLabel: '예약완료',
|
|
330
|
+
registeredAt: '2026.04.20',
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
rentId: 18616,
|
|
334
|
+
venue: '스페이스 A3',
|
|
335
|
+
title: '자유 멘토링',
|
|
336
|
+
date: '2026-05-31',
|
|
337
|
+
startTime: '13:00',
|
|
338
|
+
endTime: '14:30',
|
|
339
|
+
author: '전수열',
|
|
340
|
+
status: 'cancelled',
|
|
341
|
+
statusLabel: '예약취소',
|
|
342
|
+
registeredAt: '2026.04.20',
|
|
343
|
+
},
|
|
344
|
+
])
|
|
345
|
+
})
|
|
346
|
+
|
|
347
|
+
it('returns an empty reservation list when the 7-column table is missing', () => {
|
|
348
|
+
expect(parseRoomReservationList('<div>nothing here</div>')).toEqual([])
|
|
349
|
+
})
|
|
350
|
+
|
|
208
351
|
it('parses the rentTime fragment for room slot availability and reservation info', () => {
|
|
209
352
|
const html = `
|
|
210
353
|
<span class="ck-st2" data-hour="09" data-minute="00">
|
|
@@ -270,6 +413,7 @@ describe('formatters', () => {
|
|
|
270
413
|
members: '김개발, 이개발',
|
|
271
414
|
mentor: '전수열',
|
|
272
415
|
},
|
|
416
|
+
teams: [],
|
|
273
417
|
mentoringSessions: [
|
|
274
418
|
{
|
|
275
419
|
title: '게임 개발 AI 활용법',
|
|
@@ -336,17 +480,82 @@ describe('formatters', () => {
|
|
|
336
480
|
<ul class="bbs-team">
|
|
337
481
|
<li>
|
|
338
482
|
<div class="top">
|
|
339
|
-
<strong class="t"
|
|
483
|
+
<strong class="t">
|
|
484
|
+
<a href="javascript:void(0);" onclick="teamPageGo('전수열','owner-1','team-a');">김앤강</a>
|
|
485
|
+
</strong>
|
|
486
|
+
<span class="add-txt"></span>
|
|
487
|
+
<ul class="info">
|
|
488
|
+
<li><strong>팀장 : </strong> <span><a class="sui">강동우</a></span></li>
|
|
489
|
+
<li>
|
|
490
|
+
<strong>팀원 : </strong>
|
|
491
|
+
<span><a href="javascript: popuser('uid-a')" class="sui">강경현</a></span>
|
|
492
|
+
<span><a href="javascript: popuser('uid-b')" class="sui">강동우</a></span>
|
|
493
|
+
</li>
|
|
494
|
+
<li><strong>멘토 : </strong></li>
|
|
495
|
+
</ul>
|
|
496
|
+
</div>
|
|
497
|
+
<div class="bot">
|
|
498
|
+
<ul class="ict">
|
|
499
|
+
<li>ICT기술분류(대) : <span>방송·콘텐츠</span></li>
|
|
500
|
+
<li>ICT기술분류(중) : <span>콘텐츠</span></li>
|
|
501
|
+
</ul>
|
|
502
|
+
<div class="team-com"></div>
|
|
503
|
+
<div class="btn_w">
|
|
504
|
+
<button type="button" class="btn-team bg-light inTeam" value="team-a">참여</button>
|
|
505
|
+
</div>
|
|
506
|
+
</div>
|
|
507
|
+
</li>
|
|
508
|
+
<li>
|
|
509
|
+
<div class="top">
|
|
510
|
+
<strong class="t">
|
|
511
|
+
<a href="javascript:void(0);" onclick="teamPageGo('전수열','owner-2','team-b');">오픈소마</a>
|
|
512
|
+
</strong>
|
|
513
|
+
<span class="add-txt">Previzion</span>
|
|
514
|
+
<ul class="info">
|
|
515
|
+
<li><strong>팀장 : </strong> <span><a class="sui">전수열</a></span></li>
|
|
516
|
+
<li>
|
|
517
|
+
<strong>팀원 : </strong>
|
|
518
|
+
<span><a href="javascript: popuser('uid-1')" class="sui">전수열</a></span>
|
|
519
|
+
</li>
|
|
520
|
+
<li>
|
|
521
|
+
<strong>멘토 : </strong>
|
|
522
|
+
<span><a href="javascript: popuser('uid-m1')" class="sui">문승현</a></span>
|
|
523
|
+
</li>
|
|
524
|
+
</ul>
|
|
525
|
+
</div>
|
|
526
|
+
<div class="bot">
|
|
527
|
+
<ul class="ict">
|
|
528
|
+
<li>ICT기술분류(대) : <span>SW·SI</span></li>
|
|
529
|
+
<li>ICT기술분류(중) : <span>응용SW</span></li>
|
|
530
|
+
</ul>
|
|
531
|
+
<div class="team-com">
|
|
532
|
+
<span class="t2">멘토 구성 완료</span>
|
|
533
|
+
<span class="t1">팀 구성 완료</span>
|
|
534
|
+
</div>
|
|
535
|
+
<div class="btn_w">
|
|
536
|
+
<a href="javascript:void(0);" class="btn-team bg-black">완료</a>
|
|
537
|
+
</div>
|
|
340
538
|
</div>
|
|
341
|
-
<p>팀원 3명</p>
|
|
342
|
-
<button type="button">참여중</button>
|
|
343
539
|
</li>
|
|
344
540
|
<li>
|
|
345
541
|
<div class="top">
|
|
346
|
-
<strong class="t"
|
|
542
|
+
<strong class="t">
|
|
543
|
+
<a href="javascript:void(0);" onclick="teamPageGo('전수열','owner-3','team-c');">LAUNS</a>
|
|
544
|
+
</strong>
|
|
545
|
+
<span class="add-txt"></span>
|
|
546
|
+
<ul class="info">
|
|
547
|
+
<li><strong>팀장 : </strong> <span><a class="sui">홍길동</a></span></li>
|
|
548
|
+
<li><strong>팀원 : </strong></li>
|
|
549
|
+
<li><strong>멘토 : </strong></li>
|
|
550
|
+
</ul>
|
|
551
|
+
</div>
|
|
552
|
+
<div class="bot">
|
|
553
|
+
<ul class="ict"></ul>
|
|
554
|
+
<div class="team-com"></div>
|
|
555
|
+
<div class="btn_w">
|
|
556
|
+
<button type="button" class="btn-team bg-blue outTeam" value="team-c">탈퇴</button>
|
|
557
|
+
</div>
|
|
347
558
|
</div>
|
|
348
|
-
<p>팀원 5명</p>
|
|
349
|
-
<button type="button">참여하기</button>
|
|
350
559
|
</li>
|
|
351
560
|
</ul>
|
|
352
561
|
<p class="ico-team">현재 참여중인 방은 <strong class="color-blue">1</strong>/100팀 입니다</p>
|
|
@@ -354,14 +563,91 @@ describe('formatters', () => {
|
|
|
354
563
|
|
|
355
564
|
expect(parseTeamInfo(html)).toEqual({
|
|
356
565
|
teams: [
|
|
357
|
-
{
|
|
358
|
-
|
|
566
|
+
{
|
|
567
|
+
name: '김앤강',
|
|
568
|
+
projectName: '',
|
|
569
|
+
ownerId: 'owner-1',
|
|
570
|
+
teamId: 'team-a',
|
|
571
|
+
leader: '강동우',
|
|
572
|
+
members: [
|
|
573
|
+
{ name: '강경현', userId: 'uid-a' },
|
|
574
|
+
{ name: '강동우', userId: 'uid-b' },
|
|
575
|
+
],
|
|
576
|
+
mentors: [],
|
|
577
|
+
ictCategoryMajor: '방송·콘텐츠',
|
|
578
|
+
ictCategoryMinor: '콘텐츠',
|
|
579
|
+
teamCompleted: false,
|
|
580
|
+
mentorCompleted: false,
|
|
581
|
+
joinStatus: '참여',
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
name: '오픈소마',
|
|
585
|
+
projectName: 'Previzion',
|
|
586
|
+
ownerId: 'owner-2',
|
|
587
|
+
teamId: 'team-b',
|
|
588
|
+
leader: '전수열',
|
|
589
|
+
members: [{ name: '전수열', userId: 'uid-1' }],
|
|
590
|
+
mentors: [{ name: '문승현', userId: 'uid-m1' }],
|
|
591
|
+
ictCategoryMajor: 'SW·SI',
|
|
592
|
+
ictCategoryMinor: '응용SW',
|
|
593
|
+
teamCompleted: true,
|
|
594
|
+
mentorCompleted: true,
|
|
595
|
+
joinStatus: '완료',
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
name: 'LAUNS',
|
|
599
|
+
projectName: '',
|
|
600
|
+
ownerId: 'owner-3',
|
|
601
|
+
teamId: 'team-c',
|
|
602
|
+
leader: '홍길동',
|
|
603
|
+
members: [],
|
|
604
|
+
mentors: [],
|
|
605
|
+
ictCategoryMajor: '',
|
|
606
|
+
ictCategoryMinor: '',
|
|
607
|
+
teamCompleted: false,
|
|
608
|
+
mentorCompleted: false,
|
|
609
|
+
joinStatus: '탈퇴',
|
|
610
|
+
},
|
|
359
611
|
],
|
|
360
612
|
currentTeams: 1,
|
|
361
613
|
maxTeams: 100,
|
|
362
614
|
})
|
|
363
615
|
})
|
|
364
616
|
|
|
617
|
+
it('parses native trainee team summary when no max team count is rendered', () => {
|
|
618
|
+
const html = `
|
|
619
|
+
<ul class="bbs-team">
|
|
620
|
+
<li>
|
|
621
|
+
<div class="top">
|
|
622
|
+
<strong class="t">
|
|
623
|
+
<a href="javascript:void(0);" onclick="teamPageGo('Member A','owner-1','team-alpha');">Team Alpha</a>
|
|
624
|
+
</strong>
|
|
625
|
+
<span class="add-txt"></span>
|
|
626
|
+
<ul class="info">
|
|
627
|
+
<li><strong>팀장 : </strong> <span><a class="sui">Member A</a></span></li>
|
|
628
|
+
<li><strong>팀원 : </strong><span><a href="javascript: popuser('member-a')">Member A</a></span></li>
|
|
629
|
+
<li><strong>멘토 : </strong><span><a href="javascript: popuser('mentor-one')">Mentor One</a></span></li>
|
|
630
|
+
</ul>
|
|
631
|
+
</div>
|
|
632
|
+
<div class="bot">
|
|
633
|
+
<ul class="ict"></ul>
|
|
634
|
+
<div class="team-com"></div>
|
|
635
|
+
<div class="btn_w">
|
|
636
|
+
<button type="button" class="btn-team bg-blue outTeam" value="team-alpha">탈퇴</button>
|
|
637
|
+
</div>
|
|
638
|
+
</div>
|
|
639
|
+
</li>
|
|
640
|
+
</ul>
|
|
641
|
+
<p class="ico-team">현재 참여중인 방은 <strong class="color-blue">1</strong>팀 입니다</p>
|
|
642
|
+
`
|
|
643
|
+
|
|
644
|
+
const result = parseTeamInfo(html)
|
|
645
|
+
|
|
646
|
+
expect(result.currentTeams).toBe(1)
|
|
647
|
+
expect(result.maxTeams).toBe(0)
|
|
648
|
+
expect(result.teams.map((team) => team.name)).toEqual(['Team Alpha'])
|
|
649
|
+
})
|
|
650
|
+
|
|
365
651
|
it('parses member info from <dl> definition pairs', () => {
|
|
366
652
|
const html = `
|
|
367
653
|
<dl><dt><span class="point">아이디</span></dt><dd>devxoul@gmail.com</dd></dl>
|
|
@@ -384,27 +670,61 @@ describe('formatters', () => {
|
|
|
384
670
|
})
|
|
385
671
|
})
|
|
386
672
|
|
|
387
|
-
it('parses the
|
|
673
|
+
it('parses the 3-column monthly schedule table and synthesizes pagination', () => {
|
|
388
674
|
const html = `
|
|
389
675
|
<table>
|
|
390
|
-
<thead><tr><th>NO.</th><th
|
|
676
|
+
<thead><tr><th>NO.</th><th>팀명</th><th>팀장</th><th>팀원</th><th>멘토명</th><th>프로젝트 명</th><th>ICT기술분류(대)</th><th>ICT기술분류(중)</th></tr></thead>
|
|
391
677
|
<tbody>
|
|
392
|
-
<tr><td>
|
|
678
|
+
<tr><td>1</td><td>팀78</td><td>배준서</td><td>이유제, 이중곤</td><td>-</td><td>-</td><td>-</td><td>-</td></tr>
|
|
679
|
+
</tbody>
|
|
680
|
+
</table>
|
|
681
|
+
<table>
|
|
682
|
+
<thead><tr><th>날짜</th><th>구분</th><th>제목</th></tr></thead>
|
|
683
|
+
<tbody>
|
|
684
|
+
<tr><td>2026-04-21~2026-04-26</td><td>교육</td><td>[교육] 디자인씽킹 교육</td></tr>
|
|
393
685
|
</tbody>
|
|
394
686
|
</table>
|
|
395
687
|
`
|
|
396
688
|
|
|
397
|
-
expect(
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
},
|
|
407
|
-
|
|
689
|
+
expect(parseScheduleList(html)).toEqual({
|
|
690
|
+
items: [
|
|
691
|
+
{
|
|
692
|
+
id: 1,
|
|
693
|
+
category: '교육',
|
|
694
|
+
title: '[교육] 디자인씽킹 교육',
|
|
695
|
+
period: { start: '2026-04-21', end: '2026-04-26' },
|
|
696
|
+
},
|
|
697
|
+
],
|
|
698
|
+
pagination: { total: 1, currentPage: 1, totalPages: 1 },
|
|
699
|
+
})
|
|
700
|
+
})
|
|
701
|
+
|
|
702
|
+
it('returns empty items when the monthly schedule table is missing', () => {
|
|
703
|
+
const html = `
|
|
704
|
+
<table>
|
|
705
|
+
<thead><tr><th>NO.</th><th>팀명</th></tr></thead>
|
|
706
|
+
<tbody><tr><td>1</td><td>팀78</td></tr></tbody>
|
|
707
|
+
</table>
|
|
708
|
+
`
|
|
709
|
+
|
|
710
|
+
expect(parseScheduleList(html)).toEqual({
|
|
711
|
+
items: [],
|
|
712
|
+
pagination: { total: 0, currentPage: 1, totalPages: 1 },
|
|
713
|
+
})
|
|
714
|
+
})
|
|
715
|
+
|
|
716
|
+
it('honors the parsed pagination block when SWMaestro provides one', () => {
|
|
717
|
+
const html = `
|
|
718
|
+
<table>
|
|
719
|
+
<thead><tr><th>날짜</th><th>구분</th><th>제목</th></tr></thead>
|
|
720
|
+
<tbody>
|
|
721
|
+
<tr><td>2026-04-21~2026-04-26</td><td>교육</td><td>강의</td></tr>
|
|
722
|
+
</tbody>
|
|
723
|
+
</table>
|
|
724
|
+
<ul class="bbs-total"><li>Total : 9</li><li>2/3 Page</li></ul>
|
|
725
|
+
`
|
|
726
|
+
|
|
727
|
+
expect(parseScheduleList(html).pagination).toEqual({ total: 9, currentPage: 2, totalPages: 3 })
|
|
408
728
|
})
|
|
409
729
|
|
|
410
730
|
it('parses the 10-column mentoring application history table', () => {
|
|
@@ -435,6 +755,7 @@ describe('formatters', () => {
|
|
|
435
755
|
id: 1,
|
|
436
756
|
category: '멘토 특강',
|
|
437
757
|
title: '웹 성능 특강',
|
|
758
|
+
url: '/sw/mypage/mentoLec/view.do?qustnrSn=9572',
|
|
438
759
|
author: '전수열',
|
|
439
760
|
sessionDate: '2026-04-11',
|
|
440
761
|
appliedAt: '2026.04.02',
|
|
@@ -457,12 +778,35 @@ describe('formatters', () => {
|
|
|
457
778
|
expect(parsePagination(html)).toEqual({ total: 11, currentPage: 1, totalPages: 2 })
|
|
458
779
|
})
|
|
459
780
|
|
|
781
|
+
describe('parsePagination with itemCount', () => {
|
|
782
|
+
it('overrides bogus totalPages when all items fit on the first page', () => {
|
|
783
|
+
// SWMaestro returns "1/50 Page" even for 7 items on report/mentoring lists.
|
|
784
|
+
const html = `<ul class="bbs-total"><li>Total : 7</li><li>1/50 Page</li></ul>`
|
|
785
|
+
expect(parsePagination(html, { itemCount: 7 })).toEqual({ total: 7, currentPage: 1, totalPages: 1 })
|
|
786
|
+
})
|
|
787
|
+
|
|
788
|
+
it('honors parsed totalPages when there are genuinely more pages', () => {
|
|
789
|
+
const html = `<ul class="bbs-total"><li>Total : 12</li><li>1/2 Page</li></ul>`
|
|
790
|
+
expect(parsePagination(html, { itemCount: 10 })).toEqual({ total: 12, currentPage: 1, totalPages: 2 })
|
|
791
|
+
})
|
|
792
|
+
|
|
793
|
+
it('returns single page when total is zero', () => {
|
|
794
|
+
const html = `<ul class="bbs-total"><li>Total : 0</li><li>1/1 Page</li></ul>`
|
|
795
|
+
expect(parsePagination(html, { itemCount: 0 })).toEqual({ total: 0, currentPage: 1, totalPages: 1 })
|
|
796
|
+
})
|
|
797
|
+
|
|
798
|
+
it('falls back to parsed totalPages when itemCount is omitted', () => {
|
|
799
|
+
const html = `<ul class="bbs-total"><li>Total : 7</li><li>1/50 Page</li></ul>`
|
|
800
|
+
expect(parsePagination(html)).toEqual({ total: 7, currentPage: 1, totalPages: 50 })
|
|
801
|
+
})
|
|
802
|
+
})
|
|
803
|
+
|
|
460
804
|
it('extracts the CSRF token from a hidden input field', () => {
|
|
461
805
|
expect(parseCsrfToken('<form><input type="hidden" name="csrfToken" value="csrf-123"></form>')).toBe('csrf-123')
|
|
462
806
|
})
|
|
463
807
|
|
|
464
808
|
describe('parseReportList', () => {
|
|
465
|
-
it('parses every field of the report list table', () => {
|
|
809
|
+
it('parses every field of the report list table, picking the title from div.rel so it matches the detail view', () => {
|
|
466
810
|
const html = `
|
|
467
811
|
<ul class="bbs-total"><li><strong class="color-blue">Total :</strong> 2</li><li><span class="color-blue">1</span>/1 Page</li></ul>
|
|
468
812
|
<table class=" t">
|
|
@@ -475,9 +819,18 @@ describe('formatters', () => {
|
|
|
475
819
|
</thead>
|
|
476
820
|
<tbody>
|
|
477
821
|
<tr>
|
|
478
|
-
<td>2</td>
|
|
479
|
-
<td>자유 멘토링</td>
|
|
480
|
-
<td
|
|
822
|
+
<td class="pc_only"><a href="/sw/mypage/mentoringReport/view.do?menuNo=200049&reportId=12345">2</a></td>
|
|
823
|
+
<td class="pc_only"><a href="/sw/mypage/mentoringReport/view.do?menuNo=200049&reportId=12345">자유 멘토링</a></td>
|
|
824
|
+
<td class="tit">
|
|
825
|
+
<div class="date_m block-t clearfix">
|
|
826
|
+
<span class="l"><a href="/sw/mypage/mentoringReport/view.do?menuNo=200049&reportId=12345">자유 멘토링</a></span>
|
|
827
|
+
<span class="r">2026-04-10</span>
|
|
828
|
+
</div>
|
|
829
|
+
<div class="rel">
|
|
830
|
+
<a href="/sw/mypage/mentoringReport/view.do?menuNo=200049&reportId=12345">[자유 멘토링] 2026년 04월 10일 멘토링 보고</a>
|
|
831
|
+
<div class="ab color-blue block-t"><strong class="label-state ing">승인</strong></div>
|
|
832
|
+
</div>
|
|
833
|
+
</td>
|
|
481
834
|
<td>2026-04-10</td>
|
|
482
835
|
<td>[승인]</td>
|
|
483
836
|
<td class="pc_only">전수열</td>
|
|
@@ -486,9 +839,18 @@ describe('formatters', () => {
|
|
|
486
839
|
<td>200,000</td>
|
|
487
840
|
</tr>
|
|
488
841
|
<tr>
|
|
489
|
-
<td>1</td>
|
|
490
|
-
<td>멘토 특강</td>
|
|
491
|
-
<td
|
|
842
|
+
<td class="pc_only"><a href="/sw/mypage/mentoringReport/view.do?menuNo=200049&reportId=67890">1</a></td>
|
|
843
|
+
<td class="pc_only"><a href="/sw/mypage/mentoringReport/view.do?menuNo=200049&reportId=67890">멘토 특강</a></td>
|
|
844
|
+
<td class="tit">
|
|
845
|
+
<div class="date_m block-t clearfix">
|
|
846
|
+
<span class="l"><a href="/sw/mypage/mentoringReport/view.do?menuNo=200049&reportId=67890">멘토 특강</a></span>
|
|
847
|
+
<span class="r">2026-03-15</span>
|
|
848
|
+
</div>
|
|
849
|
+
<div class="rel">
|
|
850
|
+
<a href="/sw/mypage/mentoringReport/view.do?menuNo=200049&reportId=67890">[멘토 특강] 2026년 03월 15일 멘토링 보고</a>
|
|
851
|
+
<div class="ab color-blue block-t"><strong class="label-state ing">접수</strong></div>
|
|
852
|
+
</div>
|
|
853
|
+
</td>
|
|
492
854
|
<td>2026-03-15</td>
|
|
493
855
|
<td>[접수]</td>
|
|
494
856
|
<td class="pc_only">전수열</td>
|
|
@@ -506,7 +868,7 @@ describe('formatters', () => {
|
|
|
506
868
|
expect(result[0]).toEqual({
|
|
507
869
|
id: 12345,
|
|
508
870
|
category: '자유 멘토링',
|
|
509
|
-
title: '2026년 04월 10일 멘토링 보고',
|
|
871
|
+
title: '[자유 멘토링] 2026년 04월 10일 멘토링 보고',
|
|
510
872
|
progressDate: '2026-04-10',
|
|
511
873
|
status: '[승인]',
|
|
512
874
|
author: '전수열',
|
|
@@ -517,7 +879,7 @@ describe('formatters', () => {
|
|
|
517
879
|
expect(result[1]).toEqual({
|
|
518
880
|
id: 67890,
|
|
519
881
|
category: '멘토 특강',
|
|
520
|
-
title: '2026년 03월 15일 멘토링 보고',
|
|
882
|
+
title: '[멘토 특강] 2026년 03월 15일 멘토링 보고',
|
|
521
883
|
progressDate: '2026-03-15',
|
|
522
884
|
status: '[접수]',
|
|
523
885
|
author: '전수열',
|
|
@@ -526,7 +888,6 @@ describe('formatters', () => {
|
|
|
526
888
|
payAmount: '150,000',
|
|
527
889
|
})
|
|
528
890
|
|
|
529
|
-
// Validate against schema
|
|
530
891
|
result.forEach((item) => {
|
|
531
892
|
ReportListItemSchema.parse(item)
|
|
532
893
|
})
|
|
@@ -671,3 +1032,137 @@ describe('formatters', () => {
|
|
|
671
1032
|
})
|
|
672
1033
|
})
|
|
673
1034
|
})
|
|
1035
|
+
|
|
1036
|
+
describe('parseMentoringEditForm', () => {
|
|
1037
|
+
const untilLectureHtml = `
|
|
1038
|
+
<form id="board">
|
|
1039
|
+
<input type="hidden" name="qustnrSn" value="9572" />
|
|
1040
|
+
<input type="radio" name="reportCd" value="MRC010" />
|
|
1041
|
+
<input type="radio" name="reportCd" value="MRC020" checked />
|
|
1042
|
+
<input type="text" name="qustnrSj" value="웹 성능 특강" />
|
|
1043
|
+
<input type="radio" name="receiptType" value="UNTIL_LECTURE" checked />
|
|
1044
|
+
<input type="radio" name="receiptType" value="DIRECT" />
|
|
1045
|
+
<input type="text" name="bgndeDate" value="2026-04-01" />
|
|
1046
|
+
<select name="bgndeTime"><option value="00:00" selected>00시</option></select>
|
|
1047
|
+
<input type="text" name="enddeDate" value="2026-04-11" />
|
|
1048
|
+
<select name="enddeTime"><option value="14:00" selected>14시</option></select>
|
|
1049
|
+
<input type="text" name="eventDt" value="2026-04-11" />
|
|
1050
|
+
<select name="eventStime"><option value="14:00" selected>14시</option></select>
|
|
1051
|
+
<select name="eventEtime"><option value="15:30" selected>15시30분</option></select>
|
|
1052
|
+
<input type="text" name="applyCnt" value="20" />
|
|
1053
|
+
<select name="place"><option value="온라인(Webex)" selected>온라인(Webex)</option></select>
|
|
1054
|
+
</form>
|
|
1055
|
+
`
|
|
1056
|
+
|
|
1057
|
+
const directHtml = `
|
|
1058
|
+
<form id="board">
|
|
1059
|
+
<input type="hidden" name="qustnrSn" value="10551" />
|
|
1060
|
+
<input type="radio" name="reportCd" value="MRC010" />
|
|
1061
|
+
<input type="radio" name="reportCd" value="MRC020" checked />
|
|
1062
|
+
<input type="text" name="qustnrSj" value="리뷰 검증" />
|
|
1063
|
+
<input type="radio" name="receiptType" value="UNTIL_LECTURE" />
|
|
1064
|
+
<input type="radio" name="receiptType" value="DIRECT" checked />
|
|
1065
|
+
<input type="text" name="bgndeDate" value="2026-11-01" />
|
|
1066
|
+
<select name="bgndeTime"><option value="09:00" selected>09시</option></select>
|
|
1067
|
+
<input type="text" name="enddeDate" value="2026-12-20" />
|
|
1068
|
+
<select name="enddeTime"><option value="18:00" selected>18시</option></select>
|
|
1069
|
+
<input type="text" name="eventDt" value="2026-12-30" />
|
|
1070
|
+
<select name="eventStime"><option value="19:00" selected>19시</option></select>
|
|
1071
|
+
<select name="eventEtime"><option value="20:00" selected>20시</option></select>
|
|
1072
|
+
<input type="text" name="applyCnt" value="10" />
|
|
1073
|
+
<select name="place"><option value="온라인(Webex)" selected>온라인(Webex)</option></select>
|
|
1074
|
+
</form>
|
|
1075
|
+
`
|
|
1076
|
+
|
|
1077
|
+
it('extracts the UNTIL_LECTURE receipt window and session schedule verbatim from the form', () => {
|
|
1078
|
+
const form = parseMentoringEditForm(untilLectureHtml)
|
|
1079
|
+
|
|
1080
|
+
expect(form).toMatchObject({
|
|
1081
|
+
id: 9572,
|
|
1082
|
+
title: '웹 성능 특강',
|
|
1083
|
+
reportCd: 'MRC020',
|
|
1084
|
+
receiptType: 'UNTIL_LECTURE',
|
|
1085
|
+
bgndeDate: '2026-04-01',
|
|
1086
|
+
bgndeTime: '00:00',
|
|
1087
|
+
enddeDate: '2026-04-11',
|
|
1088
|
+
enddeTime: '14:00',
|
|
1089
|
+
eventDt: '2026-04-11',
|
|
1090
|
+
eventStime: '14:00',
|
|
1091
|
+
eventEtime: '15:30',
|
|
1092
|
+
applyCnt: 20,
|
|
1093
|
+
place: '온라인(Webex)',
|
|
1094
|
+
})
|
|
1095
|
+
})
|
|
1096
|
+
|
|
1097
|
+
it('preserves the DIRECT receipt window so partial updates do not silently switch to UNTIL_LECTURE defaults', () => {
|
|
1098
|
+
const form = parseMentoringEditForm(directHtml)
|
|
1099
|
+
|
|
1100
|
+
expect(form.receiptType).toBe('DIRECT')
|
|
1101
|
+
expect(form.bgndeDate).toBe('2026-11-01')
|
|
1102
|
+
expect(form.bgndeTime).toBe('09:00')
|
|
1103
|
+
expect(form.enddeDate).toBe('2026-12-20')
|
|
1104
|
+
expect(form.enddeTime).toBe('18:00')
|
|
1105
|
+
})
|
|
1106
|
+
|
|
1107
|
+
it('falls back to the INITIAL_DATA JS block when the server omits option[selected] on time selects', () => {
|
|
1108
|
+
const html = `
|
|
1109
|
+
<form id="board">
|
|
1110
|
+
<input type="hidden" name="qustnrSn" value="10552" />
|
|
1111
|
+
<input type="radio" name="reportCd" value="MRC020" checked />
|
|
1112
|
+
<input type="text" name="qustnrSj" value="리뷰 검증" />
|
|
1113
|
+
<input type="text" name="bgndeDate" value="2026-11-01" />
|
|
1114
|
+
<select id="bgndeTime" name="bgndeTime"></select>
|
|
1115
|
+
<input type="text" name="enddeDate" value="2026-12-20" />
|
|
1116
|
+
<select id="enddeTime" name="enddeTime"></select>
|
|
1117
|
+
<input type="text" name="eventDt" value="2026-12-30" />
|
|
1118
|
+
<select id="eventStime" name="eventStime"></select>
|
|
1119
|
+
<select id="eventEtime" name="eventEtime"></select>
|
|
1120
|
+
<input type="text" name="applyCnt" value="10" />
|
|
1121
|
+
<select name="place"><option value="온라인(Webex)" selected>온라인(Webex)</option></select>
|
|
1122
|
+
</form>
|
|
1123
|
+
<script>
|
|
1124
|
+
var INITIAL_DATA = {
|
|
1125
|
+
bgndeDate: '2026-11-01',
|
|
1126
|
+
bgndeTime: '09:00',
|
|
1127
|
+
enddeDate: '2026-12-20',
|
|
1128
|
+
enddeTime: '18:00',
|
|
1129
|
+
eventDt: '2026-12-30',
|
|
1130
|
+
eventStime: '19:00',
|
|
1131
|
+
eventEtime: '20:00',
|
|
1132
|
+
receiptType: 'DIRECT'
|
|
1133
|
+
};
|
|
1134
|
+
</script>
|
|
1135
|
+
`
|
|
1136
|
+
|
|
1137
|
+
const form = parseMentoringEditForm(html)
|
|
1138
|
+
|
|
1139
|
+
expect(form).toMatchObject({
|
|
1140
|
+
receiptType: 'DIRECT',
|
|
1141
|
+
bgndeDate: '2026-11-01',
|
|
1142
|
+
bgndeTime: '09:00',
|
|
1143
|
+
enddeDate: '2026-12-20',
|
|
1144
|
+
enddeTime: '18:00',
|
|
1145
|
+
eventStime: '19:00',
|
|
1146
|
+
eventEtime: '20:00',
|
|
1147
|
+
})
|
|
1148
|
+
})
|
|
1149
|
+
|
|
1150
|
+
it('uses the provided id argument when the form has no qustnrSn hidden input', () => {
|
|
1151
|
+
const html = `<form id="board">
|
|
1152
|
+
<input type="radio" name="reportCd" value="MRC010" checked />
|
|
1153
|
+
<input type="radio" name="receiptType" value="UNTIL_LECTURE" checked />
|
|
1154
|
+
<input type="text" name="qustnrSj" value="" />
|
|
1155
|
+
<input type="text" name="bgndeDate" value="2026-04-01" />
|
|
1156
|
+
<select name="bgndeTime"><option value="00:00" selected>00시</option></select>
|
|
1157
|
+
<input type="text" name="enddeDate" value="2026-04-01" />
|
|
1158
|
+
<select name="enddeTime"><option value="10:00" selected>10시</option></select>
|
|
1159
|
+
<input type="text" name="eventDt" value="2026-04-01" />
|
|
1160
|
+
<select name="eventStime"><option value="10:00" selected>10시</option></select>
|
|
1161
|
+
<select name="eventEtime"><option value="11:00" selected>11시</option></select>
|
|
1162
|
+
<input type="text" name="applyCnt" value="3" />
|
|
1163
|
+
<select name="place"><option value="온라인(Webex)" selected>온라인(Webex)</option></select>
|
|
1164
|
+
</form>`
|
|
1165
|
+
|
|
1166
|
+
expect(parseMentoringEditForm(html, 77).id).toBe(77)
|
|
1167
|
+
})
|
|
1168
|
+
})
|