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.
Files changed (133) hide show
  1. package/dist/package.json +5 -1
  2. package/dist/src/agent-browser-launcher.d.ts +43 -0
  3. package/dist/src/agent-browser-launcher.d.ts.map +1 -0
  4. package/dist/src/agent-browser-launcher.js +97 -0
  5. package/dist/src/agent-browser-launcher.js.map +1 -0
  6. package/dist/src/cli.d.ts.map +1 -1
  7. package/dist/src/cli.js +3 -2
  8. package/dist/src/cli.js.map +1 -1
  9. package/dist/src/client.d.ts +36 -7
  10. package/dist/src/client.d.ts.map +1 -1
  11. package/dist/src/client.js +231 -63
  12. package/dist/src/client.js.map +1 -1
  13. package/dist/src/commands/agent-browser.d.ts +3 -0
  14. package/dist/src/commands/agent-browser.d.ts.map +1 -0
  15. package/dist/src/commands/agent-browser.js +27 -0
  16. package/dist/src/commands/agent-browser.js.map +1 -0
  17. package/dist/src/commands/auth.d.ts +1 -1
  18. package/dist/src/commands/auth.d.ts.map +1 -1
  19. package/dist/src/commands/auth.js +4 -2
  20. package/dist/src/commands/auth.js.map +1 -1
  21. package/dist/src/commands/dashboard.d.ts +13 -0
  22. package/dist/src/commands/dashboard.d.ts.map +1 -1
  23. package/dist/src/commands/dashboard.js +10 -18
  24. package/dist/src/commands/dashboard.js.map +1 -1
  25. package/dist/src/commands/helpers.d.ts +1 -1
  26. package/dist/src/commands/helpers.d.ts.map +1 -1
  27. package/dist/src/commands/helpers.js +2 -2
  28. package/dist/src/commands/helpers.js.map +1 -1
  29. package/dist/src/commands/index.d.ts +2 -1
  30. package/dist/src/commands/index.d.ts.map +1 -1
  31. package/dist/src/commands/index.js +2 -1
  32. package/dist/src/commands/index.js.map +1 -1
  33. package/dist/src/commands/mentoring.d.ts.map +1 -1
  34. package/dist/src/commands/mentoring.js +54 -29
  35. package/dist/src/commands/mentoring.js.map +1 -1
  36. package/dist/src/commands/notice.d.ts.map +1 -1
  37. package/dist/src/commands/notice.js +2 -1
  38. package/dist/src/commands/notice.js.map +1 -1
  39. package/dist/src/commands/report.d.ts.map +1 -1
  40. package/dist/src/commands/report.js +4 -2
  41. package/dist/src/commands/report.js.map +1 -1
  42. package/dist/src/commands/room.d.ts.map +1 -1
  43. package/dist/src/commands/room.js +125 -2
  44. package/dist/src/commands/room.js.map +1 -1
  45. package/dist/src/commands/schedule.d.ts +3 -0
  46. package/dist/src/commands/schedule.d.ts.map +1 -0
  47. package/dist/src/commands/schedule.js +27 -0
  48. package/dist/src/commands/schedule.js.map +1 -0
  49. package/dist/src/commands/team.d.ts.map +1 -1
  50. package/dist/src/commands/team.js +55 -4
  51. package/dist/src/commands/team.js.map +1 -1
  52. package/dist/src/constants.d.ts +5 -5
  53. package/dist/src/constants.d.ts.map +1 -1
  54. package/dist/src/constants.js +20 -8
  55. package/dist/src/constants.js.map +1 -1
  56. package/dist/src/credential-manager.d.ts +9 -0
  57. package/dist/src/credential-manager.d.ts.map +1 -1
  58. package/dist/src/credential-manager.js +24 -0
  59. package/dist/src/credential-manager.js.map +1 -1
  60. package/dist/src/formatters.d.ts +11 -3
  61. package/dist/src/formatters.d.ts.map +1 -1
  62. package/dist/src/formatters.js +281 -52
  63. package/dist/src/formatters.js.map +1 -1
  64. package/dist/src/http.d.ts +8 -0
  65. package/dist/src/http.d.ts.map +1 -1
  66. package/dist/src/http.js +29 -1
  67. package/dist/src/http.js.map +1 -1
  68. package/dist/src/index.d.ts +4 -1
  69. package/dist/src/index.d.ts.map +1 -1
  70. package/dist/src/index.js +2 -1
  71. package/dist/src/index.js.map +1 -1
  72. package/dist/src/shared/utils/swmaestro.d.ts +34 -1
  73. package/dist/src/shared/utils/swmaestro.d.ts.map +1 -1
  74. package/dist/src/shared/utils/swmaestro.js +102 -43
  75. package/dist/src/shared/utils/swmaestro.js.map +1 -1
  76. package/dist/src/shared/utils/team-action-params.d.ts +3 -0
  77. package/dist/src/shared/utils/team-action-params.d.ts.map +1 -0
  78. package/dist/src/shared/utils/team-action-params.js +10 -0
  79. package/dist/src/shared/utils/team-action-params.js.map +1 -0
  80. package/dist/src/shared/utils/team-params.d.ts +12 -0
  81. package/dist/src/shared/utils/team-params.d.ts.map +1 -0
  82. package/dist/src/shared/utils/team-params.js +38 -0
  83. package/dist/src/shared/utils/team-params.js.map +1 -0
  84. package/dist/src/types.d.ts +147 -10
  85. package/dist/src/types.d.ts.map +1 -1
  86. package/dist/src/types.js +74 -6
  87. package/dist/src/types.js.map +1 -1
  88. package/package.json +5 -1
  89. package/src/agent-browser-launcher.test.ts +263 -0
  90. package/src/agent-browser-launcher.ts +159 -0
  91. package/src/cli.ts +4 -2
  92. package/src/client.test.ts +801 -140
  93. package/src/client.ts +293 -79
  94. package/src/commands/agent-browser.ts +33 -0
  95. package/src/commands/auth.test.ts +83 -32
  96. package/src/commands/auth.ts +5 -3
  97. package/src/commands/dashboard.test.ts +57 -0
  98. package/src/commands/dashboard.ts +22 -19
  99. package/src/commands/helpers.test.ts +79 -32
  100. package/src/commands/helpers.ts +3 -3
  101. package/src/commands/index.ts +2 -1
  102. package/src/commands/mentoring.ts +60 -29
  103. package/src/commands/notice.ts +2 -1
  104. package/src/commands/report.test.ts +7 -7
  105. package/src/commands/report.ts +4 -2
  106. package/src/commands/room.ts +160 -1
  107. package/src/commands/schedule.ts +32 -0
  108. package/src/commands/team.ts +73 -5
  109. package/src/constants.ts +20 -8
  110. package/src/credential-manager.test.ts +49 -5
  111. package/src/credential-manager.ts +27 -0
  112. package/src/formatters.test.ts +548 -53
  113. package/src/formatters.ts +309 -55
  114. package/src/http.test.ts +108 -39
  115. package/src/http.ts +41 -2
  116. package/src/index.ts +10 -1
  117. package/src/shared/utils/mentoring-params.test.ts +16 -16
  118. package/src/shared/utils/swmaestro.test.ts +326 -11
  119. package/src/shared/utils/swmaestro.ts +150 -52
  120. package/src/shared/utils/team-action-params.test.ts +32 -0
  121. package/src/shared/utils/team-action-params.ts +10 -0
  122. package/src/shared/utils/team-params.test.ts +141 -0
  123. package/src/shared/utils/team-params.ts +53 -0
  124. package/src/shared/utils/toz.test.ts +12 -7
  125. package/src/token-extractor.test.ts +12 -12
  126. package/src/toz-http.test.ts +11 -11
  127. package/src/types.test.ts +235 -206
  128. package/src/types.ts +87 -7
  129. package/dist/src/commands/event.d.ts +0 -3
  130. package/dist/src/commands/event.d.ts.map +0 -1
  131. package/dist/src/commands/event.js +0 -58
  132. package/dist/src/commands/event.js.map +0 -1
  133. package/src/commands/event.ts +0 -73
@@ -0,0 +1,141 @@
1
+ import { describe, expect, it } from 'bun:test'
2
+
3
+ import { MENU_NO } from '../../constants'
4
+ import { buildTeamListParams, parseTeamSearchQuery } from './team-params'
5
+
6
+ describe('parseTeamSearchQuery', () => {
7
+ it('defaults plain text to an all-fields search', () => {
8
+ expect(parseTeamSearchQuery('전수열')).toEqual({ field: 'all', value: '전수열' })
9
+ })
10
+
11
+ it('parses the "team:" prefix as a team-name search', () => {
12
+ expect(parseTeamSearchQuery('team:오픈소마')).toEqual({ field: 'team', value: '오픈소마' })
13
+ })
14
+
15
+ it('parses the "member:" prefix as a member-name search', () => {
16
+ expect(parseTeamSearchQuery('member:김철수')).toEqual({ field: 'member', value: '김철수' })
17
+ })
18
+
19
+ it('parses the "mentor:" prefix as a mentor-name search', () => {
20
+ expect(parseTeamSearchQuery('mentor:전수열')).toEqual({ field: 'mentor', value: '전수열' })
21
+ })
22
+
23
+ it('parses the "project:" prefix as a project-name search', () => {
24
+ expect(parseTeamSearchQuery('project:Previzion')).toEqual({ field: 'project', value: 'Previzion' })
25
+ })
26
+
27
+ it('parses the "all:" prefix as an all-fields search', () => {
28
+ expect(parseTeamSearchQuery('all:전수열')).toEqual({ field: 'all', value: '전수열' })
29
+ })
30
+
31
+ it('sets the "me" flag when parsing "mentor:@me"', () => {
32
+ expect(parseTeamSearchQuery('mentor:@me')).toEqual({ field: 'mentor', value: '@me', me: true })
33
+ })
34
+
35
+ it('sets the "me" flag when parsing "member:@me"', () => {
36
+ expect(parseTeamSearchQuery('member:@me')).toEqual({ field: 'member', value: '@me', me: true })
37
+ })
38
+
39
+ it('does not set the "me" flag for "team:@me" or "project:@me"', () => {
40
+ expect(parseTeamSearchQuery('team:@me')).toEqual({ field: 'team', value: '@me' })
41
+ expect(parseTeamSearchQuery('project:@me')).toEqual({ field: 'project', value: '@me' })
42
+ })
43
+
44
+ it('treats an unknown prefix as a plain all-fields search', () => {
45
+ expect(parseTeamSearchQuery('foo:bar')).toEqual({ field: 'all', value: 'foo:bar' })
46
+ })
47
+
48
+ it('preserves everything after the first colon when the value contains colons', () => {
49
+ expect(parseTeamSearchQuery('team:foo:bar')).toEqual({ field: 'team', value: 'foo:bar' })
50
+ })
51
+ })
52
+
53
+ describe('buildTeamListParams', () => {
54
+ it('returns only menuNo when no options are passed', () => {
55
+ expect(buildTeamListParams()).toEqual({ menuNo: MENU_NO.TEAM })
56
+ })
57
+
58
+ it('omits search params when search is not provided', () => {
59
+ expect(buildTeamListParams({})).toEqual({ menuNo: MENU_NO.TEAM })
60
+ })
61
+
62
+ it('sets searchCnd="" for an all-fields search', () => {
63
+ expect(buildTeamListParams({ search: { field: 'all', value: '전수열' } })).toEqual({
64
+ menuNo: MENU_NO.TEAM,
65
+ searchCnd: '',
66
+ searchWrd: '전수열',
67
+ })
68
+ })
69
+
70
+ it('sets searchCnd=1 for a member-name search', () => {
71
+ expect(buildTeamListParams({ search: { field: 'member', value: '김철수' } })).toEqual({
72
+ menuNo: MENU_NO.TEAM,
73
+ searchCnd: '1',
74
+ searchWrd: '김철수',
75
+ })
76
+ })
77
+
78
+ it('sets searchCnd=2 for a mentor-name search', () => {
79
+ expect(buildTeamListParams({ search: { field: 'mentor', value: '전수열' } })).toEqual({
80
+ menuNo: MENU_NO.TEAM,
81
+ searchCnd: '2',
82
+ searchWrd: '전수열',
83
+ })
84
+ })
85
+
86
+ it('sets searchCnd=3 for a project-name search', () => {
87
+ expect(buildTeamListParams({ search: { field: 'project', value: 'Previzion' } })).toEqual({
88
+ menuNo: MENU_NO.TEAM,
89
+ searchCnd: '3',
90
+ searchWrd: 'Previzion',
91
+ })
92
+ })
93
+
94
+ it('sets searchCnd=4 for a team-name search', () => {
95
+ expect(buildTeamListParams({ search: { field: 'team', value: '오픈소마' } })).toEqual({
96
+ menuNo: MENU_NO.TEAM,
97
+ searchCnd: '4',
98
+ searchWrd: '오픈소마',
99
+ })
100
+ })
101
+
102
+ it('substitutes the current user name when "mentor:@me" is used', () => {
103
+ expect(
104
+ buildTeamListParams({
105
+ search: { field: 'mentor', value: '@me', me: true },
106
+ user: { userId: 'neo@example.com', userNm: '전수열' },
107
+ }),
108
+ ).toEqual({
109
+ menuNo: MENU_NO.TEAM,
110
+ searchCnd: '2',
111
+ searchWrd: '전수열',
112
+ })
113
+ })
114
+
115
+ it('substitutes the current user name when "member:@me" is used', () => {
116
+ expect(
117
+ buildTeamListParams({
118
+ search: { field: 'member', value: '@me', me: true },
119
+ user: { userId: 'neo@example.com', userNm: '강동우' },
120
+ }),
121
+ ).toEqual({
122
+ menuNo: MENU_NO.TEAM,
123
+ searchCnd: '1',
124
+ searchWrd: '강동우',
125
+ })
126
+ })
127
+
128
+ it('does not emit a searchId param (the native team page does not accept it)', () => {
129
+ const params = buildTeamListParams({
130
+ search: { field: 'mentor', value: '@me', me: true },
131
+ user: { userId: 'neo@example.com', userNm: '전수열' },
132
+ })
133
+ expect(params).not.toHaveProperty('searchId')
134
+ })
135
+
136
+ it('omits search params when "@me" is requested but the user identity is unavailable', () => {
137
+ expect(buildTeamListParams({ search: { field: 'mentor', value: '@me', me: true } })).toEqual({
138
+ menuNo: MENU_NO.TEAM,
139
+ })
140
+ })
141
+ })
@@ -0,0 +1,53 @@
1
+ import { MENU_NO } from '../../constants'
2
+ import type { UserIdentity } from '../../http'
3
+
4
+ const SEARCH_FIELD_MAP: Record<string, string> = {
5
+ all: '',
6
+ member: '1',
7
+ mentor: '2',
8
+ project: '3',
9
+ team: '4',
10
+ }
11
+
12
+ export interface TeamSearchQuery {
13
+ field: 'all' | 'member' | 'mentor' | 'project' | 'team'
14
+ value: string
15
+ me?: boolean
16
+ }
17
+
18
+ export function parseTeamSearchQuery(raw: string): TeamSearchQuery {
19
+ const colonIndex = raw.indexOf(':')
20
+ if (colonIndex === -1) {
21
+ return { field: 'all', value: raw }
22
+ }
23
+
24
+ const field = raw.slice(0, colonIndex) as TeamSearchQuery['field']
25
+ if (!(field in SEARCH_FIELD_MAP)) {
26
+ return { field: 'all', value: raw }
27
+ }
28
+
29
+ const value = raw.slice(colonIndex + 1)
30
+ const me = (field === 'mentor' || field === 'member') && value === '@me'
31
+ return me ? { field, value, me } : { field, value }
32
+ }
33
+
34
+ export function buildTeamListParams(options?: {
35
+ search?: TeamSearchQuery
36
+ user?: UserIdentity
37
+ }): Record<string, string> {
38
+ const params: Record<string, string> = { menuNo: MENU_NO.TEAM }
39
+
40
+ if (options?.search) {
41
+ if (options.search.me) {
42
+ if (options.user) {
43
+ params.searchCnd = SEARCH_FIELD_MAP[options.search.field]
44
+ params.searchWrd = options.user.userNm
45
+ }
46
+ } else {
47
+ params.searchCnd = SEARCH_FIELD_MAP[options.search.field]
48
+ params.searchWrd = options.search.value
49
+ }
50
+ }
51
+
52
+ return params
53
+ }
@@ -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)