11<?php
22
33require_once "src/UI/Lessons.php " ;
4+ require_once "src/Config/ConfigInfo.php " ;
45
56use \Tsugi \UI \Lessons ;
67
78class LessonsTest extends \PHPUnit \Framework \TestCase
89{
910 private $ testJsonFile ;
11+ private $ originalCFG ;
1012
1113 protected function setUp (): void
1214 {
1315 parent ::setUp ();
16+ global $ CFG ;
17+ $ this ->originalCFG = $ CFG ;
18+
19+ // Set up test CFG
20+ $ CFG = new \Tsugi \Config \ConfigInfo (basename (__FILE__ ), 'http://localhost ' );
21+ $ CFG ->apphome = 'http://localhost/app ' ;
22+ $ CFG ->wwwroot = 'http://localhost ' ;
23+
1424 // Create a temporary JSON file for testing
1525 $ this ->testJsonFile = sys_get_temp_dir () . '/test_lessons_ ' . uniqid () . '.json ' ;
1626 $ testData = [
@@ -34,10 +44,13 @@ protected function setUp(): void
3444
3545 protected function tearDown (): void
3646 {
47+ global $ CFG ;
3748 // Clean up temporary file
3849 if (file_exists ($ this ->testJsonFile )) {
3950 unlink ($ this ->testJsonFile );
4051 }
52+ // Restore original CFG
53+ $ CFG = $ this ->originalCFG ;
4154 parent ::tearDown ();
4255 }
4356
@@ -86,5 +99,155 @@ public function testGetSettingFromLoadedJson() {
8699 $ result = $ lessons ->getSetting ('nonexistent ' , 'default ' );
87100 $ this ->assertEquals ('default ' , $ result , 'getSetting should return default for nonexistent key ' );
88101 }
102+
103+ /**
104+ * Test expandLink() static method - does macro substitution on URLs
105+ */
106+ public function testExpandLink () {
107+ global $ CFG ;
108+
109+ // Test with {apphome} macro
110+ $ url = '{apphome}/some/path ' ;
111+ $ result = Lessons::expandLink ($ url );
112+ $ this ->assertEquals ('http://localhost/app/some/path ' , $ result , 'expandLink should replace {apphome} ' );
113+
114+ // Test with {wwwroot} macro
115+ $ url = '{wwwroot}/other/path ' ;
116+ $ result = Lessons::expandLink ($ url );
117+ $ this ->assertEquals ('http://localhost/other/path ' , $ result , 'expandLink should replace {wwwroot} ' );
118+
119+ // Test with both macros
120+ $ url = '{apphome}/app and {wwwroot}/www ' ;
121+ $ result = Lessons::expandLink ($ url );
122+ $ this ->assertEquals ('http://localhost/app/app and http://localhost/www ' , $ result , 'expandLink should replace both macros ' );
123+
124+ // Test with no macros
125+ $ url = 'http://example.com/path ' ;
126+ $ result = Lessons::expandLink ($ url );
127+ $ this ->assertEquals ('http://example.com/path ' , $ result , 'expandLink should leave URLs without macros unchanged ' );
128+ }
129+
130+ /**
131+ * Test expandLink() with URLs that already start with http:// or https://
132+ */
133+ public function testExpandLinkSkipsHttpUrls () {
134+ global $ CFG ;
135+
136+ // Test with http:// URL (should skip expansion)
137+ $ url = 'http://example.com/path ' ;
138+ $ result = Lessons::expandLink ($ url );
139+ $ this ->assertEquals ('http://example.com/path ' , $ result , 'expandLink should skip URLs starting with http:// ' );
140+
141+ // Test with https:// URL (should skip expansion)
142+ $ url = 'https://example.com/path ' ;
143+ $ result = Lessons::expandLink ($ url );
144+ $ this ->assertEquals ('https://example.com/path ' , $ result , 'expandLink should skip URLs starting with https:// ' );
145+
146+ // Test with http:// URL containing macros (should still skip)
147+ $ url = 'http://example.com/{apphome}/path ' ;
148+ $ result = Lessons::expandLink ($ url );
149+ $ this ->assertEquals ('http://example.com/{apphome}/path ' , $ result , 'expandLink should skip URLs starting with http:// even if they contain macros ' );
150+ }
151+
152+ /**
153+ * Test expandLink() with duplicate placeholders cleanup
154+ */
155+ public function testExpandLinkCleansDuplicatePlaceholders () {
156+ global $ CFG ;
157+
158+ // Test with duplicate {apphome} placeholders
159+ $ url = '{apphome}/{apphome}/path ' ;
160+ $ result = Lessons::expandLink ($ url );
161+ $ this ->assertEquals ('http://localhost/app/path ' , $ result , 'expandLink should clean up duplicate {apphome} placeholders ' );
162+
163+ // Test with duplicate {wwwroot} placeholders
164+ $ url = '{wwwroot}/{wwwroot}/path ' ;
165+ $ result = Lessons::expandLink ($ url );
166+ $ this ->assertEquals ('http://localhost/path ' , $ result , 'expandLink should clean up duplicate {wwwroot} placeholders ' );
167+
168+ // Test with multiple slashes between duplicates
169+ $ url = '{apphome}//{apphome}/path ' ;
170+ $ result = Lessons::expandLink ($ url );
171+ $ this ->assertEquals ('http://localhost/app/path ' , $ result , 'expandLink should clean up duplicate placeholders with multiple slashes ' );
172+ }
173+
174+ /**
175+ * Test expandLink() prevents double expansion
176+ */
177+ public function testExpandLinkPreventsDoubleExpansion () {
178+ global $ CFG ;
179+
180+ // Test that URLs starting with http:// are returned as-is (even with placeholders)
181+ $ url = 'http://localhost/app/some/path/{apphome} ' ;
182+ $ result = Lessons::expandLink ($ url );
183+ $ this ->assertEquals ('http://localhost/app/some/path/{apphome} ' , $ result , 'expandLink should return http:// URLs as-is without processing ' );
184+
185+ // Test double expansion prevention with a URL that contains full expanded apphome
186+ // but doesn't start with http:// (edge case - unlikely in practice)
187+ $ url = 'someprefix ' . $ CFG ->apphome . '/path/{apphome} ' ;
188+ $ result = Lessons::expandLink ($ url );
189+ // Should detect that apphome is already present and remove placeholder
190+ $ this ->assertStringNotContainsString ('{apphome} ' , $ result , 'expandLink should remove placeholders when URL already contains full expanded apphome ' );
191+ $ this ->assertStringContainsString ($ CFG ->apphome , $ result , 'expandLink should preserve existing expanded apphome ' );
192+
193+ // Test with wwwroot
194+ $ url = 'someprefix ' . $ CFG ->wwwroot . '/path/{wwwroot} ' ;
195+ $ result = Lessons::expandLink ($ url );
196+ $ this ->assertStringNotContainsString ('{wwwroot} ' , $ result , 'expandLink should remove placeholders when URL already contains full expanded wwwroot ' );
197+ }
198+
199+ /**
200+ * Test expandLink() cleans up double slashes
201+ */
202+ public function testExpandLinkCleansDoubleSlashes () {
203+ global $ CFG ;
204+
205+ // Test that double slashes are cleaned up after placeholder removal
206+ $ url = 'prefix ' . $ CFG ->apphome . '/path//{apphome} ' ;
207+ $ result = Lessons::expandLink ($ url );
208+ // After removing placeholder, should clean up double slashes
209+ $ pos = strpos ($ result , $ CFG ->apphome );
210+ if ($ pos !== false ) {
211+ $ after_apphome = substr ($ result , $ pos + strlen ($ CFG ->apphome ));
212+ // Should not have double slashes (except http:// at the start)
213+ $ this ->assertStringNotContainsString ('// ' , $ after_apphome , 'expandLink should clean up double slashes after placeholder removal ' );
214+ }
215+
216+ // Test normal expansion doesn't create double slashes
217+ $ url = '{apphome}/path ' ;
218+ $ result = Lessons::expandLink ($ url );
219+ $ this ->assertEquals ($ CFG ->apphome . '/path ' , $ result , 'Normal expansion should create clean URLs ' );
220+ }
221+
222+ /**
223+ * Test isSingle() method
224+ */
225+ public function testIsSingle () {
226+ $ lessons = new class extends Lessons {
227+ public function __construct () {
228+ // Skip parent constructor
229+ }
230+ };
231+
232+ // Test with no anchor or position
233+ $ lessons ->anchor = null ;
234+ $ lessons ->position = null ;
235+ $ this ->assertFalse ($ lessons ->isSingle (), 'isSingle should return false when anchor and position are null ' );
236+
237+ // Test with anchor set
238+ $ lessons ->anchor = 'test-anchor ' ;
239+ $ lessons ->position = null ;
240+ $ this ->assertTrue ($ lessons ->isSingle (), 'isSingle should return true when anchor is set ' );
241+
242+ // Test with position set
243+ $ lessons ->anchor = null ;
244+ $ lessons ->position = 5 ;
245+ $ this ->assertTrue ($ lessons ->isSingle (), 'isSingle should return true when position is set ' );
246+
247+ // Test with both set
248+ $ lessons ->anchor = 'test-anchor ' ;
249+ $ lessons ->position = 5 ;
250+ $ this ->assertTrue ($ lessons ->isSingle (), 'isSingle should return true when both anchor and position are set ' );
251+ }
89252
90253}
0 commit comments