mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 12:15:57 +00:00
feat(browser): 增加 Phase 6B mock response 能力
在现有拦截规则之上补齐 fulfill 动作,支持最小状态码、响应头和文本响应体。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
1fbb53c4b1
commit
f133deb539
@@ -477,7 +477,22 @@ function getTools() {
|
||||
pageId: { type: 'string' },
|
||||
urlIncludes: { type: 'string' },
|
||||
method: { type: 'string' },
|
||||
action: { type: 'string', enum: ['continue', 'fail'] },
|
||||
action: { type: 'string', enum: ['continue', 'fail', 'fulfill'] },
|
||||
statusCode: { type: 'integer', minimum: 100, maximum: 599 },
|
||||
headers: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' },
|
||||
value: { type: 'string' },
|
||||
},
|
||||
required: ['name', 'value'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
body: { type: 'string' },
|
||||
contentType: { type: 'string' },
|
||||
},
|
||||
required: ['action'],
|
||||
additionalProperties: false,
|
||||
@@ -801,12 +816,56 @@ function parseOptionalPageId(toolArgs) {
|
||||
}
|
||||
|
||||
function parseInterceptAction(value) {
|
||||
if (value !== 'continue' && value !== 'fail') {
|
||||
throw new Error('action must be one of: continue, fail');
|
||||
if (value !== 'continue' && value !== 'fail' && value !== 'fulfill') {
|
||||
throw new Error('action must be one of: continue, fail, fulfill');
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function parseOptionalStatusCode(value) {
|
||||
if (value === undefined) {
|
||||
return 200;
|
||||
}
|
||||
if (!Number.isInteger(value) || value < 100 || value > 599) {
|
||||
throw new Error('statusCode must be an integer between 100 and 599');
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function parseOptionalHeaders(value, contentType) {
|
||||
const headers = [];
|
||||
if (Array.isArray(value)) {
|
||||
for (const entry of value) {
|
||||
if (!entry || typeof entry !== 'object') {
|
||||
throw new Error('headers entries must be objects with name and value');
|
||||
}
|
||||
const name = requireNonEmptyString(entry.name, 'headers.name');
|
||||
const headerValue = String(entry.value ?? '');
|
||||
headers.push({ name, value: headerValue });
|
||||
}
|
||||
} else if (value !== undefined) {
|
||||
throw new Error('headers must be an array');
|
||||
}
|
||||
if (contentType && !headers.some((header) => header.name.toLowerCase() === 'content-type')) {
|
||||
headers.push({ name: 'Content-Type', value: contentType });
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
function parseOptionalBody(value) {
|
||||
if (value === undefined) {
|
||||
return '';
|
||||
}
|
||||
if (typeof value !== 'string') {
|
||||
throw new Error('body must be a string');
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function encodeFulfillBody(body) {
|
||||
return Buffer.from(body, 'utf8').toString('base64');
|
||||
}
|
||||
|
||||
function parseOptionalMethod(value) {
|
||||
if (value === undefined) {
|
||||
return '';
|
||||
@@ -862,6 +921,8 @@ function formatInterceptRules(rules) {
|
||||
`urlIncludes: ${rule.urlIncludes || '<any>'}`,
|
||||
`method: ${rule.method || '<any>'}`,
|
||||
`action: ${rule.action}`,
|
||||
`statusCode: ${rule.action === 'fulfill' ? rule.statusCode : 'n/a'}`,
|
||||
`contentType: ${rule.action === 'fulfill' ? rule.contentType || '<none>' : 'n/a'}`,
|
||||
].join('\n')
|
||||
)
|
||||
.join('\n---\n');
|
||||
@@ -881,6 +942,7 @@ function formatRecentRequests(entries) {
|
||||
`resourceType: ${entry.resourceType || ''}`,
|
||||
`matchedRuleId: ${entry.matchedRuleId || 'none'}`,
|
||||
`action: ${entry.action}`,
|
||||
`statusCode: ${entry.action === 'fulfill' ? entry.statusCode : 'n/a'}`,
|
||||
].join('\n')
|
||||
)
|
||||
.join('\n---\n');
|
||||
@@ -2574,6 +2636,19 @@ async function ensureInterceptSession(page) {
|
||||
params: { requestId: paused.requestId, errorReason: FETCH_FAIL_ERROR_REASON },
|
||||
})
|
||||
);
|
||||
} else if (action === 'fulfill') {
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
id: ++requestCounter,
|
||||
method: 'Fetch.fulfillRequest',
|
||||
params: {
|
||||
requestId: paused.requestId,
|
||||
responseCode: matchedRule.statusCode,
|
||||
responseHeaders: matchedRule.headers,
|
||||
body: encodeFulfillBody(matchedRule.body),
|
||||
},
|
||||
})
|
||||
);
|
||||
} else {
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
@@ -2591,6 +2666,7 @@ async function ensureInterceptSession(page) {
|
||||
resourceType: String(paused.resourceType || ''),
|
||||
matchedRuleId: matchedRule ? matchedRule.ruleId : '',
|
||||
action,
|
||||
statusCode: action === 'fulfill' ? matchedRule.statusCode : 0,
|
||||
});
|
||||
})();
|
||||
activityChain = activityChain.catch(() => {}).then(() => activity).catch(() => {});
|
||||
@@ -2696,10 +2772,15 @@ async function handleAddInterceptRule(toolArgs) {
|
||||
}
|
||||
const urlIncludes = parseOptionalUrlIncludes(toolArgs.urlIncludes);
|
||||
const method = parseOptionalMethod(toolArgs.method);
|
||||
const action = parseInterceptAction(toolArgs.action);
|
||||
if (!urlIncludes && !method) {
|
||||
throw new Error('urlIncludes or method is required');
|
||||
}
|
||||
const action = parseInterceptAction(toolArgs.action);
|
||||
const statusCode = parseOptionalStatusCode(toolArgs.statusCode);
|
||||
const contentType =
|
||||
toolArgs.contentType === undefined ? '' : requireNonEmptyString(toolArgs.contentType, 'contentType');
|
||||
const body = parseOptionalBody(toolArgs.body);
|
||||
const headers = parseOptionalHeaders(toolArgs.headers, contentType);
|
||||
const rule = {
|
||||
ruleId: createInterceptRuleId(),
|
||||
pageId: page.id,
|
||||
@@ -2707,6 +2788,10 @@ async function handleAddInterceptRule(toolArgs) {
|
||||
urlIncludes,
|
||||
method,
|
||||
action,
|
||||
statusCode: action === 'fulfill' ? statusCode : 0,
|
||||
contentType: action === 'fulfill' ? contentType : '',
|
||||
headers: action === 'fulfill' ? headers : [],
|
||||
body: action === 'fulfill' ? body : '',
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
interceptRules.push(rule);
|
||||
|
||||
Reference in New Issue
Block a user