Skip to content

Commit 301c1f1

Browse files
committed
refactor(tests): update test descriptions to English and improve normalization logic in location functions
1 parent c2c72c1 commit 301c1f1

File tree

2 files changed

+80
-41
lines changed

2 files changed

+80
-41
lines changed

packages/router/src/location.test.ts

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ expect.extend({
2424
// biome-ignore lint/correctness/noSelfAssign:
2525
expected.hash = expected.hash;
2626
return {
27-
message: () => `输出 ${received.href} 应该为 ${expected.href}`,
27+
message: () => `expected ${received.href} to be ${expected.href}`,
2828
pass: received.href === expected.href
2929
};
3030
}
@@ -41,37 +41,39 @@ describe('normalizeURL', () => {
4141
input: '//example.com/path',
4242
base: 'https://github.com',
4343
expected: 'http://example.com/path',
44-
description: '应该处理协议相对路径(以//开头)'
44+
description:
45+
'should handle protocol-relative URLs (starting with //)'
4546
},
4647
{
4748
input: 'http://github.com/path?a#h',
4849
base: 'http://example.com',
4950
expected: 'http://github.com/path?a#h',
50-
description: '应该处理绝对URL'
51+
description: 'should handle absolute URLs'
5152
},
5253
{
5354
input: '/path',
5455
base: 'http://example.com/en/',
5556
expected: 'http://example.com/en/path',
56-
description: '应该处理带基URL的相对路径'
57+
description: 'should handle relative paths with a base URL'
5758
},
5859
{
5960
input: 'github.com',
6061
base: 'http://example.com',
6162
expected: 'http://example.com/github.com',
62-
description: '裸域名应该当做相对路径处理'
63+
description: 'should treat bare domains as relative paths'
6364
},
6465
{
6566
input: new URL('http://example.com/path'),
6667
base: 'http://example.com',
6768
expected: 'http://example.com/path',
68-
description: '应该处理URL对象'
69+
description: 'should handle URL objects'
6970
},
7071
{
7172
input: '-a://example.com',
7273
base: 'http://example.com',
7374
expected: 'http://example.com/-a://example.com',
74-
description: '协议开头但解析失败后应该当做相对路径'
75+
description:
76+
'should treat strings that fail to parse as a protocol as relative paths'
7577
}
7678
];
7779

@@ -94,19 +96,19 @@ describe('parseLocation', () => {
9496
input: '/products',
9597
base: 'http://example.com',
9698
expected: 'http://example.com/products',
97-
description: '应该处理字符串路径'
99+
description: 'should handle string paths'
98100
},
99101
{
100102
input: { path: '/products' },
101103
base: 'http://example.com',
102104
expected: 'http://example.com/products',
103-
description: '应该处理带path属性的对象'
105+
description: 'should handle objects with a path property'
104106
},
105107
{
106108
input: { url: '/products' },
107109
base: 'http://example.com',
108110
expected: 'http://example.com/products',
109-
description: '应该处理带url属性的对象'
111+
description: 'should handle objects with a url property'
110112
},
111113
{
112114
input: {
@@ -115,19 +117,19 @@ describe('parseLocation', () => {
115117
},
116118
base: 'http://example.com',
117119
expected: 'http://example.com/products?id=123&category=electronics',
118-
description: '应该处理带query参数的对象'
120+
description: 'should handle objects with query parameters'
119121
},
120122
{
121123
input: { path: '/products', query: { id: '123' }, hash: 'details' },
122124
base: 'http://example.com',
123125
expected: 'http://example.com/products?id=123#details',
124-
description: '应该处理带hash的对象'
126+
description: 'should handle objects with a hash'
125127
},
126128
{
127129
input: { path: '/products', queryArray: { tag: ['new', 'sale'] } },
128130
base: 'http://example.com',
129131
expected: 'http://example.com/products?tag=new&tag=sale',
130-
description: '应该处理带queryArray的对象'
132+
description: 'should handle objects with queryArray'
131133
},
132134
{
133135
input: {
@@ -139,7 +141,7 @@ describe('parseLocation', () => {
139141
base: 'http://example.com',
140142
expected:
141143
'http://example.com/products?id=123&category=electronics&tag=new&tag=sale#details',
142-
description: '应该处理带所有属性的复杂对象'
144+
description: 'should handle complex objects with all properties'
143145
},
144146
{
145147
input: {
@@ -148,7 +150,7 @@ describe('parseLocation', () => {
148150
},
149151
base: 'http://example.com',
150152
expected: 'http://example.com/products#a?a',
151-
description: '特殊 hash 字符应该被正确处理'
153+
description: 'should handle special hash characters correctly'
152154
},
153155
{
154156
input: {
@@ -157,7 +159,7 @@ describe('parseLocation', () => {
157159
},
158160
base: 'http://example.com',
159161
expected: 'http://example.com/products#a?a#b',
160-
description: '特殊 hash 字符应该被正确处理'
162+
description: 'should handle special hash characters correctly'
161163
},
162164
{
163165
input: {
@@ -180,13 +182,14 @@ describe('parseLocation', () => {
180182
expected: `http://example.com/products?symbol=Symbol()&fn=${String(
181183
async () => ''
182184
)}&obj=${String({})}&big=12345678901234567891234567890123456789&b&c=0&d=0&e=1`,
183-
description: '应该忽略null、undefined和NaN的query参数'
185+
description:
186+
'should ignore null, undefined, and NaN query parameters'
184187
},
185188
{
186189
input: { path: '/products', queryArray: { tag: [] } },
187190
base: 'http://example.com',
188191
expected: 'http://example.com/products',
189-
description: '应该处理空queryArray'
192+
description: 'should handle empty queryArray'
190193
},
191194
{
192195
input: {
@@ -195,7 +198,7 @@ describe('parseLocation', () => {
195198
},
196199
base: 'http://example.com',
197200
expected: 'http://example.com/products?id=query&a',
198-
description: 'query的值应覆盖path中的query参数'
201+
description: 'query value should override query parameter in path'
199202
},
200203
{
201204
input: {
@@ -205,7 +208,8 @@ describe('parseLocation', () => {
205208
},
206209
base: 'http://example.com',
207210
expected: 'http://example.com/products?id=queryArray&a',
208-
description: 'queryArray的值应覆盖query和path中的query参数'
211+
description:
212+
'queryArray value should override query and path parameters'
209213
},
210214
{
211215
input: {
@@ -214,7 +218,8 @@ describe('parseLocation', () => {
214218
},
215219
base: 'http://example.com',
216220
expected: 'http://example.com/products?id=queryArray&a',
217-
description: 'queryArray的值应覆盖path中的query参数'
221+
description:
222+
'queryArray value should override query parameter in path'
218223
},
219224
{
220225
input: {
@@ -224,7 +229,8 @@ describe('parseLocation', () => {
224229
},
225230
base: 'http://example.com',
226231
expected: 'http://example.com?a&a',
227-
description: '应正确处理空字符串和重复的query参数'
232+
description:
233+
'should handle empty strings and duplicate query parameters correctly'
228234
},
229235
{
230236
input: { path: '/products?id=123', url: '/products?id=456' },

packages/router/src/location.ts

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,75 @@
11
import type { RouteLocationInput } from './types';
22
import { isNotNullish } from './util';
33

4+
/**
5+
* Normalizes a URL input into a URL object.
6+
* @param url - The URL or string to normalize.
7+
* @param base - The base URL to resolve against if the input is relative.
8+
* @returns A URL object.
9+
*/
410
export function normalizeURL(url: string | URL, base: URL): URL {
5-
if (typeof url === 'string') {
6-
// 处理协议相对路径(以//开头)
7-
if (url.startsWith('//')) {
8-
return new URL(`http:${url}`);
9-
}
10-
// 相对于根的路径(根为 base)
11-
if (url.startsWith('/')) {
12-
base = new URL('.', base);
13-
const parsed = new URL(url, base);
14-
parsed.pathname = base.pathname.slice(0, -1) + parsed.pathname; // 确保路径正确
15-
return parsed;
16-
}
11+
if (url instanceof URL) {
12+
return url;
13+
}
14+
15+
// Handle protocol-relative URLs (e.g., //example.com)
16+
if (url.startsWith('//')) {
17+
// Using http: as a default protocol for protocol-relative URLs.
18+
return new URL(`http:${url}`);
19+
}
20+
21+
// Handle root-relative paths
22+
if (url.startsWith('/')) {
23+
const newBase = new URL('.', base);
24+
const parsed = new URL(url, newBase);
25+
// This ensures that the path is resolved relative to the base's path directory.
26+
parsed.pathname = newBase.pathname.slice(0, -1) + parsed.pathname;
27+
return parsed;
28+
}
29+
30+
try {
31+
// Try to parse as an absolute URL.
32+
// This is the WHATWG standard approach (new URL()) and works consistently across all modern browsers and Node.js.
33+
// We use a try-catch block because the standard URL constructor throws an error for invalid URLs.
34+
//
35+
// NOTE: While `URL.parse()` might be observed in Chromium-based browsers (e.g., Chrome, Edge),
36+
// it is a non-standard, legacy feature implemented by the V8 engine for Node.js compatibility.
37+
// It is not part of the WHATWG URL Standard and is not supported by other browsers like Firefox or Safari.
38+
// Therefore, relying on it would compromise cross-browser compatibility.
39+
return new URL(url);
40+
} catch (e) {
41+
// Otherwise, parse as a relative URL
42+
return new URL(url, base);
1743
}
18-
return URL.parse(url) || new URL(url, base);
1944
}
2045

46+
/**
47+
* Parses a RouteLocationInput object into a full URL.
48+
* @param toInput - The route location input.
49+
* @param baseURL - The base URL to resolve against.
50+
* @returns The parsed URL object.
51+
*/
2152
export function parseLocation(toInput: RouteLocationInput, baseURL: URL): URL {
2253
if (typeof toInput === 'string') {
2354
return normalizeURL(toInput, baseURL);
2455
}
2556
const url = normalizeURL(toInput.path ?? toInput.url ?? '', baseURL);
2657
const searchParams = url.searchParams;
2758

28-
// 优先级 queryArray > query > path中的query
59+
// Priority: queryArray > query > query in path
2960
Object.entries<string | (string | undefined)[]>(
3061
Object.assign({}, toInput.query, toInput.queryArray)
3162
).forEach(([key, value]) => {
32-
searchParams.delete(key); // 清除之前的同名参数
63+
searchParams.delete(key); // Clear previous params with the same name
3364
value = Array.isArray(value) ? value : [value];
34-
value.filter(isNotNullish).forEach((v) => {
35-
searchParams.append(key, String(v));
36-
});
65+
value
66+
.filter((v) => isNotNullish(v) && !Number.isNaN(v))
67+
.forEach((v) => {
68+
searchParams.append(key, String(v));
69+
});
3770
});
3871

39-
// 设置hash值(URL片段标识符)
72+
// Set the hash (URL fragment identifier)
4073
if (toInput.hash) {
4174
url.hash = toInput.hash;
4275
}

0 commit comments

Comments
 (0)