初始化蓝鸟文件快递柜源码与部署文档
This commit is contained in:
commit
5643d22242
70 files changed
+18669
No files matched your search
@@ -0,0 +1,92 @@
|
||||
import { test as base, expect, type Page, type Cookie } from '@playwright/test'
|
||||
|
||||
// Reuse one administrator login for UI fixtures, preserving the real login
|
||||
// rate limiter and leaving its remaining burst for the full transfer scenario.
|
||||
const test=base.extend<{}, {adminCookies:Cookie[]}>({
|
||||
adminCookies:[async({browser},use)=>{
|
||||
const context=await browser.newContext()
|
||||
try{
|
||||
const response=await context.request.post('http://127.0.0.1:18981/api/v1/admin/login',{headers:{Origin:'http://127.0.0.1:18981'},data:{username:'admin',password:'e2e-secret-password'}})
|
||||
expect(response.status()).toBe(200)
|
||||
await use(await context.cookies())
|
||||
}finally{await context.close()}
|
||||
},{scope:'worker'}]
|
||||
})
|
||||
|
||||
async function adminFixture(page: Page) {
|
||||
const parcelQueries: URLSearchParams[] = [], auditQueries: URLSearchParams[] = []
|
||||
await page.route('**/api/v1/admin/parcels?**', async route => {
|
||||
const q = new URL(route.request().url()).searchParams; parcelQueries.push(q)
|
||||
const size = Number(q.get('page_size') ?? 10), total = q.get('q') ? 1 : 23, pages = Math.max(1, Math.ceil(total / size)), current = Math.min(Number(q.get('page') ?? 1), pages)
|
||||
const items = Array.from({length: Math.min(size, total-(current-1)*size)}, (_,i) => {
|
||||
const n = (current-1)*size+i+1
|
||||
return { id: n.toString(16).padStart(32,'0'), state:'ready', ip:'203.0.113.1', created:1791187200, expires:1791792000, count:1, size:12, files:[{id:'file-'+n,name:'样例文件-'+n+'.txt',size:12,status:'ready',remaining:5,expires:1791792000}] }
|
||||
})
|
||||
await route.fulfill({json:{items,total,page:current,page_size:size,pages}})
|
||||
})
|
||||
await page.route('**/api/v1/admin/audit?**', async route => {
|
||||
const q = new URL(route.request().url()).searchParams; auditQueries.push(q)
|
||||
const size = Number(q.get('page_size') ?? 20), total = q.get('source') ? (q.get('source')==='no-match' ? 0 : 2) : 100000, pages = Math.max(1,Math.ceil(total/size)), current = Math.min(Number(q.get('page') ?? 1),pages)
|
||||
const items = Array.from({length:Math.min(size,Math.max(0,total-(current-1)*size))}, (_,i) => ({id:total-(current-1)*size-i,created:1791187200,action:q.get('action')||'admin.login',target:q.get('target')||'audit-'+(total-(current-1)*size-i),source:q.get('source')||'203.0.113.1'}))
|
||||
await route.fulfill({json:{items,total,page:current,page_size:size,pages}})
|
||||
})
|
||||
await page.goto('/#/admin')
|
||||
await expect(page.getByRole('tab',{name:'文件管理'})).toBeVisible()
|
||||
await expect(page.getByRole('navigation',{name:'文件管理分页'})).toContainText('共 23 个包裹')
|
||||
return {parcelQueries,auditQueries}
|
||||
}
|
||||
|
||||
for(const mobile of [false,true]) {
|
||||
test(`admin tabs expand naturally and lists paginate/filter (${mobile?'mobile':'desktop'})`,async({page,adminCookies})=>{
|
||||
await page.context().addCookies(adminCookies)
|
||||
if(mobile)await page.setViewportSize({width:390,height:844})
|
||||
const errors:string[]=[];page.on('pageerror',e=>errors.push(e.message))
|
||||
const {parcelQueries,auditQueries}=await adminFixture(page)
|
||||
const pager=page.getByRole('navigation',{name:'文件管理分页'})
|
||||
await expect(page.locator('.admin-parcel:visible')).toHaveCount(10)
|
||||
await pager.getByRole('button',{name:'上一页',exact:true}).isDisabled().then(v=>expect(v).toBeTruthy())
|
||||
await pager.getByRole('button',{name:'下一页',exact:true}).click()
|
||||
await expect(pager).toContainText('第 2 / 3 页');await expect(page.getByText('样例文件-11.txt',{exact:true})).toBeVisible()
|
||||
expect(parcelQueries.at(-1)?.get('page')).toBe('2')
|
||||
await pager.getByRole('button',{name:'下一页',exact:true}).click()
|
||||
await expect(page.locator('.admin-parcel:visible')).toHaveCount(3)
|
||||
await expect(pager.getByRole('button',{name:'下一页',exact:true})).toBeDisabled()
|
||||
await page.getByLabel('文件名或包裹 ID',{exact:true}).fill('样例文件-2')
|
||||
await page.locator('.parcel-filters').getByRole('button',{name:'搜索',exact:true}).click()
|
||||
await expect(pager).toContainText('共 1 个包裹');await expect(pager).toContainText('第 1 / 1 页')
|
||||
expect(parcelQueries.at(-1)?.get('q')).toBe('样例文件-2')
|
||||
await expect(pager.getByRole('button',{name:'下一页',exact:true})).toBeDisabled()
|
||||
await page.locator('.parcel-filters').getByRole('button',{name:'重置',exact:true}).click()
|
||||
await expect(pager).toContainText('共 23 个包裹')
|
||||
for(const name of ['文件管理','服务策略','容量与保留','操作审计']){
|
||||
await page.getByRole('tab',{name,exact:true}).click()
|
||||
expect(await page.locator('.admin-panel:visible .admin-scroll').evaluate(node=>node.scrollHeight<=node.clientHeight+1)).toBeTruthy()
|
||||
expect(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth)).toBeTruthy()
|
||||
}
|
||||
const auditPager=page.getByRole('navigation',{name:'操作审计分页'})
|
||||
await expect(page.locator('.audit-table tbody tr')).toHaveCount(20)
|
||||
await expect(auditPager).toContainText('共 100000 条')
|
||||
await expect(auditPager).toContainText('第 1 / 5000 页')
|
||||
await auditPager.getByRole('button',{name:'下一页',exact:true}).click()
|
||||
await expect(auditPager).toContainText('第 2 / 5000 页');expect(auditQueries.at(-1)?.get('page')).toBe('2')
|
||||
await page.getByLabel('操作审计分页跳转页码',{exact:true}).fill('5000')
|
||||
await auditPager.getByRole('button',{name:'跳转',exact:true}).click()
|
||||
await expect(auditPager).toContainText('第 5000 / 5000 页');await expect(auditPager.getByRole('button',{name:'下一页',exact:true})).toBeDisabled()
|
||||
await page.getByLabel('开始时间',{exact:true}).fill('2026-10-05T00:00');await page.getByLabel('结束时间',{exact:true}).fill('2026-10-06T00:00')
|
||||
await page.getByLabel('来源 IP',{exact:true}).fill('203.0.113')
|
||||
await page.getByLabel('目标标识',{exact:true}).fill('target-fragment')
|
||||
await page.locator('.audit-filters .n-base-selection').click()
|
||||
await page.getByText('删除包裹 · admin.delete',{exact:true}).click()
|
||||
await page.locator('.audit-filters').getByRole('button',{name:'搜索',exact:true}).click()
|
||||
await expect(auditPager).toContainText('共 2 条');await expect(auditPager).toContainText('第 1 / 1 页')
|
||||
const query=auditQueries.at(-1)!;expect(query.get('action')).toBe('admin.delete');expect(query.get('source')).toBe('203.0.113');expect(query.get('target')).toBe('target-fragment');expect(query.get('from')).toBe(await page.evaluate(()=>String(new Date('2026-10-05T00:00').getTime()/1000)));expect(query.get('to')).toBe(await page.evaluate(()=>String(new Date('2026-10-06T00:00').getTime()/1000)))
|
||||
await expect(page.locator('.audit-table tbody tr')).toHaveCount(2)
|
||||
await page.getByLabel('来源 IP',{exact:true}).fill('no-match');await page.locator('.audit-filters').getByRole('button',{name:'搜索',exact:true}).click()
|
||||
await expect(page.getByText('没有符合条件的审计记录。',{exact:true})).toBeVisible();await expect(auditPager).toContainText('共 0 条');await expect(auditPager.getByRole('button',{name:'下一页',exact:true})).toBeDisabled()
|
||||
await page.locator('.audit-filters').getByRole('button',{name:'重置',exact:true}).click()
|
||||
await expect(auditPager).toContainText('共 100000 条');expect(auditQueries.at(-1)?.has('source')).toBeFalsy()
|
||||
await page.getByLabel('操作审计分页每页条数',{exact:true}).selectOption('50');await expect(page.locator('.audit-table tbody tr')).toHaveCount(50);await expect(auditPager).toContainText('第 1 / 2000 页')
|
||||
|
||||
expect(errors).toEqual([])
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user