feat: 添加 Kiro 授权流程的 cookie 清理和重试机制,优化跨页面恢复
This commit is contained in:
@@ -54,7 +54,28 @@ test('kiro start device login opens the auth tab and waits for the email entry p
|
||||
const contentReadyCalls = [];
|
||||
const contentMessages = [];
|
||||
const stableWaitCalls = [];
|
||||
const removedCookies = [];
|
||||
const browsingDataCalls = [];
|
||||
const { chrome, updates: tabUpdates } = createChromeRecorder();
|
||||
chrome.cookies = {
|
||||
getAllCookieStores: async () => [{ id: 'store-a' }],
|
||||
getAll: async () => [
|
||||
{ domain: '.view.awsapps.com', path: '/start', name: 'awsapps', storeId: 'store-a' },
|
||||
{ domain: '.oidc.us-east-1.amazonaws.com', path: '/', name: 'oidc', storeId: 'store-a' },
|
||||
{ domain: '.signin.aws', path: '/', name: 'signin', storeId: 'store-a' },
|
||||
{ domain: '.profile.aws.amazon.com', path: '/', name: 'profile-amazon', storeId: 'store-a' },
|
||||
{ domain: '.example.com', path: '/', name: 'keep', storeId: 'store-a' },
|
||||
],
|
||||
remove: async (details) => {
|
||||
removedCookies.push(details);
|
||||
return details;
|
||||
},
|
||||
};
|
||||
chrome.browsingData = {
|
||||
removeCookies: async (details) => {
|
||||
browsingDataCalls.push(details);
|
||||
},
|
||||
};
|
||||
|
||||
const executor = api.createKiroDeviceAuthExecutor({
|
||||
addLog: async () => {},
|
||||
@@ -132,6 +153,40 @@ test('kiro start device login opens the auth tab and waits for the email entry p
|
||||
nodeId: 'kiro-start-device-login',
|
||||
});
|
||||
|
||||
assert.deepEqual(removedCookies, [
|
||||
{
|
||||
url: 'https://view.awsapps.com/start',
|
||||
name: 'awsapps',
|
||||
storeId: 'store-a',
|
||||
},
|
||||
{
|
||||
url: 'https://oidc.us-east-1.amazonaws.com/',
|
||||
name: 'oidc',
|
||||
storeId: 'store-a',
|
||||
},
|
||||
{
|
||||
url: 'https://signin.aws/',
|
||||
name: 'signin',
|
||||
storeId: 'store-a',
|
||||
},
|
||||
{
|
||||
url: 'https://profile.aws.amazon.com/',
|
||||
name: 'profile-amazon',
|
||||
storeId: 'store-a',
|
||||
},
|
||||
]);
|
||||
assert.deepEqual(browsingDataCalls, [{
|
||||
since: 0,
|
||||
origins: [
|
||||
'https://view.awsapps.com',
|
||||
'https://login.awsapps.com',
|
||||
'https://oidc.us-east-1.amazonaws.com',
|
||||
'https://signin.aws',
|
||||
'https://signin.aws.amazon.com',
|
||||
'https://profile.aws',
|
||||
'https://profile.aws.amazon.com',
|
||||
],
|
||||
}]);
|
||||
assert.equal(fetchCalls.length, 2);
|
||||
assert.equal(fetchCalls[0].url, 'https://oidc.us-east-1.amazonaws.com/client/register');
|
||||
assert.equal(fetchCalls[1].url, 'https://oidc.us-east-1.amazonaws.com/device_authorization');
|
||||
|
||||
@@ -152,6 +152,60 @@ test('tab runtime gives step 5 profile submit enough response time for slow page
|
||||
);
|
||||
});
|
||||
|
||||
test('tab runtime replays retryable transport recovery hook and surfaces a localized timeout error', async () => {
|
||||
const source = fs.readFileSync('background/tab-runtime.js', 'utf8');
|
||||
const globalScope = {};
|
||||
const api = new Function('self', `${source}; return self.MultiPageBackgroundTabRuntime;`)(globalScope);
|
||||
|
||||
let recoveryCalls = 0;
|
||||
const runtime = api.createTabRuntime({
|
||||
LOG_PREFIX: '[test]',
|
||||
addLog: async () => {},
|
||||
chrome: {
|
||||
tabs: {
|
||||
get: async () => ({
|
||||
id: 9,
|
||||
windowId: 1,
|
||||
url: 'https://profile.aws.amazon.com/complete',
|
||||
status: 'complete',
|
||||
}),
|
||||
query: async () => [],
|
||||
sendMessage: async () => {
|
||||
throw new Error('Could not establish connection. Receiving end does not exist.');
|
||||
},
|
||||
},
|
||||
},
|
||||
getSourceLabel: () => 'Kiro 授权页',
|
||||
getState: async () => ({
|
||||
tabRegistry: {
|
||||
'kiro-device-auth': { tabId: 9, ready: true },
|
||||
},
|
||||
sourceLastUrls: {},
|
||||
}),
|
||||
isRetryableContentScriptTransportError: (error) => /Receiving end does not exist/i.test(String(error?.message || error || '')),
|
||||
matchesSourceUrlFamily: () => false,
|
||||
setState: async () => {},
|
||||
sleepWithStop: async () => {},
|
||||
throwIfStopped: () => {},
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
runtime.sendToContentScriptResilient('kiro-device-auth', {
|
||||
type: 'ENSURE_KIRO_PAGE_STATE',
|
||||
payload: {},
|
||||
}, {
|
||||
timeoutMs: 5,
|
||||
retryDelayMs: 0,
|
||||
onRetryableError: async () => {
|
||||
recoveryCalls += 1;
|
||||
},
|
||||
}),
|
||||
/页面刚完成跳转或刷新,内容脚本还没有重新接回/
|
||||
);
|
||||
|
||||
assert.equal(recoveryCalls > 0, true);
|
||||
});
|
||||
|
||||
test('tab runtime waitForTabComplete waits until tab status becomes complete', async () => {
|
||||
const source = fs.readFileSync('background/tab-runtime.js', 'utf8');
|
||||
const globalScope = {};
|
||||
|
||||
@@ -21,6 +21,10 @@ test('flow registry exposes openai and kiro with canonical source metadata', ()
|
||||
assert.equal(flowRegistry.normalizeFlowId('codex'), 'openai');
|
||||
assert.equal(flowRegistry.normalizeSourceId('openai', 'sub2api'), 'sub2api');
|
||||
assert.equal(flowRegistry.normalizeSourceId('kiro', 'anything-else'), 'kiro-rs');
|
||||
assert.deepEqual(
|
||||
flowRegistry.getVisibleGroupIds('openai', 'cpa'),
|
||||
['openai-plus', 'openai-phone', 'openai-oauth', 'openai-step6', 'openai-source-cpa', 'service-account', 'service-email', 'service-proxy']
|
||||
);
|
||||
assert.deepEqual(
|
||||
flowRegistry.getVisibleGroupIds('kiro', 'kiro-rs'),
|
||||
['kiro-runtime-status', 'kiro-source-kiro-rs', 'service-account', 'service-email', 'service-proxy']
|
||||
@@ -33,6 +37,10 @@ test('flow registry exposes openai and kiro with canonical source metadata', ()
|
||||
flowRegistry.getSettingsGroupDefinition('openai-phone')?.rowIds || [],
|
||||
[]
|
||||
);
|
||||
assert.deepEqual(
|
||||
flowRegistry.getSettingsGroupDefinition('openai-step6')?.rowIds || [],
|
||||
['row-step6-cookie-settings']
|
||||
);
|
||||
});
|
||||
|
||||
test('settings schema normalizes flat input into canonical flow and service namespaces', () => {
|
||||
|
||||
@@ -37,6 +37,7 @@ test('sidepanel html exposes flow selector and kiro source fields', () => {
|
||||
[
|
||||
'id="select-flow"',
|
||||
'id="label-source-selector"',
|
||||
'id="row-step6-cookie-settings"',
|
||||
'id="row-kiro-rs-url"',
|
||||
'id="row-kiro-rs-key"',
|
||||
'id="row-kiro-device-code"',
|
||||
|
||||
@@ -42,16 +42,19 @@ test('sidepanel no longer exposes operation delay switch and places step executi
|
||||
assert.doesNotMatch(html, /id="row-operation-delay-settings"/);
|
||||
assert.doesNotMatch(html, /id="input-operation-delay-enabled"/);
|
||||
|
||||
const step6CookieIndex = html.indexOf('id="row-step6-cookie-settings"');
|
||||
const autoDelayIndex = html.indexOf('id="row-auto-delay-settings"');
|
||||
const oauthTimeoutIndex = html.indexOf('id="row-oauth-flow-timeout"');
|
||||
const stepRangeIndex = html.indexOf('id="row-step-execution-range"');
|
||||
const oauthDisplayIndex = html.indexOf('id="row-oauth-display"');
|
||||
|
||||
assert.notEqual(step6CookieIndex, -1);
|
||||
assert.notEqual(autoDelayIndex, -1);
|
||||
assert.notEqual(oauthTimeoutIndex, -1);
|
||||
assert.notEqual(stepRangeIndex, -1);
|
||||
assert.notEqual(oauthDisplayIndex, -1);
|
||||
assert.ok(stepRangeIndex > autoDelayIndex, 'step execution range should still remain below the step6 toggle row');
|
||||
assert.ok(autoDelayIndex > step6CookieIndex, 'startup delay row should render below the openai step6 cookie row');
|
||||
assert.ok(stepRangeIndex > autoDelayIndex, 'step execution range should still remain below the startup delay row');
|
||||
assert.ok(stepRangeIndex > oauthTimeoutIndex, 'step execution range should render below oauth timeout');
|
||||
assert.ok(stepRangeIndex < oauthDisplayIndex, 'step execution range should stay above oauth runtime display');
|
||||
});
|
||||
|
||||
@@ -22,6 +22,25 @@ test('manifest loads shared source registry before content utils in static bundl
|
||||
}
|
||||
});
|
||||
|
||||
test('manifest ships a static Kiro auth bundle for cross-page recovery', () => {
|
||||
const manifest = JSON.parse(fs.readFileSync('manifest.json', 'utf8'));
|
||||
const kiroEntry = (manifest.content_scripts || []).find((entry) => {
|
||||
const matches = Array.isArray(entry.matches) ? entry.matches : [];
|
||||
return matches.includes('https://view.awsapps.com/*')
|
||||
&& matches.includes('https://signin.aws/*')
|
||||
&& matches.includes('https://signin.aws.amazon.com/*')
|
||||
&& matches.includes('https://profile.aws/*')
|
||||
&& matches.includes('https://profile.aws.amazon.com/*');
|
||||
});
|
||||
|
||||
assert.ok(kiroEntry, 'missing static Kiro auth content script entry');
|
||||
assert.deepEqual(kiroEntry.js, [
|
||||
'shared/source-registry.js',
|
||||
'content/utils.js',
|
||||
'content/kiro-device-auth-page.js',
|
||||
]);
|
||||
});
|
||||
|
||||
test('shared source registry exposes canonical source, alias, detection, and ready policies', () => {
|
||||
const flowRegistrySource = fs.readFileSync('shared/flow-registry.js', 'utf8');
|
||||
const source = fs.readFileSync('shared/source-registry.js', 'utf8');
|
||||
@@ -57,6 +76,54 @@ test('shared source registry exposes canonical source, alias, detection, and rea
|
||||
url: 'https://view.awsapps.com/start',
|
||||
hostname: 'view.awsapps.com',
|
||||
}), 'kiro-device-auth');
|
||||
assert.equal(registry.detectSourceFromLocation({
|
||||
url: 'https://signin.aws/register',
|
||||
hostname: 'signin.aws',
|
||||
}), 'kiro-device-auth');
|
||||
assert.equal(registry.detectSourceFromLocation({
|
||||
url: 'https://profile.aws/complete',
|
||||
hostname: 'profile.aws',
|
||||
}), 'kiro-device-auth');
|
||||
assert.equal(registry.detectSourceFromLocation({
|
||||
url: 'https://signin.aws.amazon.com/register',
|
||||
hostname: 'signin.aws.amazon.com',
|
||||
}), 'kiro-device-auth');
|
||||
assert.equal(registry.detectSourceFromLocation({
|
||||
url: 'https://profile.aws.amazon.com/complete',
|
||||
hostname: 'profile.aws.amazon.com',
|
||||
}), 'kiro-device-auth');
|
||||
assert.equal(
|
||||
registry.matchesSourceUrlFamily(
|
||||
'kiro-device-auth',
|
||||
'https://signin.aws/register',
|
||||
'https://view.awsapps.com/start'
|
||||
),
|
||||
true
|
||||
);
|
||||
assert.equal(
|
||||
registry.matchesSourceUrlFamily(
|
||||
'kiro-device-auth',
|
||||
'https://profile.aws/complete',
|
||||
'https://signin.aws/register'
|
||||
),
|
||||
true
|
||||
);
|
||||
assert.equal(
|
||||
registry.matchesSourceUrlFamily(
|
||||
'kiro-device-auth',
|
||||
'https://profile.aws.amazon.com/complete',
|
||||
'https://signin.aws.amazon.com/register'
|
||||
),
|
||||
true
|
||||
);
|
||||
assert.equal(
|
||||
registry.matchesSourceUrlFamily(
|
||||
'kiro-device-auth',
|
||||
'https://oidc.us-east-1.amazonaws.com/authorize',
|
||||
'https://view.awsapps.com/start'
|
||||
),
|
||||
true
|
||||
);
|
||||
assert.equal(registry.shouldReportReadyForFrame('mail-163', true), false);
|
||||
assert.equal(registry.shouldReportReadyForFrame('unknown-source', false), false);
|
||||
assert.equal(registry.getCleanupOwnerSource('oauth-localhost-callback'), 'openai-auth');
|
||||
|
||||
Reference in New Issue
Block a user