feat(plugins): 4 个业务插件代码 + 前台公开页面改造
plugins/navManager:导航 CRUD + 导航分类 CRUD(独立 blueprint) plugins/friendManager:友情链接 CRUD plugins/aboutManager:关于本站 CRUD plugins/siteStats:访问统计主页 + JSON 接口 + /site/* GET 埋点 + nav 卡片点击埋点 applications/view/public/nav.py:导航聚合页 / 分类详情 / JSON 接口,支持 visit_count 注入 applications/view/public/about.py:关于本站公开页 applications/view/public/friend.py:友情链接公开页 templates/public/base.html:响应式首页(搜索框 / 分类抽屉 / 5 主题切换 / footer 隐私小字) templates/public/index.html / category.html:卡片右下角访问次数 templates/public/about.html / friend.html:关于 + 友链前台页 docs/plugins-development.md:插件开发完整指南(生命周期 + 4 个示例 + FAQ + framework 迁移) scripts/:探针与端到端验证脚本(probe_*/verify_*/test_*)
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
import sys, time
|
||||
sys.path.insert(0, r'D:\bwstudio\pear-admin-flask')
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
BASE = 'http://127.0.0.1:5000'
|
||||
|
||||
def login(page):
|
||||
page.goto(f'{BASE}/system/passport/login', wait_until='networkidle')
|
||||
page.wait_for_selector('input[name="captcha"]')
|
||||
# 用一个新的 context request 取同一个 session 的 captcha —— 因为 page.request 和 page 可能共享 cookie
|
||||
resp = page.request.get(f'{BASE}/system/passport/getCaptcha')
|
||||
code = resp.headers.get('x-captcha-code', '').lower()
|
||||
if not code:
|
||||
raise RuntimeError('no captcha code')
|
||||
# 再强制刷新一遍 login 页上的 captcha 图片,确保 server session code == 这张图绑定的那个
|
||||
# 因为 server 端 captcha() 会覆写 session['code'] 为最近一张图
|
||||
page.reload(wait_until='networkidle')
|
||||
page.wait_for_selector('input[name="captcha"]')
|
||||
resp = page.request.get(f'{BASE}/system/passport/getCaptcha')
|
||||
code = resp.headers.get('x-captcha-code', '').lower()
|
||||
print('captcha code =', code)
|
||||
page.fill('input[name="username"]', 'admin')
|
||||
page.fill('input[name="password"]', '123456')
|
||||
page.fill('input[name="captcha"]', code)
|
||||
page.click('button[lay-filter="login"]')
|
||||
# 等待跳转离开 login 页
|
||||
time.sleep(2)
|
||||
print('after login url =', page.url)
|
||||
|
||||
with sync_playwright() as pw:
|
||||
browser = pw.chromium.launch(headless=True, args=['--no-sandbox', '--disable-gpu'])
|
||||
ctx = browser.new_context()
|
||||
page = ctx.new_page()
|
||||
|
||||
msgs = []
|
||||
page.on('console', lambda m: msgs.append(f'[{m.type}] {m.text}'))
|
||||
page.on('pageerror', lambda e: msgs.append(f'[PAGE-ERR] {e}'))
|
||||
|
||||
login(page)
|
||||
|
||||
for menu_path in ['/system/nav/', '/system/friend/', '/system/about/', '/system/stats/']:
|
||||
msgs.clear()
|
||||
page.on('console', lambda m: msgs.append(f'[{m.type}] {m.text}'))
|
||||
page.on('pageerror', lambda e: msgs.append(f'[PAGE-ERR] {e}'))
|
||||
print(f'\n===== Visit {menu_path} =====')
|
||||
page.goto(BASE + menu_path, wait_until='networkidle')
|
||||
time.sleep(2.5)
|
||||
row_count = page.locator('.layui-table tbody tr').count()
|
||||
has_loading = page.locator('.layui-table-init').count()
|
||||
none_elem = page.locator('.layui-table-none').count()
|
||||
empty_text = ''
|
||||
if none_elem:
|
||||
empty_text = page.locator('.layui-table-none').first.inner_text() or ''
|
||||
print(f' rows={row_count}, init={has_loading}, none={none_elem}, none_text={empty_text!r}')
|
||||
out = menu_path.strip('/').replace('/', '_') + '.png'
|
||||
path = 'D:\\bwstudio\\pear-admin-flask\\scripts\\screenshots\\' + out
|
||||
import os
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
page.screenshot(path=path, full_page=True)
|
||||
print(f' screenshot -> {path}')
|
||||
for m in msgs[-15:]:
|
||||
print(' ', m)
|
||||
|
||||
browser.close()
|
||||
Reference in New Issue
Block a user