1
- import { normalize } from "../src/common/util"
1
+ import { JSDOM } from "jsdom"
2
+ // Note: we need to import logger from the root
3
+ // because this is the logger used in logError in ../src/common/util
4
+ import { logger } from "../node_modules/@coder/logger"
5
+ import {
6
+ arrayify ,
7
+ generateUuid ,
8
+ getFirstString ,
9
+ getOptions ,
10
+ logError ,
11
+ normalize ,
12
+ plural ,
13
+ resolveBase ,
14
+ split ,
15
+ trimSlashes ,
16
+ } from "../src/common/util"
17
+
18
+ const dom = new JSDOM ( )
19
+ global . document = dom . window . document
20
+ // global.window = (dom.window as unknown) as Window & typeof globalThis
21
+
22
+ type LocationLike = Pick < Location , "pathname" | "origin" >
2
23
3
24
describe ( "util" , ( ) => {
4
25
describe ( "normalize" , ( ) => {
@@ -15,4 +36,227 @@ describe("util", () => {
15
36
expect ( normalize ( "qux" , true ) ) . toBe ( "qux" )
16
37
} )
17
38
} )
39
+
40
+ describe ( "split" , ( ) => {
41
+ it ( "should split at a comma" , ( ) => {
42
+ expect ( split ( "Hello,world" , "," ) ) . toStrictEqual ( [ "Hello" , "world" ] )
43
+ } )
44
+
45
+ it ( "shouldn't split if the delimiter doesn't exist" , ( ) => {
46
+ expect ( split ( "Hello world" , "," ) ) . toStrictEqual ( [ "Hello world" , "" ] )
47
+ } )
48
+ } )
49
+
50
+ describe ( "plural" , ( ) => {
51
+ it ( "should add an s if count is greater than 1" , ( ) => {
52
+ expect ( plural ( 2 , "dog" ) ) . toBe ( "dogs" )
53
+ } )
54
+ it ( "should NOT add an s if the count is 1" , ( ) => {
55
+ expect ( plural ( 1 , "dog" ) ) . toBe ( "dog" )
56
+ } )
57
+ } )
58
+
59
+ describe ( "generateUuid" , ( ) => {
60
+ it ( "should generate a unique uuid" , ( ) => {
61
+ const uuid = generateUuid ( )
62
+ const uuid2 = generateUuid ( )
63
+ expect ( uuid ) . toHaveLength ( 24 )
64
+ expect ( typeof uuid ) . toBe ( "string" )
65
+ expect ( uuid ) . not . toBe ( uuid2 )
66
+ } )
67
+ it ( "should generate a uuid of a specific length" , ( ) => {
68
+ const uuid = generateUuid ( 10 )
69
+ expect ( uuid ) . toHaveLength ( 10 )
70
+ } )
71
+ } )
72
+
73
+ describe ( "trimSlashes" , ( ) => {
74
+ it ( "should remove leading slashes" , ( ) => {
75
+ expect ( trimSlashes ( "/hello-world" ) ) . toBe ( "hello-world" )
76
+ } )
77
+
78
+ it ( "should remove trailing slashes" , ( ) => {
79
+ expect ( trimSlashes ( "hello-world/" ) ) . toBe ( "hello-world" )
80
+ } )
81
+
82
+ it ( "should remove both leading and trailing slashes" , ( ) => {
83
+ expect ( trimSlashes ( "/hello-world/" ) ) . toBe ( "hello-world" )
84
+ } )
85
+
86
+ it ( "should remove multiple leading and trailing slashes" , ( ) => {
87
+ expect ( trimSlashes ( "///hello-world////" ) ) . toBe ( "hello-world" )
88
+ } )
89
+ } )
90
+
91
+ describe ( "resolveBase" , ( ) => {
92
+ beforeEach ( ( ) => {
93
+ const location : LocationLike = {
94
+ pathname : "/healthz" ,
95
+ origin : "http://localhost:8080" ,
96
+ }
97
+
98
+ // Because resolveBase is not a pure function
99
+ // and relies on the global location to be set
100
+ // we set it before all the tests
101
+ // and tell TS that our location should be looked at
102
+ // as Location (even though it's missing some properties)
103
+ global . location = location as Location
104
+ } )
105
+
106
+ it ( "should resolve a base" , ( ) => {
107
+ expect ( resolveBase ( "localhost:8080" ) ) . toBe ( "/localhost:8080" )
108
+ } )
109
+
110
+ it ( "should resolve a base with a forward slash at the beginning" , ( ) => {
111
+ expect ( resolveBase ( "/localhost:8080" ) ) . toBe ( "/localhost:8080" )
112
+ } )
113
+
114
+ it ( "should resolve a base with query params" , ( ) => {
115
+ expect ( resolveBase ( "localhost:8080?folder=hello-world" ) ) . toBe ( "/localhost:8080" )
116
+ } )
117
+
118
+ it ( "should resolve a base with a path" , ( ) => {
119
+ expect ( resolveBase ( "localhost:8080/hello/world" ) ) . toBe ( "/localhost:8080/hello/world" )
120
+ } )
121
+
122
+ it ( "should resolve a base to an empty string when not provided" , ( ) => {
123
+ expect ( resolveBase ( ) ) . toBe ( "" )
124
+ } )
125
+ } )
126
+
127
+ describe ( "getOptions" , ( ) => {
128
+ // Things to mock
129
+ // logger
130
+ // location
131
+ // document
132
+ beforeEach ( ( ) => {
133
+ const location : LocationLike = {
134
+ pathname : "/healthz" ,
135
+ origin : "http://localhost:8080" ,
136
+ // search: "?environmentId=600e0187-0909d8a00cb0a394720d4dce",
137
+ }
138
+
139
+ // Because resolveBase is not a pure function
140
+ // and relies on the global location to be set
141
+ // we set it before all the tests
142
+ // and tell TS that our location should be looked at
143
+ // as Location (even though it's missing some properties)
144
+ global . location = location as Location
145
+ } )
146
+
147
+ afterEach ( ( ) => {
148
+ jest . restoreAllMocks ( )
149
+ } )
150
+
151
+ it ( "should return options with base and cssStaticBase even if it doesn't exist" , ( ) => {
152
+ expect ( getOptions ( ) ) . toStrictEqual ( {
153
+ base : "" ,
154
+ csStaticBase : "" ,
155
+ } )
156
+ } )
157
+
158
+ it ( "should return options when they do exist" , ( ) => {
159
+ // Mock getElementById
160
+ const spy = jest . spyOn ( document , "getElementById" )
161
+ // Create a fake element and set the attribute
162
+ const mockElement = document . createElement ( "div" )
163
+ mockElement . setAttribute (
164
+ "data-settings" ,
165
+ '{"base":".","csStaticBase":"./static/development/Users/jp/Dev/code-server","logLevel":2,"disableTelemetry":false,"disableUpdateCheck":false}' ,
166
+ )
167
+ // Return mockElement from the spy
168
+ // this way, when we call "getElementById"
169
+ // it returns the element
170
+ spy . mockImplementation ( ( ) => mockElement )
171
+
172
+ expect ( getOptions ( ) ) . toStrictEqual ( {
173
+ base : "" ,
174
+ csStaticBase : "/static/development/Users/jp/Dev/code-server" ,
175
+ disableTelemetry : false ,
176
+ disableUpdateCheck : false ,
177
+ logLevel : 2 ,
178
+ } )
179
+ } )
180
+
181
+ it ( "should include queryOpts" , ( ) => {
182
+ // Trying to understand how the implementation works
183
+ // 1. It grabs the search params from location.search (i.e. ?)
184
+ // 2. it then grabs the "options" param if it exists
185
+ // 3. then it creates a new options object
186
+ // spreads the original options
187
+ // then parses the queryOpts
188
+ location . search = '?options={"logLevel":2}'
189
+ expect ( getOptions ( ) ) . toStrictEqual ( {
190
+ base : "" ,
191
+ csStaticBase : "" ,
192
+ logLevel : 2 ,
193
+ } )
194
+ } )
195
+ } )
196
+
197
+ describe ( "arrayify" , ( ) => {
198
+ it ( "should return value it's already an array" , ( ) => {
199
+ expect ( arrayify ( [ "hello" , "world" ] ) ) . toStrictEqual ( [ "hello" , "world" ] )
200
+ } )
201
+
202
+ it ( "should wrap the value in an array if not an array" , ( ) => {
203
+ expect (
204
+ arrayify ( {
205
+ name : "Coder" ,
206
+ version : "3.8" ,
207
+ } ) ,
208
+ ) . toStrictEqual ( [ { name : "Coder" , version : "3.8" } ] )
209
+ } )
210
+
211
+ it ( "should return an empty array if the value is undefined" , ( ) => {
212
+ expect ( arrayify ( undefined ) ) . toStrictEqual ( [ ] )
213
+ } )
214
+ } )
215
+
216
+ describe ( "getFirstString" , ( ) => {
217
+ it ( "should return the string if passed a string" , ( ) => {
218
+ expect ( getFirstString ( "Hello world!" ) ) . toBe ( "Hello world!" )
219
+ } )
220
+
221
+ it ( "should get the first string from an array" , ( ) => {
222
+ expect ( getFirstString ( [ "Hello" , "World" ] ) ) . toBe ( "Hello" )
223
+ } )
224
+
225
+ it ( "should return undefined if the value isn't an array or a string" , ( ) => {
226
+ expect ( getFirstString ( { name : "Coder" } ) ) . toBe ( undefined )
227
+ } )
228
+ } )
229
+
230
+ describe ( "logError" , ( ) => {
231
+ let spy : jest . SpyInstance
232
+
233
+ beforeEach ( ( ) => {
234
+ spy = jest . spyOn ( logger , "error" )
235
+ } )
236
+
237
+ afterEach ( ( ) => {
238
+ jest . clearAllMocks ( )
239
+ } )
240
+
241
+ afterAll ( ( ) => {
242
+ jest . restoreAllMocks ( )
243
+ } )
244
+
245
+ it ( "should log an error with the message and stack trace" , ( ) => {
246
+ const message = "You don't have access to that folder."
247
+ const error = new Error ( message )
248
+
249
+ logError ( "ui" , error )
250
+
251
+ expect ( spy ) . toHaveBeenCalled ( )
252
+ expect ( spy ) . toHaveBeenCalledWith ( `ui: ${ error . message } ${ error . stack } ` )
253
+ } )
254
+
255
+ it ( "should log an error, even if not an instance of error" , ( ) => {
256
+ logError ( "api" , "oh no" )
257
+
258
+ expect ( spy ) . toHaveBeenCalled ( )
259
+ expect ( spy ) . toHaveBeenCalledWith ( "api: oh no" )
260
+ } )
261
+ } )
18
262
} )
0 commit comments