|
|
@@ -21,7 +21,7 @@ import { DEFAULT_TIMEOUTS } from './constants';
|
|
|
* @param selector - 文件输入框的选择器
|
|
|
* @param fileName - 要上传的文件名(相对于 fixtures 目录)
|
|
|
* @param options - 可选配置
|
|
|
- * @param options.fixturesDir - fixtures 目录路径,默认为 'tests/fixtures'
|
|
|
+ * @param options.fixturesDir - fixtures 目录路径,默认为 'web/tests/fixtures'
|
|
|
* @param options.timeout - 超时时间(毫秒),默认 5000ms
|
|
|
* @param options.waitForUpload - 是否等待上传完成,默认为 true
|
|
|
* @throws {E2ETestError} 当文件不存在或选择器无效时
|
|
|
@@ -53,7 +53,7 @@ export async function uploadFileToField(
|
|
|
// 1. 合并默认配置
|
|
|
const config = {
|
|
|
timeout: options?.timeout ?? DEFAULT_TIMEOUTS.static,
|
|
|
- fixturesDir: options?.fixturesDir ?? 'tests/fixtures',
|
|
|
+ fixturesDir: options?.fixturesDir ?? 'web/tests/fixtures',
|
|
|
waitForUpload: options?.waitForUpload ?? true
|
|
|
};
|
|
|
|
|
|
@@ -89,13 +89,20 @@ export async function uploadFileToField(
|
|
|
|
|
|
console.debug(`[uploadFileToField] 上传完成`);
|
|
|
} catch (error) {
|
|
|
- // 选择器无效或其他错误
|
|
|
+ // 选择器无效或其他错误 - 提供详细的上下文信息
|
|
|
+ const errorMessage = error instanceof Error ? error.message : '未知错误';
|
|
|
+
|
|
|
throwError({
|
|
|
operation: 'uploadFileToField',
|
|
|
target: `选择器 "${selector}"`,
|
|
|
- expected: `文件输入框存在于页面`,
|
|
|
- actual: error instanceof Error ? error.message : '未知错误',
|
|
|
- suggestion: '检查选择器是否正确,确认文件输入框已渲染到页面'
|
|
|
+ expected: '文件输入框存在于页面且可访问',
|
|
|
+ actual: `错误: ${errorMessage}`,
|
|
|
+ suggestion: [
|
|
|
+ '检查选择器是否正确(推荐使用 data-testid)',
|
|
|
+ '确认文件输入框已渲染到页面',
|
|
|
+ '确认元素可见且未被隐藏(display: none)',
|
|
|
+ '检查是否需要等待页面加载完成'
|
|
|
+ ].join('\n ')
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
@@ -116,8 +123,28 @@ export async function uploadFileToField(
|
|
|
|
|
|
function resolveFixturePath(fileName: string, fixturesDir: string): string {
|
|
|
const normalizedFileName = path.normalize(fileName);
|
|
|
+
|
|
|
+ // 拒绝绝对路径和向上遍历路径
|
|
|
if (normalizedFileName.startsWith("..") || path.isAbsolute(normalizedFileName)) {
|
|
|
- throwError({ operation: "uploadFileToField", target: fileName, suggestion: "use relative path" });
|
|
|
+ throwError({
|
|
|
+ operation: "uploadFileToField",
|
|
|
+ target: fileName,
|
|
|
+ suggestion: "文件名必须是相对于 fixtures 目录的路径,不能使用 '..' 或绝对路径"
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 解析完整路径
|
|
|
+ const resolvedPath = path.resolve(path.join(fixturesDir, normalizedFileName));
|
|
|
+ const resolvedFixturesDir = path.resolve(fixturesDir);
|
|
|
+
|
|
|
+ // 验证解析后的路径在 fixtures 目录内(防止路径遍历攻击)
|
|
|
+ if (!resolvedPath.startsWith(resolvedFixturesDir)) {
|
|
|
+ throwError({
|
|
|
+ operation: "uploadFileToField",
|
|
|
+ target: fileName,
|
|
|
+ suggestion: "文件名路径试图访问 fixtures 目录之外的文件"
|
|
|
+ });
|
|
|
}
|
|
|
- return path.resolve(path.join(fixturesDir, normalizedFileName));
|
|
|
+
|
|
|
+ return resolvedPath;
|
|
|
}
|