mirror of
https://github.com/yaklang/yaklang-chrome-extension.git
synced 2026-09-22 03:10:43 +08:00
* feat(proxy): reflect and release browser proxy control * feat(browser): report browser product identity
103 lines
6.4 KiB
JavaScript
103 lines
6.4 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { mkdtemp, mkdir, rm } from 'node:fs/promises';
|
|
import { tmpdir } from 'node:os';
|
|
import { resolve, join } from 'node:path';
|
|
import { chromium } from 'playwright-core';
|
|
import { resolveChromiumPath } from './resolve-chromium.mjs';
|
|
|
|
// Uses an isolated profile; never changes the user's browser or system proxy.
|
|
const profile = await mkdtemp(join(tmpdir(), 'yakit-proxy-control-'));
|
|
const extension = resolve('.output/chrome-mv3');
|
|
const context = await chromium.launchPersistentContext(profile, {
|
|
executablePath: await resolveChromiumPath(), headless: true,
|
|
viewport: { width: 390, height: 640 }, reducedMotion: 'reduce',
|
|
args: [`--disable-extensions-except=${extension}`, `--load-extension=${extension}`, '--proxy-server=http://127.0.0.1:18083'],
|
|
});
|
|
try {
|
|
const worker = context.serviceWorkers()[0] || await context.waitForEvent('serviceworker');
|
|
const id = new URL(worker.url()).host;
|
|
const page = await context.newPage();
|
|
await page.goto(`chrome-extension://${id}/ytray-bootstrap.html?manager=ytray&instanceId=proxy-test&badge=A&startupProxy=${encodeURIComponent('http://127.0.0.1:18083')}&target=chrome://version`);
|
|
await page.waitForURL('chrome://version/');
|
|
await page.goto(`chrome-extension://${id}/options.html`);
|
|
const call = async (action, payload) => {
|
|
const response = await page.evaluate(({ action, payload }) => chrome.runtime.sendMessage({ action, payload }), { action, payload });
|
|
assert.equal(response.ok, true, response.error);
|
|
return response.data;
|
|
};
|
|
const launch = await call('proxy.status');
|
|
assert.equal((await call('state.get')).startupProxy, 'http://127.0.0.1:18083');
|
|
assert.equal(launch.followingStartup, true);
|
|
assert.equal(launch.control, 'controllable_by_this_extension');
|
|
assert.equal(launch.activeProfileId, undefined);
|
|
assert.match(launch.label, /18083/);
|
|
await call('proxy.switch', { id: 'direct' });
|
|
assert.equal((await call('proxy.status')).activeProfileId, 'direct');
|
|
await call('proxy.switch', { id: 'yakit-mitm' });
|
|
assert.equal((await call('proxy.status')).activeProfileId, 'yakit-mitm');
|
|
await call('proxy.auto.apply');
|
|
assert.equal((await call('proxy.status')).activeProfileId, 'auto');
|
|
await call('proxy.switch', { id: 'system' });
|
|
assert.equal((await call('proxy.status')).activeProfileId, 'system');
|
|
await call('proxy.release');
|
|
assert.deepEqual(await call('proxy.status'), launch);
|
|
await page.goto(`chrome-extension://${id}/popup.html`);
|
|
await page.getByRole('button', { name: '代理', exact: true }).click();
|
|
await page.getByRole('status').filter({ hasText: '实际代理' }).getByText('http://127.0.0.1:18083', { exact: true }).waitFor();
|
|
assert.equal(await page.getByRole('radio', { name: /直接连接/ }).getAttribute('aria-checked'), 'false');
|
|
const follow = page.getByRole('radio', { name: /跟随启动配置/ });
|
|
assert.equal(await follow.getAttribute('aria-checked'), 'true');
|
|
await page.evaluate(() => {
|
|
const startup = document.querySelector('.startup-proxy-option');
|
|
const ordinary = document.querySelector('.popup-proxy-list > button');
|
|
for (const [a, b] of [[startup.querySelector('.startup-proxy-icon'), ordinary.querySelector('.popup-mode-icon')], [startup.querySelector('strong'), ordinary.querySelector('strong')], [startup.querySelector('small'), ordinary.querySelector('small')]]) {
|
|
if (Math.abs(a.getBoundingClientRect().x - b.getBoundingClientRect().x) > 1) throw new Error('Proxy mode columns are not aligned');
|
|
}
|
|
});
|
|
await page.emulateMedia({ reducedMotion: 'no-preference' });
|
|
await page.getByRole('radio', { name: /直接连接/ }).click();
|
|
await page.locator('.popup-global-notice').waitFor();
|
|
await page.evaluate(() => {
|
|
const notice = document.querySelector('.popup-global-notice');
|
|
const animation = notice.getAnimations()[0];
|
|
if (!animation) throw new Error('Expected notice entrance animation');
|
|
animation.pause();
|
|
for (const time of [0, 40, 80, 159, 200]) {
|
|
animation.currentTime = time;
|
|
const rect = notice.getBoundingClientRect();
|
|
const parent = notice.offsetParent.getBoundingClientRect();
|
|
if (Math.abs(rect.x + rect.width / 2 - (parent.x + parent.width / 2)) > 1) {
|
|
throw new Error(`Notice is not centered at animation time ${time}ms`);
|
|
}
|
|
}
|
|
animation.finish();
|
|
});
|
|
await page.emulateMedia({ reducedMotion: 'reduce' });
|
|
await page.waitForFunction(() => document.querySelector('[aria-label="实际代理状态"]')?.textContent === '实际代理直接连接');
|
|
assert.equal(await follow.getAttribute('aria-checked'), 'false');
|
|
await follow.click();
|
|
await page.waitForFunction(() => document.querySelector('.startup-proxy-option [role="radio"]')?.getAttribute('aria-checked') === 'true');
|
|
assert.deepEqual(await call('proxy.status'), launch);
|
|
await page.getByRole('button', { name: '解释跟随启动配置' }).focus();
|
|
await page.getByRole('tooltip').waitFor();
|
|
assert.match(await page.getByRole('tooltip').innerText(), /切换后使用浏览器启动时的网络配置/);
|
|
assert.doesNotMatch(await page.getByRole('tooltip').innerText(), /清除|接管/);
|
|
await page.getByRole('radio', { name: /跟随启动配置/ }).focus();
|
|
await page.mouse.move(4, 4);
|
|
await mkdir('.artifacts/proxy', { recursive: true });
|
|
await page.screenshot({ path: '.artifacts/proxy/launch-proxy.png' });
|
|
// External settings changes must update an already-open view, without storage mutations.
|
|
await page.evaluate(() => chrome.proxy.settings.set({ scope: 'regular', value: { mode: 'direct' } }));
|
|
await page.getByRole('status').filter({ hasText: '实际代理' }).getByText('直接连接', { exact: true }).waitFor();
|
|
assert.equal(await page.getByRole('radio', { name: /直接连接/ }).getAttribute('aria-checked'), 'false');
|
|
await page.goto(`chrome-extension://${id}/ytray-bootstrap.html?manager=ytray&instanceId=direct-test&badge=A&startupProxy=direct&target=chrome://version`);
|
|
await page.waitForURL('chrome://version/');
|
|
await page.goto(`chrome-extension://${id}/popup.html`);
|
|
await page.getByRole('button', { name: '代理', exact: true }).click();
|
|
assert.equal(await page.getByRole('radio', { name: /跟随启动配置/ }).count(), 0);
|
|
console.log('PASS: launch proxy → direct → fixed → PAC → system → release; live status; stale selection not marked active.');
|
|
} finally {
|
|
await context.close();
|
|
await rm(profile, { recursive: true, force: true });
|
|
}
|