@@ -62,64 +62,244 @@ function makePlanet(tagName = "music", isDisplay = "1") {
6262 } ;
6363}
6464
65- // ---------------------------------------------------------------------------
66- // Regression: toTitleCase must be reachable when GlobalTag.render() runs
67- // ---------------------------------------------------------------------------
65+ describe ( "GlobalTag" , ( ) => {
66+ let tag ;
67+ let mockPlanet ;
6868
69- describe ( "GlobalTag – regression: toTitleCase dependency after utils modularization" , ( ) => {
7069 beforeEach ( ( ) => {
7170 addChipContainer ( "primarychips" ) ;
7271 addChipContainer ( "morechips" ) ;
73- global . _ = str => str ; // identity stub for translation helper
72+ global . _ = str => str ;
73+
74+ mockPlanet = {
75+ GlobalPlanet : {
76+ selectSpecialTag : jest . fn ( ) ,
77+ refreshTagList : jest . fn ( )
78+ } ,
79+ TagsManifest : {
80+ 1 : { TagName : "Music" , IsDisplayTag : "1" } ,
81+ 2 : { TagName : "Art" , IsDisplayTag : "0" } ,
82+ 3 : { TagName : "Math" , IsDisplayTag : "1" }
83+ }
84+ } ;
85+
86+ tag = new GlobalTag ( mockPlanet ) ;
7487 } ) ;
7588
7689 afterEach ( ( ) => {
7790 document . body . innerHTML = "" ;
7891 delete global . _ ;
92+ jest . restoreAllMocks ( ) ;
7993 } ) ;
8094
81- it ( "toTitleCase is defined on global after loading utils-logic" , ( ) => {
82- expect ( typeof global . toTitleCase ) . toBe ( "function" ) ;
83- } ) ;
95+ describe ( "regression tests for toTitleCase" , ( ) => {
96+ it ( "toTitleCase is defined on global after loading utils-logic" , ( ) => {
97+ expect ( typeof global . toTitleCase ) . toBe ( "function" ) ;
98+ } ) ;
99+
100+ it ( "toTitleCase capitalises the first letter of a string" , ( ) => {
101+ expect ( global . toTitleCase ( "hello" ) ) . toBe ( "Hello" ) ;
102+ } ) ;
103+
104+ it ( "toTitleCase returns undefined for non-string input" , ( ) => {
105+ expect ( global . toTitleCase ( 123 ) ) . toBeUndefined ( ) ;
106+ } ) ;
107+
108+ it ( "toTitleCase returns empty string for empty input" , ( ) => {
109+ expect ( global . toTitleCase ( "" ) ) . toBe ( "" ) ;
110+ } ) ;
111+
112+ it ( "GlobalTag.render() does not throw ReferenceError for toTitleCase" , ( ) => {
113+ const tag2 = new GlobalTag ( makePlanet ( "music" ) ) ;
114+ expect ( ( ) => tag2 . init ( { id : 1 } ) ) . not . toThrow ( ) ;
115+ } ) ;
116+
117+ it ( "GlobalTag.render() sets textContent via toTitleCase" , ( ) => {
118+ const tag2 = new GlobalTag ( makePlanet ( "art" ) ) ;
119+ tag2 . init ( { id : 1 } ) ;
120+ expect ( tag2 . tagElement . textContent ) . toBe ( "Art" ) ;
121+ } ) ;
84122
85- it ( "toTitleCase capitalises the first letter of a string" , ( ) => {
86- expect ( global . toTitleCase ( "hello" ) ) . toBe ( "Hello" ) ;
123+ it ( "display tag is appended to #primarychips" , ( ) => {
124+ const tag2 = new GlobalTag ( makePlanet ( "game" , "1" ) ) ;
125+ tag2 . init ( { id : 1 } ) ;
126+ expect ( document . getElementById ( "primarychips" ) . children . length ) . toBeGreaterThan ( 0 ) ;
127+ } ) ;
128+
129+ it ( "non-display tag is appended to #morechips" , ( ) => {
130+ const tag2 = new GlobalTag ( makePlanet ( "sensors" , "0" ) ) ;
131+ tag2 . init ( { id : 1 } ) ;
132+ expect ( document . getElementById ( "morechips" ) . children . length ) . toBeGreaterThan ( 0 ) ;
133+ } ) ;
134+
135+ it ( "special tag render() also does not throw" , ( ) => {
136+ const tag2 = new GlobalTag ( makePlanet ( ) ) ;
137+ expect ( ( ) => tag2 . init ( { name : "All Projects" , func : jest . fn ( ) } ) ) . not . toThrow ( ) ;
138+ } ) ;
87139 } ) ;
88140
89- it ( "toTitleCase returns undefined for non-string input" , ( ) => {
90- expect ( global . toTitleCase ( 123 ) ) . toBeUndefined ( ) ;
141+ describe ( "constructor" , ( ) => {
142+ it ( "should initialize with default values" , ( ) => {
143+ expect ( tag . Planet ) . toBe ( mockPlanet ) ;
144+ expect ( tag . id ) . toBeNull ( ) ;
145+ expect ( tag . name ) . toBeNull ( ) ;
146+ expect ( tag . func ) . toBeNull ( ) ;
147+ expect ( tag . IsDisplayTag ) . toBeNull ( ) ;
148+ expect ( tag . specialTag ) . toBeNull ( ) ;
149+ expect ( tag . tagElement ) . toBeNull ( ) ;
150+ expect ( tag . selected ) . toBe ( false ) ;
151+ expect ( tag . selectedClass ) . toBeNull ( ) ;
152+ } ) ;
153+
154+ it ( "should reference globalPlanet from Planet" , ( ) => {
155+ expect ( tag . globalPlanet ) . toBe ( mockPlanet . GlobalPlanet ) ;
156+ } ) ;
91157 } ) ;
92158
93- it ( "toTitleCase returns empty string for empty input" , ( ) => {
94- expect ( global . toTitleCase ( "" ) ) . toBe ( "" ) ;
159+ describe ( "init with regular tag (has id)" , ( ) => {
160+ it ( "should initialize as a non-special tag" , ( ) => {
161+ tag . init ( { id : "1" } ) ;
162+ expect ( tag . specialTag ) . toBe ( false ) ;
163+ expect ( tag . id ) . toBe ( "1" ) ;
164+ expect ( tag . name ) . toBe ( "Music" ) ;
165+ expect ( tag . func ) . toBeNull ( ) ;
166+ expect ( tag . selectedClass ) . toBe ( "selected" ) ;
167+ } ) ;
168+
169+ it ( "should set IsDisplayTag based on TagsManifest" , ( ) => {
170+ tag . init ( { id : "1" } ) ;
171+ expect ( tag . IsDisplayTag ) . toBe ( true ) ;
172+
173+ const tag2 = new GlobalTag ( mockPlanet ) ;
174+ tag2 . init ( { id : "2" } ) ;
175+ expect ( tag2 . IsDisplayTag ) . toBe ( false ) ;
176+ } ) ;
177+
178+ it ( "should render into primarychips for display tags" , ( ) => {
179+ tag . init ( { id : "1" } ) ;
180+ const chips = document . getElementById ( "primarychips" ) ;
181+ expect ( chips . children . length ) . toBe ( 1 ) ;
182+ } ) ;
183+
184+ it ( "should render into morechips for non-display tags" , ( ) => {
185+ tag . init ( { id : "2" } ) ;
186+ const chips = document . getElementById ( "morechips" ) ;
187+ expect ( chips . children . length ) . toBe ( 1 ) ;
188+ } ) ;
95189 } ) ;
96190
97- it ( "GlobalTag.render() does not throw ReferenceError for toTitleCase" , ( ) => {
98- const tag = new GlobalTag ( makePlanet ( "music" ) ) ;
99- expect ( ( ) => tag . init ( { id : 1 } ) ) . not . toThrow ( ) ;
191+ describe ( "init with special tag (no id)" , ( ) => {
192+ it ( "should initialize as a special tag" , ( ) => {
193+ const mockFunc = jest . fn ( ) ;
194+ tag . init ( { name : "All Projects" , func : mockFunc } ) ;
195+
196+ expect ( tag . specialTag ) . toBe ( true ) ;
197+ expect ( tag . IsDisplayTag ) . toBe ( true ) ;
198+ expect ( tag . id ) . toBeNull ( ) ;
199+ expect ( tag . name ) . toBe ( "All Projects" ) ;
200+ expect ( tag . func ) . toBe ( mockFunc ) ;
201+ expect ( tag . selectedClass ) . toBe ( "selected-special" ) ;
202+ } ) ;
203+
204+ it ( "should render special tags into primarychips" , ( ) => {
205+ tag . init ( { name : "My Projects" , func : jest . fn ( ) } ) ;
206+ const chips = document . getElementById ( "primarychips" ) ;
207+ expect ( chips . children . length ) . toBe ( 1 ) ;
208+ } ) ;
100209 } ) ;
101210
102- it ( "GlobalTag.render() sets textContent via toTitleCase" , ( ) => {
103- const tag = new GlobalTag ( makePlanet ( "art" ) ) ;
104- tag . init ( { id : 1 } ) ;
105- // _ is identity stub, toTitleCase("art") === "Art"
106- expect ( tag . tagElement . textContent ) . toBe ( "Art" ) ;
211+ describe ( "render" , ( ) => {
212+ it ( "should create a div element with chipselect and cursor classes" , ( ) => {
213+ tag . init ( { id : "1" } ) ;
214+ expect ( tag . tagElement . classList . contains ( "chipselect" ) ) . toBe ( true ) ;
215+ expect ( tag . tagElement . classList . contains ( "cursor" ) ) . toBe ( true ) ;
216+ } ) ;
217+
218+ it ( "should set text content from translated and title-cased name" , ( ) => {
219+ const spy = jest . spyOn ( global , "toTitleCase" ) ;
220+ tag . init ( { id : "1" } ) ;
221+ expect ( spy ) . toHaveBeenCalled ( ) ;
222+ expect ( tag . tagElement . textContent ) . toBe ( "Music" ) ;
223+ } ) ;
224+
225+ it ( "should add selectedClass when tag is selected before rendering" , ( ) => {
226+ tag . selected = true ;
227+ tag . selectedClass = "selected" ;
228+ tag . name = "Music" ;
229+ tag . IsDisplayTag = true ;
230+ tag . id = "1" ;
231+ tag . specialTag = false ;
232+
233+ tag . render ( ) ;
234+
235+ expect ( tag . tagElement . classList . contains ( "selected" ) ) . toBe ( true ) ;
236+ } ) ;
237+
238+ it ( "should not add selectedClass when tag is not selected" , ( ) => {
239+ tag . init ( { id : "1" } ) ;
240+ expect ( tag . tagElement . classList . contains ( "selected" ) ) . toBe ( false ) ;
241+ } ) ;
107242 } ) ;
108243
109- it ( "display tag is appended to #primarychips" , ( ) => {
110- const tag = new GlobalTag ( makePlanet ( "game" , "1" ) ) ;
111- tag . init ( { id : 1 } ) ;
112- expect ( document . getElementById ( "primarychips" ) . children . length ) . toBeGreaterThan ( 0 ) ;
244+ describe ( "select" , ( ) => {
245+ it ( "should add the selectedClass and set selected to true" , ( ) => {
246+ tag . init ( { id : "1" } ) ;
247+ tag . select ( ) ;
248+
249+ expect ( tag . tagElement . classList . contains ( "selected" ) ) . toBe ( true ) ;
250+ expect ( tag . selected ) . toBe ( true ) ;
251+ } ) ;
113252 } ) ;
114253
115- it ( "non-display tag is appended to #morechips" , ( ) => {
116- const tag = new GlobalTag ( makePlanet ( "sensors" , "0" ) ) ;
117- tag . init ( { id : 1 } ) ;
118- expect ( document . getElementById ( "morechips" ) . children . length ) . toBeGreaterThan ( 0 ) ;
254+ describe ( "unselect" , ( ) => {
255+ it ( "should remove the selectedClass and set selected to false" , ( ) => {
256+ tag . init ( { id : "1" } ) ;
257+ tag . select ( ) ;
258+ tag . unselect ( ) ;
259+
260+ expect ( tag . tagElement . classList . contains ( "selected" ) ) . toBe ( false ) ;
261+ expect ( tag . selected ) . toBe ( false ) ;
262+ } ) ;
119263 } ) ;
120264
121- it ( "special tag render() also does not throw" , ( ) => {
122- const tag = new GlobalTag ( makePlanet ( ) ) ;
123- expect ( ( ) => tag . init ( { name : "All Projects" , func : jest . fn ( ) } ) ) . not . toThrow ( ) ;
265+ describe ( "onTagClick" , ( ) => {
266+ it ( "should call selectSpecialTag when clicking an unselected special tag" , ( ) => {
267+ tag . init ( { name : "All Projects" , func : jest . fn ( ) } ) ;
268+ tag . selected = false ;
269+
270+ tag . onTagClick ( ) ;
271+
272+ expect ( mockPlanet . GlobalPlanet . selectSpecialTag ) . toHaveBeenCalledWith ( tag ) ;
273+ } ) ;
274+
275+ it ( "should select a non-special tag and refresh when unselected" , ( ) => {
276+ tag . init ( { id : "1" } ) ;
277+ tag . selected = false ;
278+
279+ tag . onTagClick ( ) ;
280+
281+ expect ( tag . selected ) . toBe ( true ) ;
282+ expect ( mockPlanet . GlobalPlanet . refreshTagList ) . toHaveBeenCalled ( ) ;
283+ } ) ;
284+
285+ it ( "should unselect a non-special tag and refresh when already selected" , ( ) => {
286+ tag . init ( { id : "1" } ) ;
287+ tag . select ( ) ;
288+
289+ tag . onTagClick ( ) ;
290+
291+ expect ( tag . selected ) . toBe ( false ) ;
292+ expect ( mockPlanet . GlobalPlanet . refreshTagList ) . toHaveBeenCalled ( ) ;
293+ } ) ;
294+
295+ it ( "should toggle selected special tag via unselect and refresh" , ( ) => {
296+ tag . init ( { name : "All Projects" , func : jest . fn ( ) } ) ;
297+ tag . select ( ) ;
298+
299+ tag . onTagClick ( ) ;
300+
301+ expect ( tag . selected ) . toBe ( false ) ;
302+ expect ( mockPlanet . GlobalPlanet . refreshTagList ) . toHaveBeenCalled ( ) ;
303+ } ) ;
124304 } ) ;
125305} ) ;
0 commit comments