opensoma 0.5.0 → 0.5.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.
@@ -1,44 +1,44 @@
1
- import { describe, expect, test } from 'bun:test'
1
+ import { describe, expect, it } from 'bun:test'
2
2
 
3
3
  import { MENU_NO, REPORT_CD } from '../../constants'
4
4
  import { buildMentoringListParams, parseSearchQuery } from './mentoring-params'
5
5
 
6
6
  describe('parseSearchQuery', () => {
7
- test('plain text defaults to title', () => {
7
+ it('defaults plain text to a title search', () => {
8
8
  expect(parseSearchQuery('OpenCode')).toEqual({ field: 'title', value: 'OpenCode' })
9
9
  })
10
10
 
11
- test('title: prefix', () => {
11
+ it('parses the "title:" prefix as a title search', () => {
12
12
  expect(parseSearchQuery('title:OpenCode')).toEqual({ field: 'title', value: 'OpenCode' })
13
13
  })
14
14
 
15
- test('author: prefix', () => {
15
+ it('parses the "author:" prefix as an author search', () => {
16
16
  expect(parseSearchQuery('author:전수열')).toEqual({ field: 'author', value: '전수열' })
17
17
  })
18
18
 
19
- test('author:@me sets me flag', () => {
19
+ it('sets the "me" flag when parsing "author:@me"', () => {
20
20
  expect(parseSearchQuery('author:@me')).toEqual({ field: 'author', value: '@me', me: true })
21
21
  })
22
22
 
23
- test('content: prefix', () => {
23
+ it('parses the "content:" prefix as a content search', () => {
24
24
  expect(parseSearchQuery('content:하네스')).toEqual({ field: 'content', value: '하네스' })
25
25
  })
26
26
 
27
- test('unknown prefix treated as plain title search', () => {
27
+ it('treats an unknown prefix as a plain title search', () => {
28
28
  expect(parseSearchQuery('foo:bar')).toEqual({ field: 'title', value: 'foo:bar' })
29
29
  })
30
30
 
31
- test('value with colons preserves everything after first colon', () => {
31
+ it('preserves everything after the first colon when the value contains colons', () => {
32
32
  expect(parseSearchQuery('title:foo:bar')).toEqual({ field: 'title', value: 'foo:bar' })
33
33
  })
34
34
  })
35
35
 
36
36
  describe('buildMentoringListParams', () => {
37
- test('no options returns only menuNo', () => {
37
+ it('returns only menuNo when no options are passed', () => {
38
38
  expect(buildMentoringListParams()).toEqual({ menuNo: MENU_NO.MENTORING })
39
39
  })
40
40
 
41
- test('status maps open to A, closed to C', () => {
41
+ it('maps status open to "A" and closed to "C"', () => {
42
42
  expect(buildMentoringListParams({ status: 'open' })).toEqual({
43
43
  menuNo: MENU_NO.MENTORING,
44
44
  searchStatMentolec: 'A',
@@ -49,7 +49,7 @@ describe('buildMentoringListParams', () => {
49
49
  })
50
50
  })
51
51
 
52
- test('type maps public and lecture to report codes', () => {
52
+ it('maps type public and lecture to their report codes', () => {
53
53
  expect(buildMentoringListParams({ type: 'public' })).toEqual({
54
54
  menuNo: MENU_NO.MENTORING,
55
55
  searchGubunMentolec: REPORT_CD.PUBLIC_MENTORING,
@@ -60,7 +60,7 @@ describe('buildMentoringListParams', () => {
60
60
  })
61
61
  })
62
62
 
63
- test('title search sets searchCnd=1', () => {
63
+ it('sets searchCnd=1 for a title search', () => {
64
64
  expect(buildMentoringListParams({ search: { field: 'title', value: 'OpenCode' } })).toEqual({
65
65
  menuNo: MENU_NO.MENTORING,
66
66
  searchCnd: '1',
@@ -68,7 +68,7 @@ describe('buildMentoringListParams', () => {
68
68
  })
69
69
  })
70
70
 
71
- test('author search sets searchCnd=2', () => {
71
+ it('sets searchCnd=2 for an author search', () => {
72
72
  expect(buildMentoringListParams({ search: { field: 'author', value: '전수열' } })).toEqual({
73
73
  menuNo: MENU_NO.MENTORING,
74
74
  searchCnd: '2',
@@ -76,7 +76,7 @@ describe('buildMentoringListParams', () => {
76
76
  })
77
77
  })
78
78
 
79
- test('author:@me with user sets searchId and userNm', () => {
79
+ it('sets searchId and searchWrd from the current user when "author:@me" is used', () => {
80
80
  expect(
81
81
  buildMentoringListParams({
82
82
  search: { field: 'author', value: '@me', me: true },
@@ -90,7 +90,7 @@ describe('buildMentoringListParams', () => {
90
90
  })
91
91
  })
92
92
 
93
- test('search composes with status and type', () => {
93
+ it('composes search with status and type filters', () => {
94
94
  expect(
95
95
  buildMentoringListParams({
96
96
  status: 'open',
@@ -108,7 +108,7 @@ describe('buildMentoringListParams', () => {
108
108
  })
109
109
  })
110
110
 
111
- test('page sets pageIndex', () => {
111
+ it('sets pageIndex from the page option', () => {
112
112
  expect(buildMentoringListParams({ page: 3 })).toEqual({
113
113
  menuNo: MENU_NO.MENTORING,
114
114
  pageIndex: '3',
@@ -1,9 +1,9 @@
1
- import { describe, expect, test } from 'bun:test'
1
+ import { describe, expect, it } from 'bun:test'
2
2
 
3
- import { resolveVenue } from './swmaestro'
3
+ import { buildRoomReservationPayload, resolveVenue } from './swmaestro'
4
4
 
5
5
  describe('resolveVenue', () => {
6
- test('adds 토즈- prefix to bare toz location names', () => {
6
+ it('prepends "토즈-" to bare TOZ location names', () => {
7
7
  expect(resolveVenue('광화문점')).toBe('토즈-광화문점')
8
8
  expect(resolveVenue('양재점')).toBe('토즈-양재점')
9
9
  expect(resolveVenue('강남컨퍼런스센터점')).toBe('토즈-강남컨퍼런스센터점')
@@ -14,17 +14,17 @@ describe('resolveVenue', () => {
14
14
  expect(resolveVenue('홍대점')).toBe('토즈-홍대점')
15
15
  })
16
16
 
17
- test('passes through already-prefixed toz locations', () => {
17
+ it('passes through TOZ locations that already have the prefix', () => {
18
18
  expect(resolveVenue('토즈-광화문점')).toBe('토즈-광화문점')
19
19
  expect(resolveVenue('토즈-강남역토즈타워점')).toBe('토즈-강남역토즈타워점')
20
20
  })
21
21
 
22
- test('resolves 신촌비즈니스센터점 to 연수센터-7', () => {
22
+ it('resolves "신촌비즈니스센터점" to "연수센터-7"', () => {
23
23
  expect(resolveVenue('신촌비즈니스센터점')).toBe('연수센터-7')
24
24
  expect(resolveVenue('토즈-신촌비즈니스센터점')).toBe('연수센터-7')
25
25
  })
26
26
 
27
- test('passes through non-toz venues unchanged', () => {
27
+ it('passes through non-TOZ venues unchanged', () => {
28
28
  expect(resolveVenue('온라인(Webex)')).toBe('온라인(Webex)')
29
29
  expect(resolveVenue('스페이스 A1')).toBe('스페이스 A1')
30
30
  expect(resolveVenue('스페이스 M1')).toBe('스페이스 M1')
@@ -33,12 +33,91 @@ describe('resolveVenue', () => {
33
33
  expect(resolveVenue('(엑스퍼트) 외부_카페')).toBe('(엑스퍼트) 외부_카페')
34
34
  })
35
35
 
36
- test('trims whitespace from input', () => {
36
+ it('trims surrounding whitespace from the input', () => {
37
37
  expect(resolveVenue(' 강남역토즈타워점 ')).toBe('토즈-강남역토즈타워점')
38
38
  expect(resolveVenue(' 스페이스 A1 ')).toBe('스페이스 A1')
39
39
  })
40
40
 
41
- test('passes through unknown venues unchanged', () => {
41
+ it('passes through unknown venues unchanged', () => {
42
42
  expect(resolveVenue('기타 장소')).toBe('기타 장소')
43
43
  })
44
44
  })
45
+
46
+ describe('buildRoomReservationPayload', () => {
47
+ it('sets rentEndde to the last selected slot so the server reserves only the chosen slots', () => {
48
+ const payload = buildRoomReservationPayload({
49
+ roomId: 17,
50
+ date: '2026-04-20',
51
+ slots: ['13:00', '13:30'],
52
+ title: '회의',
53
+ })
54
+
55
+ expect(payload.rentBgnde).toBe('2026-04-20 13:00:00')
56
+ expect(payload.rentEndde).toBe('2026-04-20 13:30:00')
57
+ expect(payload['time[0]']).toBe('13:00')
58
+ expect(payload['time[1]']).toBe('13:30')
59
+ expect(payload['time[2]']).toBeUndefined()
60
+ expect(payload['chkData_1']).toBe('2026-04-20|13:00|17')
61
+ expect(payload['chkData_2']).toBe('2026-04-20|13:30|17')
62
+ expect(payload['chkData_3']).toBeUndefined()
63
+ })
64
+
65
+ it('handles a single-slot reservation', () => {
66
+ const payload = buildRoomReservationPayload({
67
+ roomId: 17,
68
+ date: '2026-04-20',
69
+ slots: ['13:00'],
70
+ title: '회의',
71
+ })
72
+
73
+ expect(payload.rentBgnde).toBe('2026-04-20 13:00:00')
74
+ expect(payload.rentEndde).toBe('2026-04-20 13:00:00')
75
+ expect(payload['time[0]']).toBe('13:00')
76
+ expect(payload['time[1]']).toBeUndefined()
77
+ })
78
+
79
+ it('handles a reservation ending at the last available slot', () => {
80
+ const payload = buildRoomReservationPayload({
81
+ roomId: 17,
82
+ date: '2026-04-20',
83
+ slots: ['23:00', '23:30'],
84
+ title: '회의',
85
+ })
86
+
87
+ expect(payload.rentBgnde).toBe('2026-04-20 23:00:00')
88
+ expect(payload.rentEndde).toBe('2026-04-20 23:30:00')
89
+ })
90
+
91
+ it('rejects non-consecutive slots', () => {
92
+ expect(() =>
93
+ buildRoomReservationPayload({
94
+ roomId: 17,
95
+ date: '2026-04-20',
96
+ slots: ['13:00', '14:00'],
97
+ title: '회의',
98
+ }),
99
+ ).toThrow('Time slots must be consecutive')
100
+ })
101
+
102
+ it('rejects invalid time slots', () => {
103
+ expect(() =>
104
+ buildRoomReservationPayload({
105
+ roomId: 17,
106
+ date: '2026-04-20',
107
+ slots: ['25:00'],
108
+ title: '회의',
109
+ }),
110
+ ).toThrow('Invalid time slot')
111
+ })
112
+
113
+ it('rejects empty slot lists', () => {
114
+ expect(() =>
115
+ buildRoomReservationPayload({
116
+ roomId: 17,
117
+ date: '2026-04-20',
118
+ slots: [],
119
+ title: '회의',
120
+ }),
121
+ ).toThrow('At least one time slot is required')
122
+ })
123
+ })
@@ -142,17 +142,12 @@ export function buildRoomReservationPayload(params: {
142
142
 
143
143
  const firstSlot = params.slots[0]
144
144
  const lastSlot = params.slots[params.slots.length - 1]
145
- const endSlot = TIME_SLOTS[TIME_SLOTS.indexOf(lastSlot) + 1]
146
-
147
- if (!endSlot) {
148
- throw new Error('Reservation end time is out of range')
149
- }
150
145
 
151
146
  const payload: Record<string, string> = {
152
147
  menuNo: MENU_NO.ROOM,
153
148
  itemId: String(params.roomId),
154
149
  rentBgnde: `${params.date} ${firstSlot}:00`,
155
- rentEndde: `${params.date} ${endSlot}:00`,
150
+ rentEndde: `${params.date} ${lastSlot}:00`,
156
151
  title: params.title,
157
152
  rentDt: params.date,
158
153
  rentNum: String(params.attendees ?? 1),
@@ -52,9 +52,12 @@ describe('formatStartTime / buildStartTimeParam', () => {
52
52
  expect(buildStartTimeParam('9:05')).toBe('0905')
53
53
  })
54
54
 
55
- it('rejects invalid time', () => {
55
+ it('rejects time without colon separator', () => {
56
56
  expect(() => formatStartTime('1000')).toThrow()
57
- expect(() => formatStartTime('25:00')).not.toThrow()
57
+ })
58
+
59
+ it('accepts out-of-range hours because the format matches', () => {
60
+ expect(formatStartTime('25:00')).toEqual({ hour: '25', minute: '00' })
58
61
  })
59
62
  })
60
63
 
@@ -120,13 +123,15 @@ describe('parseEmail', () => {
120
123
  })
121
124
 
122
125
  describe('assertDurationInRange', () => {
123
- it('accepts 120-180', () => {
124
- expect(() => assertDurationInRange(120)).not.toThrow()
125
- expect(() => assertDurationInRange(150)).not.toThrow()
126
- expect(() => assertDurationInRange(180)).not.toThrow()
126
+ it('accepts boundary and interior values', () => {
127
+ expect(assertDurationInRange(120)).toBeUndefined()
128
+ expect(assertDurationInRange(150)).toBeUndefined()
129
+ expect(assertDurationInRange(180)).toBeUndefined()
127
130
  })
128
131
 
129
- it('rejects out-of-range', () => {
132
+ it('rejects values just outside the boundary', () => {
133
+ expect(() => assertDurationInRange(119)).toThrow()
134
+ expect(() => assertDurationInRange(181)).toThrow()
130
135
  expect(() => assertDurationInRange(60)).toThrow()
131
136
  expect(() => assertDurationInRange(210)).toThrow()
132
137
  })
@@ -1,5 +1,5 @@
1
1
  import { Database } from 'bun:sqlite'
2
- import { afterEach, describe, expect, test } from 'bun:test'
2
+ import { afterEach, describe, expect, it } from 'bun:test'
3
3
  import { execSync } from 'node:child_process'
4
4
  import { createCipheriv, pbkdf2Sync } from 'node:crypto'
5
5
  import { mkdirSync, rmSync, writeFileSync } from 'node:fs'
@@ -22,7 +22,7 @@ afterEach(() => {
22
22
  })
23
23
 
24
24
  describe('TokenExtractor', () => {
25
- test('finds cookie databases for all browsers on macOS', async () => {
25
+ it('finds cookie databases for every supported browser on macOS', async () => {
26
26
  const home = await makeTempDir()
27
27
 
28
28
  for (const browser of BROWSERS) {
@@ -38,7 +38,7 @@ describe('TokenExtractor', () => {
38
38
  }
39
39
  })
40
40
 
41
- test('finds cookie databases for all browsers on Linux', async () => {
41
+ it('finds cookie databases for every supported browser on Linux', async () => {
42
42
  const home = await makeTempDir()
43
43
 
44
44
  for (const browser of BROWSERS) {
@@ -51,12 +51,12 @@ describe('TokenExtractor', () => {
51
51
  expect(paths).toHaveLength(BROWSERS.length)
52
52
  })
53
53
 
54
- test('returns null when no cookie databases exist', async () => {
54
+ it('returns null when no cookie databases exist', async () => {
55
55
  const extractor = new TokenExtractor('linux', await makeTempDir())
56
56
  expect(await extractor.extract()).toBeNull()
57
57
  })
58
58
 
59
- test('extracts plaintext cookie value', async () => {
59
+ it('extracts a plaintext cookie value', async () => {
60
60
  const home = await makeTempDir()
61
61
  const dbPath = join(home, '.config', 'google-chrome', 'Default', 'Cookies')
62
62
  createCookieDbWithPlaintext(dbPath, 'my-session-id')
@@ -65,7 +65,7 @@ describe('TokenExtractor', () => {
65
65
  expect(await extractor.extract()).toEqual({ sessionCookie: 'my-session-id' })
66
66
  })
67
67
 
68
- test('finds cookie databases across numbered browser profiles', async () => {
68
+ it('finds cookie databases across numbered browser profiles', async () => {
69
69
  const home = await makeTempDir()
70
70
  createCookieFile(join(home, '.config', 'google-chrome', 'Default', 'Cookies'))
71
71
  createCookieFile(join(home, '.config', 'google-chrome', 'Profile 1', 'Cookies'))
@@ -80,7 +80,7 @@ describe('TokenExtractor', () => {
80
80
  ])
81
81
  })
82
82
 
83
- test('extractCandidates keeps the newest unique cookie across profiles', async () => {
83
+ it('keeps the newest unique cookie across profiles when collecting candidates', async () => {
84
84
  const home = await makeTempDir()
85
85
  createCookieDbWithPlaintext(join(home, '.config', 'google-chrome', 'Default', 'Cookies'), 'stale-session', 10)
86
86
  createCookieDbWithPlaintext(join(home, '.config', 'google-chrome', 'Profile 1', 'Cookies'), 'valid-session', 20)
@@ -104,7 +104,7 @@ describe('TokenExtractor', () => {
104
104
  ])
105
105
  })
106
106
 
107
- test('extracts plaintext cookie from opensoma.dev host', async () => {
107
+ it('extracts a plaintext cookie from the opensoma.dev host', async () => {
108
108
  const home = await makeTempDir()
109
109
  const dbPath = join(home, '.config', 'google-chrome', 'Default', 'Cookies')
110
110
  createCookieDbWithPlaintext(dbPath, 'opensoma-dev-session', 0, 'opensoma.dev')
@@ -113,7 +113,7 @@ describe('TokenExtractor', () => {
113
113
  expect(await extractor.extract()).toEqual({ sessionCookie: 'opensoma-dev-session' })
114
114
  })
115
115
 
116
- test('decrypts encrypted cookie from opensoma.dev host on Linux', async () => {
116
+ it('decrypts an encrypted cookie from the opensoma.dev host on Linux', async () => {
117
117
  const home = await makeTempDir()
118
118
  const dbPath = join(home, '.config', 'google-chrome', 'Default', 'Cookies')
119
119
  const encrypted = encryptLinuxCookie('opensoma-dev-encrypted')
@@ -123,7 +123,7 @@ describe('TokenExtractor', () => {
123
123
  expect(await extractor.extract()).toEqual({ sessionCookie: 'opensoma-dev-encrypted' })
124
124
  })
125
125
 
126
- test('decrypts encrypted cookie on Linux', async () => {
126
+ it('decrypts an encrypted cookie on Linux', async () => {
127
127
  const home = await makeTempDir()
128
128
  const dbPath = join(home, '.config', 'google-chrome', 'Default', 'Cookies')
129
129
  const encrypted = encryptLinuxCookie('decrypted-session')
@@ -133,7 +133,7 @@ describe('TokenExtractor', () => {
133
133
  expect(await extractor.extract()).toEqual({ sessionCookie: 'decrypted-session' })
134
134
  })
135
135
 
136
- test('extracts plaintext cookie under Node.js (compiled ESM)', async () => {
136
+ it('extracts a plaintext cookie when running under Node.js (compiled ESM)', async () => {
137
137
  execSync('bun run build', { cwd: join(import.meta.dir, '..'), stdio: 'pipe' })
138
138
  const home = await makeTempDir()
139
139
  const dbPath = join(home, '.config', 'google-chrome', 'Default', 'Cookies')
@@ -144,7 +144,7 @@ describe('TokenExtractor', () => {
144
144
  expect(JSON.parse(result.trim())).toEqual({ sessionCookie: 'node-test-session' })
145
145
  })
146
146
 
147
- test('extracts encrypted cookie under Node.js (compiled ESM)', async () => {
147
+ it('extracts an encrypted cookie when running under Node.js (compiled ESM)', async () => {
148
148
  execSync('bun run build', { cwd: join(import.meta.dir, '..'), stdio: 'pipe' })
149
149
  const home = await makeTempDir()
150
150
  const dbPath = join(home, '.config', 'google-chrome', 'Default', 'Cookies')
@@ -1,4 +1,4 @@
1
- import { afterEach, describe, expect, mock, test } from 'bun:test'
1
+ import { afterEach, describe, expect, it, mock } from 'bun:test'
2
2
 
3
3
  import { TOZ_BASE_URL } from './constants'
4
4
  import { TozHttp } from './toz-http'
@@ -11,7 +11,7 @@ afterEach(() => {
11
11
  })
12
12
 
13
13
  describe('TozHttp', () => {
14
- test('bootstrap GETs index.htm to seed JSESSIONID', async () => {
14
+ it('seeds JSESSIONID by GETting index.htm during bootstrap', async () => {
15
15
  const fetchMock = mock(async (input: RequestInfo | URL, init?: RequestInit) => {
16
16
  expect(String(input)).toBe(`${TOZ_BASE_URL}/index.htm`)
17
17
  expect(init?.method).toBe('GET')
@@ -26,7 +26,7 @@ describe('TozHttp', () => {
26
26
  expect(fetchMock).toHaveBeenCalledTimes(1)
27
27
  })
28
28
 
29
- test('bootstrap is no-op when JSESSIONID already set', async () => {
29
+ it('skips bootstrap when JSESSIONID is already set', async () => {
30
30
  const fetchMock = mock(async () => createResponse('ok'))
31
31
  globalThis.fetch = fetchMock as typeof fetch
32
32
 
@@ -37,7 +37,7 @@ describe('TozHttp', () => {
37
37
  expect(http.getSessionCookie()).toBe('EXISTING')
38
38
  })
39
39
 
40
- test('post sends URL-encoded body with required headers and cookie', async () => {
40
+ it('sends URL-encoded POST bodies with the required headers and cookie', async () => {
41
41
  const fetchMock = mock(async (input: RequestInfo | URL, init?: RequestInit) => {
42
42
  expect(String(input)).toBe(`${TOZ_BASE_URL}/ajaxGetEnableBoothes.htm`)
43
43
  expect(init?.method).toBe('POST')
@@ -65,7 +65,7 @@ describe('TozHttp', () => {
65
65
  expect(text).toBe('[]')
66
66
  })
67
67
 
68
- test('postJson parses JSON response', async () => {
68
+ it('parses the JSON body returned by postJson', async () => {
69
69
  globalThis.fetch = mock(async () => createResponse('{"result":"SUCCESS","resultMsg":"abc"}')) as typeof fetch
70
70
 
71
71
  const http = new TozHttp({ sessionCookie: 'ABC' })
@@ -74,25 +74,25 @@ describe('TozHttp', () => {
74
74
  expect(json).toEqual({ result: 'SUCCESS', resultMsg: 'abc' })
75
75
  })
76
76
 
77
- test('postText trims plain-text response', async () => {
77
+ it('trims the plain-text body returned by postText', async () => {
78
78
  globalThis.fetch = mock(async () => createResponse('SUCCESS\n')) as typeof fetch
79
79
 
80
80
  const http = new TozHttp({ sessionCookie: 'ABC' })
81
81
  expect(await http.postText('/ajaxHpVerify.htm', {})).toBe('SUCCESS')
82
82
  })
83
83
 
84
- test('strips JSESSIONID= prefix when constructed with full cookie string', async () => {
84
+ it('strips the "JSESSIONID=" prefix when constructed with a full cookie string', async () => {
85
85
  const http = new TozHttp({ sessionCookie: 'JSESSIONID=XYZ789' })
86
86
  expect(http.getSessionCookie()).toBe('XYZ789')
87
87
  expect(http.getCookies()).toEqual({ JSESSIONID: 'XYZ789' })
88
88
  })
89
89
 
90
- test('constructor with cookies map preserves all cookies', async () => {
90
+ it('preserves every cookie when constructed with a cookies map', async () => {
91
91
  const http = new TozHttp({ cookies: { JSESSIONID: 'A', other: 'B' } })
92
92
  expect(http.getCookies()).toEqual({ JSESSIONID: 'A', other: 'B' })
93
93
  })
94
94
 
95
- test('throws on non-2xx response', async () => {
95
+ it('throws on a non-2xx response', async () => {
96
96
  globalThis.fetch = mock(async () =>
97
97
  createResponse('Server Error', [], 'text/html', { status: 500 }),
98
98
  ) as typeof fetch
@@ -101,7 +101,7 @@ describe('TozHttp', () => {
101
101
  await expect(http.get('/index.htm')).rejects.toThrow(/HTTP 500/)
102
102
  })
103
103
 
104
- test('refuses to follow cross-origin redirects (no cookie leak)', async () => {
104
+ it('refuses to follow cross-origin redirects to prevent cookie leaks', async () => {
105
105
  globalThis.fetch = mock(async (input: RequestInfo | URL) => {
106
106
  const url = String(input)
107
107
  if (url.endsWith('/index.htm')) {
@@ -114,7 +114,7 @@ describe('TozHttp', () => {
114
114
  await expect(http.get('/index.htm')).rejects.toThrow(/cross-origin redirect/)
115
115
  })
116
116
 
117
- test('follows same-origin redirects normally', async () => {
117
+ it('follows same-origin redirects normally', async () => {
118
118
  let hit = 0
119
119
  globalThis.fetch = mock(async (input: RequestInfo | URL) => {
120
120
  const url = String(input)