11import { jest } from '@jest/globals'
2- import { absoluteRegExp , relativeOrAnchorRegExp } from './url.js'
2+ import { absoluteRegExp , relativeOrAnchorRegExp , validateUrl } from './url.js'
33
44describe ( 'Lexical URL Regex Matchers' , ( ) => {
5- describe ( 'relative URLs ' , ( ) => {
5+ describe ( 'relativeOrAnchorRegExp ' , ( ) => {
66 it ( 'validation for links it should match' , async ( ) => {
77 const shouldMatch = [
88 '/path/to/resource' ,
@@ -13,6 +13,10 @@ describe('Lexical URL Regex Matchers', () => {
1313 '#anchor' ,
1414 '#section-title' ,
1515 '/path#fragment' ,
16+ '/page?id=123' ,
17+ '/page?id=123#section' ,
18+ '/search?q=test' ,
19+ '/?global=true' ,
1620 ]
1721
1822 shouldMatch . forEach ( ( testCase ) => {
@@ -40,7 +44,7 @@ describe('Lexical URL Regex Matchers', () => {
4044 } )
4145 } )
4246
43- describe ( 'absolute URLs ' , ( ) => {
47+ describe ( 'absoluteRegExp ' , ( ) => {
4448 it ( 'validation for links it should match' , async ( ) => {
4549 const shouldMatch = [
4650 'http://example.com' ,
@@ -85,4 +89,158 @@ describe('Lexical URL Regex Matchers', () => {
8589 } )
8690 } )
8791 } )
92+
93+ describe ( 'validateUrl' , ( ) => {
94+ describe ( 'absolute URLs' , ( ) => {
95+ it ( 'should validate http and https URLs' , ( ) => {
96+ const validUrls = [
97+ 'http://example.com' ,
98+ 'https://example.com' ,
99+ 'http://www.example.com' ,
100+ 'https://sub.example.com/path/file' ,
101+ 'http://example.com/resource' ,
102+ 'https://example.com/resource?key=value' ,
103+ 'http://example.com/resource#anchor' ,
104+ ]
105+
106+ validUrls . forEach ( ( url ) => {
107+ expect ( validateUrl ( url ) ) . toBe ( true )
108+ } )
109+ } )
110+
111+ it ( 'should validate other protocol URLs' , ( ) => {
112+ const validUrls = [ 'ftp://files.example.com' , 'mailto:email@example.com' , 'tel:+1234567890' ]
113+
114+ validUrls . forEach ( ( url ) => {
115+ expect ( validateUrl ( url ) ) . toBe ( true )
116+ } )
117+ } )
118+
119+ it ( 'should validate www URLs without protocol' , ( ) => {
120+ const validUrls = [
121+ 'www.example.com' ,
122+ 'www.example.com/resource' ,
123+ 'www.example.com/resource?query=1' ,
124+ 'www.example.com#fragment' ,
125+ ]
126+
127+ validUrls . forEach ( ( url ) => {
128+ expect ( validateUrl ( url ) ) . toBe ( true )
129+ } )
130+ } )
131+ } )
132+
133+ describe ( 'relative URLs' , ( ) => {
134+ it ( 'should validate relative paths' , ( ) => {
135+ const validUrls = [
136+ '/path/to/resource' ,
137+ '/file-name.html' ,
138+ '/' ,
139+ '/dir/' ,
140+ '/path.with.dots/' ,
141+ '/path#fragment' ,
142+ ]
143+
144+ validUrls . forEach ( ( url ) => {
145+ expect ( validateUrl ( url ) ) . toBe ( true )
146+ } )
147+ } )
148+ } )
149+
150+ describe ( 'anchor links' , ( ) => {
151+ it ( 'should validate anchor links' , ( ) => {
152+ const validUrls = [ '#anchor' , '#section-title' ]
153+
154+ validUrls . forEach ( ( url ) => {
155+ expect ( validateUrl ( url ) ) . toBe ( true )
156+ } )
157+ } )
158+ } )
159+
160+ describe ( 'with query params' , ( ) => {
161+ it ( 'should validate relative URLs with query parameters' , ( ) => {
162+ const validUrls = [
163+ '/page?id=123' ,
164+ '/search?q=test' ,
165+ '/products?category=electronics&sort=price' ,
166+ '/path?key=value&another=param' ,
167+ '/page?id=123&filter=active' ,
168+ '/?global=true' ,
169+ ]
170+
171+ validUrls . forEach ( ( url ) => {
172+ expect ( validateUrl ( url ) ) . toBe ( true )
173+ } )
174+ } )
175+
176+ it ( 'should validate absolute URLs with query parameters' , ( ) => {
177+ const validUrls = [
178+ 'https://example.com?id=123' ,
179+ 'http://example.com/page?key=value' ,
180+ 'www.example.com?search=query' ,
181+ 'https://example.com/path?a=1&b=2&c=3' ,
182+ ]
183+
184+ validUrls . forEach ( ( url ) => {
185+ expect ( validateUrl ( url ) ) . toBe ( true )
186+ } )
187+ } )
188+
189+ it ( 'should validate URLs with query parameters and anchors' , ( ) => {
190+ const validUrls = [
191+ '/page?id=123#section' ,
192+ 'https://example.com?key=value#anchor' ,
193+ '/search?q=test#results' ,
194+ ]
195+
196+ validUrls . forEach ( ( url ) => {
197+ expect ( validateUrl ( url ) ) . toBe ( true )
198+ } )
199+ } )
200+ } )
201+
202+ describe ( 'edge cases' , ( ) => {
203+ it ( 'should handle the default https:// case' , ( ) => {
204+ expect ( validateUrl ( 'https://' ) ) . toBe ( true )
205+ } )
206+
207+ it ( 'should return false for empty or invalid URLs' , ( ) => {
208+ const invalidUrls = [
209+ '' ,
210+ 'not-a-url' ,
211+ 'example.com' ,
212+ 'relative/path' ,
213+ 'file.html' ,
214+ 'some#fragment' ,
215+ 'http://' ,
216+ 'http:/example.com' ,
217+ 'http//example.com' ,
218+ ]
219+
220+ invalidUrls . forEach ( ( url ) => {
221+ expect ( validateUrl ( url ) ) . toBe ( false )
222+ } )
223+ } )
224+
225+ it ( 'should return false for URLs with spaces' , ( ) => {
226+ const invalidUrls = [
227+ '/path/with spaces' ,
228+ 'http://example.com/ spaces' ,
229+ 'https://example.com/path with spaces' ,
230+ ]
231+
232+ invalidUrls . forEach ( ( url ) => {
233+ expect ( validateUrl ( url ) ) . toBe ( false )
234+ } )
235+ } )
236+
237+ it ( 'should return false for malformed URLs' , ( ) => {
238+ const invalidUrls = [ '://missing.scheme' , 'ftp://example .com' , 'http://example' , '#' , '/#' ]
239+
240+ invalidUrls . forEach ( ( url ) => {
241+ expect ( validateUrl ( url ) ) . toBe ( false )
242+ } )
243+ } )
244+ } )
245+ } )
88246} )
0 commit comments