Why: the 8788 project-member runtime delivery finished (code merged via #526/#530) but its card and operation records lived only on an unpushed task branch, leaving the worktree impossible to recycle and the record invisible on main. Evidence: card status "已完成(运行目标)" on feature/project-member-runtime 835d807; branch diff against merge-base touches only .task (card + 29 operation records); credential scan finds only variable-based Authorization headers and sha256 digests. Impact: adds .task/archive/feature-project-member-runtime.md and .task/artifacts/project-member-runtime/**; no code, gitlink or policy change. Refs: worktree cleanup 2026-10-18 Co-authored-by: Cursor <cursoragent@cursor.com>
67 lines
4.9 KiB
JavaScript
67 lines
4.9 KiB
JavaScript
import { chromium, expect } from '/home/weichen/projects/Agent-System-Dev-wt-feature-project-member-access/modules/frontend/node_modules/@playwright/test/index.mjs'
|
|
import fs from 'node:fs'
|
|
import crypto from 'node:crypto'
|
|
const W='/tmp/project-member-runtime-8788-20261013', base='http://127.0.0.1:8788'
|
|
const creds=JSON.parse(fs.readFileSync(W+'/credentials.json','utf8'))
|
|
const adoption=JSON.parse(fs.readFileSync(W+'/adoption-result.json','utf8'))
|
|
const host=adoption.hostProjectId, domain=adoption.canonicalProjectId
|
|
fs.mkdirSync(W+'/screenshots',{recursive:true,mode:0o700})
|
|
const errors=[], result={source:'SELF',authentication:'temporary API credentials; existing passwords unchanged; password login NOT_RUN',hostProjectId:host,canonicalProjectId:domain,accounts:{}}
|
|
const browser=await chromium.launch({headless:true})
|
|
const context=await browser.newContext({viewport:{width:1440,height:1000},locale:'zh-CN'})
|
|
const page=await context.newPage()
|
|
page.on('pageerror',e=>errors.push(e.message))
|
|
async function api(path,method='GET',body){
|
|
return await page.evaluate(async ({path,method,body})=>{
|
|
const headers={Authorization:'Bearer '+localStorage.getItem('agent-system.apiKey')}
|
|
if(body!==undefined)headers['Content-Type']='application/json'
|
|
const r=await fetch(path,{method,headers,body:body===undefined?undefined:JSON.stringify(body)})
|
|
const text=await r.text();return {status:r.status,body:text?JSON.parse(text):null}
|
|
},{path,method,body})
|
|
}
|
|
try{
|
|
await page.goto(base,{waitUntil:'domcontentloaded'})
|
|
await expect(page.getByRole('button',{name:'登录',exact:true})).toBeVisible()
|
|
for(const owner of ['weichen','test']){
|
|
const other=owner==='weichen'?'test':'weichen'
|
|
await page.evaluate(key=>localStorage.setItem('agent-system.apiKey',key),creds[owner])
|
|
await page.reload({waitUntil:'domcontentloaded'})
|
|
await expect(page.locator('.tsb-projrow-name')).toHaveText('genui-e2e',{timeout:20000})
|
|
const me=await api('/api/me');expect(me.status).toBe(200);expect(me.body.owner).toBe(owner)
|
|
const projects=await api('/api/projects');expect(projects.status).toBe(200)
|
|
expect(projects.body.map(p=>p.id)).toEqual([host]);expect(projects.body[0].settings['rdm.projectId']).toBe(domain)
|
|
await expect(page.locator('.tsb-task-t').filter({hasText:'成员协作复测-'+owner+'-20261013'})).toBeVisible()
|
|
await expect(page.locator('.tsb-task-t').filter({hasText:'成员协作复测-'+other+'-20261013'})).toHaveCount(0)
|
|
await page.locator('.tsb-projrow').click()
|
|
await expect(page.locator('.tsb-mi').filter({hasText:'U7宿主项目'})).toHaveCount(0)
|
|
await page.locator('.tsb-mi').filter({hasText:'genui-e2e'}).click()
|
|
const waiting=page.waitForResponse(r=>new URL(r.url()).pathname==='/api/projects/'+host+'/conversations'&&r.request().method()==='POST')
|
|
await page.getByRole('button',{name:'新建对话',exact:true}).click()
|
|
const response=await waiting;expect(response.status()).toBe(200)
|
|
const conv=await response.json();expect(conv.owner).toBe(owner);expect(conv.projectId).toBe(host)
|
|
result.accounts[owner]={conversationCreatedByUI:conv.id,projectVisible:true,otherAccountConversationAbsent:true}
|
|
fs.writeFileSync(W+'/browser-progress.json',JSON.stringify(result,null,2),{mode:0o600})
|
|
const title='成员协作浏览器复测-'+owner+'-20261013-'+conv.id.slice(-6)
|
|
expect((await api('/api/conversations/'+conv.id,'PATCH',{title})).status).toBe(200)
|
|
await page.reload({waitUntil:'domcontentloaded'})
|
|
await expect(page.locator('.tsb-projrow-name')).toHaveText('genui-e2e',{timeout:20000})
|
|
await expect(page.locator('.tsb-task-t').filter({hasText:title})).toBeVisible()
|
|
await page.locator('.tsb-tool[title="文件搜索、预览、编辑与下载"]').click()
|
|
await expect(page.locator('.artifacts-panel')).toBeVisible()
|
|
await expect(page.locator('.artifacts-panel .name').filter({hasText:'成员协作复测-'+owner+'-私有.txt'})).toBeVisible()
|
|
await expect(page.locator('.artifacts-panel .name').filter({hasText:'成员协作复测-'+other+'-私有.txt'})).toHaveCount(0)
|
|
await page.screenshot({path:W+'/screenshots/'+owner+'-private-project.png'})
|
|
Object.assign(result.accounts[owner],{refreshPassed:true,ownFileVisible:true,otherAccountFileAbsent:true})
|
|
await page.locator('.tsb-user').click()
|
|
await expect(page.getByText('租户:'+owner,{exact:true})).toBeVisible()
|
|
await page.getByRole('button',{name:'退出登录',exact:true}).click()
|
|
await expect(page.getByRole('button',{name:'登录',exact:true})).toBeVisible({timeout:15000})
|
|
expect(await page.evaluate(()=>localStorage.getItem('agent-system.apiKey'))).toBeNull()
|
|
}
|
|
expect(errors).toEqual([])
|
|
result.pageerrorCount=errors.length;result.accountSwitchThroughLogout=true
|
|
result.driverSHA256=crypto.createHash('sha256').update(fs.readFileSync(W+'/browser.mjs')).digest('hex')
|
|
fs.writeFileSync(W+'/acceptance-browser.json',JSON.stringify(result,null,2),{mode:0o600})
|
|
console.log(JSON.stringify(result))
|
|
}finally{await context.close();await browser.close()}
|