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" >
223
324describe ( "util" , ( ) => {
425 describe ( "normalize" , ( ) => {
@@ -15,4 +36,227 @@ describe("util", () => {
1536 expect ( normalize ( "qux" , true ) ) . toBe ( "qux" )
1637 } )
1738 } )
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+ } )
18262} )
0 commit comments