opensoma 0.5.0 → 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 +36 -7
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/client.js +231 -63
- 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 -43
- 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 +801 -140
- package/src/client.ts +293 -79
- package/src/commands/agent-browser.ts +33 -0
- package/src/commands/auth.test.ts +83 -32
- 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 +79 -32
- 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.test.ts +7 -7
- 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 +49 -5
- package/src/credential-manager.ts +27 -0
- package/src/formatters.test.ts +548 -53
- package/src/formatters.ts +309 -55
- package/src/http.test.ts +108 -39
- package/src/http.ts +41 -2
- package/src/index.ts +10 -1
- package/src/shared/utils/mentoring-params.test.ts +16 -16
- package/src/shared/utils/swmaestro.test.ts +326 -11
- package/src/shared/utils/swmaestro.ts +150 -52
- 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/shared/utils/toz.test.ts +12 -7
- package/src/token-extractor.test.ts +12 -12
- package/src/toz-http.test.ts +11 -11
- package/src/types.test.ts +235 -206
- 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
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, it } from 'bun:test'
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
parseApprovalList,
|
|
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,13 +16,15 @@ import {
|
|
|
15
16
|
parseReportDetail,
|
|
16
17
|
parseReportList,
|
|
17
18
|
parseRoomList,
|
|
19
|
+
parseRoomReservationDetail,
|
|
20
|
+
parseRoomReservationList,
|
|
18
21
|
parseRoomSlots,
|
|
19
22
|
parseTeamInfo,
|
|
20
23
|
} from './formatters'
|
|
21
24
|
import { ApprovalListItemSchema, ReportDetailSchema, ReportListItemSchema } from './types'
|
|
22
25
|
|
|
23
26
|
describe('formatters', () => {
|
|
24
|
-
|
|
27
|
+
it('parses real mentoring list rows from the list page', () => {
|
|
25
28
|
const html = `
|
|
26
29
|
<table>
|
|
27
30
|
<thead><tr><th>NO.</th><th>제목</th><th>접수기간</th><th>진행날짜</th><th>모집인원</th><th>개설승인</th><th>상태</th><th>작성자</th><th>등록일</th></tr></thead>
|
|
@@ -59,7 +62,7 @@ describe('formatters', () => {
|
|
|
59
62
|
])
|
|
60
63
|
})
|
|
61
64
|
|
|
62
|
-
|
|
65
|
+
it('parses the key-value sections of a real mentoring detail view', () => {
|
|
63
66
|
const html = `
|
|
64
67
|
<input type="hidden" name="reportCd" value="MRC020">
|
|
65
68
|
<div class="top">
|
|
@@ -94,7 +97,7 @@ describe('formatters', () => {
|
|
|
94
97
|
})
|
|
95
98
|
})
|
|
96
99
|
|
|
97
|
-
|
|
100
|
+
it('parses the applicant list table on the mentoring detail view', () => {
|
|
98
101
|
const html = `
|
|
99
102
|
<div class="group"><strong class="t">모집 명</strong><div class="c">[자유 멘토링] 테스트 멘토링</div></div>
|
|
100
103
|
<div class="group"><strong class="t">접수 기간</strong><div class="c">2026.04.01 ~ 2026.04.10</div></div>
|
|
@@ -133,7 +136,7 @@ describe('formatters', () => {
|
|
|
133
136
|
])
|
|
134
137
|
})
|
|
135
138
|
|
|
136
|
-
|
|
139
|
+
it('returns an empty applicant list when no applicant table is present', () => {
|
|
137
140
|
const html = `
|
|
138
141
|
<div class="group"><strong class="t">모집 명</strong><div class="c">[자유 멘토링] 빈 멘토링</div></div>
|
|
139
142
|
<div class="group"><strong class="t">접수 기간</strong><div class="c">2026.04.01 ~ 2026.04.10</div></div>
|
|
@@ -146,7 +149,7 @@ describe('formatters', () => {
|
|
|
146
149
|
expect(parseMentoringDetail(html, 5678).applicants).toEqual([])
|
|
147
150
|
})
|
|
148
151
|
|
|
149
|
-
|
|
152
|
+
it('ignores unrelated 5-column tables embedded in the detail content', () => {
|
|
150
153
|
const html = `
|
|
151
154
|
<div class="group"><strong class="t">모집 명</strong><div class="c">[자유 멘토링] 콘텐츠 테이블 멘토링</div></div>
|
|
152
155
|
<div class="group"><strong class="t">접수 기간</strong><div class="c">2026.04.01 ~ 2026.04.10</div></div>
|
|
@@ -166,7 +169,7 @@ describe('formatters', () => {
|
|
|
166
169
|
expect(parseMentoringDetail(html, 9999).applicants).toEqual([])
|
|
167
170
|
})
|
|
168
171
|
|
|
169
|
-
|
|
172
|
+
it('parses real room cards along with their embedded time slots', () => {
|
|
170
173
|
const html = `
|
|
171
174
|
<ul class="bbs-reserve">
|
|
172
175
|
<li class="item">
|
|
@@ -205,7 +208,147 @@ describe('formatters', () => {
|
|
|
205
208
|
])
|
|
206
209
|
})
|
|
207
210
|
|
|
208
|
-
|
|
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
|
+
|
|
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">
|
|
211
354
|
<input type="checkbox" name="time" id="time1_1" value="1">
|
|
@@ -229,7 +372,7 @@ describe('formatters', () => {
|
|
|
229
372
|
])
|
|
230
373
|
})
|
|
231
374
|
|
|
232
|
-
|
|
375
|
+
it('parses every section of a real dashboard page', () => {
|
|
233
376
|
const html = `
|
|
234
377
|
<ul class="dash-top">
|
|
235
378
|
<li class="dash-card">
|
|
@@ -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 활용법',
|
|
@@ -287,7 +431,7 @@ describe('formatters', () => {
|
|
|
287
431
|
})
|
|
288
432
|
})
|
|
289
433
|
|
|
290
|
-
|
|
434
|
+
it('parses real notice list and detail page structures', () => {
|
|
291
435
|
const listHtml = `
|
|
292
436
|
<table>
|
|
293
437
|
<thead><tr><th>NO.</th><th>제목</th><th>작성자</th><th>등록일</th></tr></thead>
|
|
@@ -331,22 +475,87 @@ describe('formatters', () => {
|
|
|
331
475
|
})
|
|
332
476
|
})
|
|
333
477
|
|
|
334
|
-
|
|
478
|
+
it('parses team cards together with the team count summary', () => {
|
|
335
479
|
const html = `
|
|
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,15 +563,92 @@ 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
|
|
|
365
|
-
|
|
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
|
+
|
|
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>
|
|
368
654
|
<dl><dt><span class="point">이름</span></dt><dd>전수열</dd></dl>
|
|
@@ -384,30 +670,64 @@ describe('formatters', () => {
|
|
|
384
670
|
})
|
|
385
671
|
})
|
|
386
672
|
|
|
387
|
-
|
|
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
|
+
})
|
|
408
714
|
})
|
|
409
715
|
|
|
410
|
-
|
|
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 })
|
|
728
|
+
})
|
|
729
|
+
|
|
730
|
+
it('parses the 10-column mentoring application history table', () => {
|
|
411
731
|
const html = `
|
|
412
732
|
<table>
|
|
413
733
|
<thead>
|
|
@@ -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',
|
|
@@ -446,7 +767,7 @@ describe('formatters', () => {
|
|
|
446
767
|
])
|
|
447
768
|
})
|
|
448
769
|
|
|
449
|
-
|
|
770
|
+
it('parses the bbs-total pagination block', () => {
|
|
450
771
|
const html = `
|
|
451
772
|
<ul class="bbs-total">
|
|
452
773
|
<li>Total : 11</li>
|
|
@@ -457,12 +778,35 @@ describe('formatters', () => {
|
|
|
457
778
|
expect(parsePagination(html)).toEqual({ total: 11, currentPage: 1, totalPages: 2 })
|
|
458
779
|
})
|
|
459
780
|
|
|
460
|
-
|
|
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
|
+
|
|
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
|
-
|
|
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,13 +888,12 @@ 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
|
})
|
|
533
894
|
})
|
|
534
895
|
|
|
535
|
-
|
|
896
|
+
it('returns an empty array when the report list table has no rows', () => {
|
|
536
897
|
const html = `
|
|
537
898
|
<ul class="bbs-total"><li><strong class="color-blue">Total :</strong> 0</li><li><span class="color-blue">1</span>/1 Page</li></ul>
|
|
538
899
|
<table class=" t">
|
|
@@ -552,7 +913,7 @@ describe('formatters', () => {
|
|
|
552
913
|
})
|
|
553
914
|
|
|
554
915
|
describe('parseReportDetail', () => {
|
|
555
|
-
|
|
916
|
+
it('parses every field of the report detail view', () => {
|
|
556
917
|
const html = `
|
|
557
918
|
<div class="board-view">
|
|
558
919
|
<table>
|
|
@@ -590,7 +951,7 @@ describe('formatters', () => {
|
|
|
590
951
|
ReportDetailSchema.parse(result)
|
|
591
952
|
})
|
|
592
953
|
|
|
593
|
-
|
|
954
|
+
it('uses the provided id in the parsed report detail', () => {
|
|
594
955
|
const html = `
|
|
595
956
|
<div class="board-view">
|
|
596
957
|
<table>
|
|
@@ -612,7 +973,7 @@ describe('formatters', () => {
|
|
|
612
973
|
})
|
|
613
974
|
|
|
614
975
|
describe('parseApprovalList', () => {
|
|
615
|
-
|
|
976
|
+
it('parses every column of the 10-column approval list table', () => {
|
|
616
977
|
const html = `
|
|
617
978
|
<ul class="bbs-total"><li><strong class="color-blue">Total :</strong> 1</li><li><span class="color-blue">1</span>/1 Page</li></ul>
|
|
618
979
|
<table class=" t">
|
|
@@ -662,7 +1023,7 @@ describe('formatters', () => {
|
|
|
662
1023
|
})
|
|
663
1024
|
})
|
|
664
1025
|
|
|
665
|
-
|
|
1026
|
+
it('returns an empty array when the approval table shows the nodata row', () => {
|
|
666
1027
|
const html = `
|
|
667
1028
|
<table class=" t"><tbody><tr><td colspan="10" class="nodata">데이터가 없습니다.</td></tr></tbody></table>
|
|
668
1029
|
`
|
|
@@ -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
|
+
})
|