feat: complete unified plugin admin v1.1.0
This commit is contained in:
@@ -8,7 +8,9 @@ const outputDir = process.env.PLUGIN_SCREENSHOT_DIR || '.playwright-cli/subscrip
|
||||
|
||||
await fs.mkdir(path.resolve(outputDir), { recursive: true })
|
||||
|
||||
const browser = await chromium.launch({ headless: true })
|
||||
const launchOptions = { headless: true }
|
||||
if (process.env.PLAYWRIGHT_EXECUTABLE_PATH) launchOptions.executablePath = process.env.PLAYWRIGHT_EXECUTABLE_PATH
|
||||
const browser = await chromium.launch(launchOptions)
|
||||
try {
|
||||
for (const width of [425, 900, 1440]) {
|
||||
const page = await browser.newPage({ viewport: { width, height: 900 }, deviceScaleFactor: 1 })
|
||||
@@ -21,18 +23,12 @@ try {
|
||||
} catch (_) {}
|
||||
})
|
||||
await page.goto(entry, { waitUntil: 'domcontentloaded' })
|
||||
await page.getByLabel('管理员邮箱').fill('admin@example.com')
|
||||
await page.getByLabel('密码').fill('password')
|
||||
await page.getByRole('button', { name: '登录' }).click()
|
||||
await page.locator('#app-view').waitFor({ state: 'visible' })
|
||||
await page.getByRole('heading', { name: '订阅管理已纳入统一控制面' }).waitFor()
|
||||
if (await page.locator('input[type="password"], form').count()) throw new Error('subscription module exposed an independent login form')
|
||||
await page.waitForTimeout(50)
|
||||
if (responseLeaks.length) throw new Error(`sensitive response field exposed at ${width}px: ${responseLeaks.join(', ')}`)
|
||||
await page.getByRole('button', { name: '用户订阅' }).click()
|
||||
await page.locator('#subscriptions-list table').waitFor()
|
||||
await page.getByRole('button', { name: '下一页' }).click()
|
||||
await page.getByRole('button', { name: '概览' }).click()
|
||||
await page.reload({ waitUntil: 'domcontentloaded' })
|
||||
await page.locator('#app-view').waitFor({ state: 'visible' })
|
||||
await page.getByRole('heading', { name: '订阅管理已纳入统一控制面' }).waitFor()
|
||||
const overflow = await page.evaluate(() => document.documentElement.scrollWidth > window.innerWidth)
|
||||
if (overflow) throw new Error(`horizontal overflow at ${width}px`)
|
||||
await page.screenshot({ path: path.join(outputDir, `subscription-admin-${width}.png`), fullPage: true })
|
||||
|
||||
@@ -3,6 +3,7 @@ import http from 'node:http'
|
||||
const port = Number(process.env.MOCK_CORE_PORT || 18080)
|
||||
const token = process.env.MOCK_ACCESS_TOKEN || 'MOCK-ACCESS'
|
||||
const refresh = process.env.MOCK_REFRESH_TOKEN || 'MOCK-REFRESH'
|
||||
const captchaProvider = String(process.env.MOCK_CAPTCHA_PROVIDER || '').toLowerCase()
|
||||
const users = new Map([
|
||||
['admin@example.com', { id: 1, role: 'admin', email: 'admin@example.com', username: 'admin' }],
|
||||
['user@example.com', { id: 2, role: 'user', email: 'user@example.com', username: 'user' }]
|
||||
@@ -47,6 +48,9 @@ const server = http.createServer(async (req, res) => {
|
||||
const input = await body(req)
|
||||
const user = users.get(input.email)
|
||||
if (!user || input.password !== 'password') return json(res, 401, { code: 401, message: 'invalid credentials' })
|
||||
if (captchaProvider === 'turnstile' && input.turnstile_token !== 'MOCK-TURNSTILE-TOKEN') return json(res, 400, { code: 400, message: 'captcha required' })
|
||||
if (captchaProvider === 'tencent' && (!input.tencent_captcha_ticket || !input.tencent_captcha_randstr)) return json(res, 400, { code: 400, message: 'captcha required' })
|
||||
if (captchaProvider === 'aliyun' && !input.turnstile_token) return json(res, 400, { code: 400, message: 'captcha required' })
|
||||
return json(res, 200, { code: 0, message: 'success', data: { access_token: token, refresh_token: refresh, user } })
|
||||
}
|
||||
if (req.method === 'POST' && path === '/api/v1/auth/refresh') {
|
||||
@@ -55,7 +59,12 @@ const server = http.createServer(async (req, res) => {
|
||||
return json(res, 200, { code: 0, message: 'success', data: { access_token: 'NEW-MOCK-ACCESS', refresh_token: 'NEW-MOCK-REFRESH' } })
|
||||
}
|
||||
if (req.method === 'POST' && path === '/api/v1/auth/logout') return json(res, 200, { code: 0, message: 'success', data: {} })
|
||||
if (req.method === 'GET' && path === '/api/v1/settings/public') return json(res, 200, { code: 0, message: 'success', data: { turnstile_enabled: false, geetest_captcha_enabled: false, tencent_captcha_enabled: false, aliyun_captcha_enabled: false } })
|
||||
if (req.method === 'GET' && path === '/api/v1/settings/public') return json(res, 200, { code: 0, message: 'success', data: {
|
||||
turnstile_enabled: captchaProvider === 'turnstile', turnstile_site_key: captchaProvider === 'turnstile' ? 'mock-site-key' : '',
|
||||
geetest_captcha_enabled: false,
|
||||
tencent_captcha_enabled: captchaProvider === 'tencent', tencent_captcha_app_id: captchaProvider === 'tencent' ? '123456' : '', tencent_captcha_region: 'cn',
|
||||
aliyun_captcha_enabled: captchaProvider === 'aliyun', aliyun_captcha_scene_id: captchaProvider === 'aliyun' ? 'mock-scene' : '', aliyun_captcha_prefix: captchaProvider === 'aliyun' ? 'mock-prefix' : '', aliyun_captcha_region: 'cn'
|
||||
} })
|
||||
if (!authorized(req)) return json(res, 401, { code: 401, message: 'unauthorized' })
|
||||
if (req.method === 'GET' && path === '/api/v1/auth/me') return json(res, 200, { code: 0, message: 'success', data: users.get('admin@example.com') })
|
||||
if (req.method === 'GET' && path === '/api/v1/admin/payment/plans') {
|
||||
|
||||
@@ -8,14 +8,22 @@ cleanup() {
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
MOCK_CORE_PORT=18080 node "$ROOT/test/mock-core.mjs" >/tmp/subscription-admin-mock-core.log 2>&1 & CORE_PID=$!
|
||||
CORE_BASE_URL=http://127.0.0.1:18080 \
|
||||
PLUGIN_HOST=127.0.0.1 \
|
||||
PLUGIN_PORT=18082 \
|
||||
PLUGIN_PUBLIC_BASE_PATH=/extensions/qiu.subscription-admin \
|
||||
PLUGIN_COOKIE_PATH=/extensions/qiu.subscription-admin/ \
|
||||
PLUGIN_COOKIE_SECURE=false \
|
||||
go run "$ROOT" >/tmp/subscription-admin-plugin.log 2>&1 & PLUGIN_PID=$!
|
||||
(
|
||||
export CORE_BASE_URL=http://127.0.0.1:18080
|
||||
export PLUGIN_HOST=127.0.0.1
|
||||
export PLUGIN_PORT=18082
|
||||
export PLUGIN_PUBLIC_BASE_PATH=/extensions/qiu.subscription-admin
|
||||
export PLUGIN_COOKIE_PATH=/extensions/qiu.subscription-admin/
|
||||
export PLUGIN_COOKIE_SECURE=false
|
||||
cd "$ROOT"
|
||||
go run .
|
||||
) >/tmp/subscription-admin-plugin.log 2>&1 & PLUGIN_PID=$!
|
||||
PLUGIN_TARGET_PORT=18082 MOCK_PROXY_PORT=18081 node "$ROOT/test/mock-proxy.mjs" >/tmp/subscription-admin-mock-proxy.log 2>&1 & PROXY_PID=$!
|
||||
|
||||
sleep 2
|
||||
for attempt in $(seq 1 30); do
|
||||
if curl -fsS http://127.0.0.1:18081/extensions/qiu.subscription-admin/healthz >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 0.2
|
||||
done
|
||||
PLUGIN_BROWSER_ORIGIN=http://127.0.0.1:18081 PLUGIN_SCREENSHOT_DIR="$ROOT/.screenshots/subscription-admin" node "$ROOT/test/browser-check.mjs"
|
||||
|
||||
Reference in New Issue
Block a user