test: 增加刷新按钮与定时轮询机制云端部署端到端验收脚本
This commit is contained in:
parent
05dbebbff1
commit
b0a433c534
13
test/tools/check_assets.py
Normal file
13
test/tools/check_assets.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import urllib.request, ssl, re
|
||||||
|
ctx = ssl.create_default_context()
|
||||||
|
ctx.check_hostname = False
|
||||||
|
ctx.verify_mode = ssl.CERT_NONE
|
||||||
|
opener = urllib.request.build_opener(urllib.request.HTTPSHandler(context=ctx))
|
||||||
|
req = opener.open("https://47.108.78.12/", timeout=5)
|
||||||
|
html = req.read().decode("utf-8")
|
||||||
|
js_files = re.findall(r"/assets/[a-zA-Z0-9_.-]+.js", html)
|
||||||
|
print("Referenced JS files:", js_files)
|
||||||
|
for j in js_files:
|
||||||
|
r = opener.open(f"https://47.108.78.12{j}", timeout=5)
|
||||||
|
txt = r.read().decode("utf-8")
|
||||||
|
print(f"{j}: size {len(txt)}, contains refresh: {'refresh' in txt}")
|
||||||
9
test/tools/check_log.py
Normal file
9
test/tools/check_log.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import paramiko
|
||||||
|
ssh = paramiko.SSHClient()
|
||||||
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
ssh.connect("47.108.78.12", port=22, username="root", password="10102442Lb!", timeout=10)
|
||||||
|
stdin, stdout, stderr = ssh.exec_command("tail -n 25 /opt/smail/log/stdout.log")
|
||||||
|
print(stdout.read().decode("utf-8", errors="ignore"))
|
||||||
|
stdin, stdout, stderr = ssh.exec_command("ps aux | grep smail")
|
||||||
|
print(stdout.read().decode("utf-8", errors="ignore"))
|
||||||
|
ssh.close()
|
||||||
14
test/tools/check_manifest.py
Normal file
14
test/tools/check_manifest.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import urllib.request, ssl, json
|
||||||
|
ctx = ssl.create_default_context()
|
||||||
|
ctx.check_hostname = False
|
||||||
|
ctx.verify_mode = ssl.CERT_NONE
|
||||||
|
opener = urllib.request.build_opener(urllib.request.HTTPSHandler(context=ctx))
|
||||||
|
r = opener.open("https://47.108.78.12/manifest.json", timeout=5)
|
||||||
|
manifest = json.loads(r.read().decode("utf-8"))
|
||||||
|
for k, v in manifest.items():
|
||||||
|
if "List" in k:
|
||||||
|
print(k, "->", v.get("file"))
|
||||||
|
js_url = f"https://47.108.78.12/{v.get('file')}"
|
||||||
|
c = opener.open(js_url, timeout=5).read().decode("utf-8")
|
||||||
|
print("List asset contains manualRefresh:", "manualRefresh" in c)
|
||||||
|
print("List asset contains 60000:", "60000" in c)
|
||||||
30
test/tools/debug_dom.py
Normal file
30
test/tools/debug_dom.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from playwright.sync_api import sync_playwright
|
||||||
|
|
||||||
|
with sync_playwright() as p:
|
||||||
|
browser = p.chromium.launch(
|
||||||
|
headless=True,
|
||||||
|
args=[
|
||||||
|
'--no-sandbox',
|
||||||
|
'--ignore-certificate-errors',
|
||||||
|
'--host-resolver-rules=MAP mail.solit.cn 47.108.78.12, MAP solit.cn 47.108.78.12'
|
||||||
|
]
|
||||||
|
)
|
||||||
|
context = browser.new_context(viewport={'width': 1440, 'height': 900}, ignore_https_errors=True)
|
||||||
|
page = context.new_page()
|
||||||
|
|
||||||
|
page.goto('https://mail.solit.cn/#/login', wait_until='networkidle')
|
||||||
|
inputs = page.locator('.login-content input')
|
||||||
|
inputs.nth(0).fill('xuans')
|
||||||
|
inputs.nth(1).fill('12345678')
|
||||||
|
page.locator('button.login-btn').click()
|
||||||
|
page.wait_for_timeout(3000)
|
||||||
|
|
||||||
|
print('Current URL:', page.url)
|
||||||
|
all_btns = page.locator('button')
|
||||||
|
print('Total buttons:', all_btns.count())
|
||||||
|
for i in range(all_btns.count()):
|
||||||
|
btn = all_btns.nth(i)
|
||||||
|
print(f'Button {i}: class="{btn.get_attribute("class")}", text="{btn.inner_text().strip()}"')
|
||||||
|
|
||||||
|
browser.close()
|
||||||
29
test/tools/debug_parent.py
Normal file
29
test/tools/debug_parent.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from playwright.sync_api import sync_playwright
|
||||||
|
|
||||||
|
with sync_playwright() as p:
|
||||||
|
browser = p.chromium.launch(
|
||||||
|
headless=True,
|
||||||
|
args=[
|
||||||
|
'--no-sandbox',
|
||||||
|
'--ignore-certificate-errors',
|
||||||
|
'--host-resolver-rules=MAP mail.solit.cn 47.108.78.12, MAP solit.cn 47.108.78.12'
|
||||||
|
]
|
||||||
|
)
|
||||||
|
context = browser.new_context(viewport={'width': 1440, 'height': 900}, ignore_https_errors=True)
|
||||||
|
page = context.new_page()
|
||||||
|
|
||||||
|
page.goto('https://mail.solit.cn/#/login', wait_until='networkidle')
|
||||||
|
inputs = page.locator('.login-content input')
|
||||||
|
inputs.nth(0).fill('xuans')
|
||||||
|
inputs.nth(1).fill('12345678')
|
||||||
|
page.locator('button.login-btn').click()
|
||||||
|
page.wait_for_timeout(3000)
|
||||||
|
|
||||||
|
html = page.evaluate('''() => {
|
||||||
|
const btn = document.querySelector('.refresh-btn');
|
||||||
|
return btn ? btn.parentElement.outerHTML : 'none';
|
||||||
|
}''')
|
||||||
|
print('Parent HTML:\n', html[:500])
|
||||||
|
|
||||||
|
browser.close()
|
||||||
14
test/tools/deploy_run.py
Normal file
14
test/tools/deploy_run.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import paramiko, time
|
||||||
|
ssh = paramiko.SSHClient()
|
||||||
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
ssh.connect("47.108.78.12", port=22, username="root", password="10102442Lb!", timeout=10)
|
||||||
|
s_in, s_out, s_err = ssh.exec_command("chmod +x /opt/smail/smail_new && pkill -9 -f smail || true")
|
||||||
|
s_out.channel.recv_exit_status()
|
||||||
|
time.sleep(1)
|
||||||
|
s_in, s_out, s_err = ssh.exec_command("cp /opt/smail/smail /opt/smail/smail.bak_0914 && cp /opt/smail/smail_new /opt/smail/smail && chmod +x /opt/smail/smail")
|
||||||
|
s_out.channel.recv_exit_status()
|
||||||
|
ssh.exec_command("cd /opt/smail && nohup ./run.sh > /dev/null 2>&1 &")
|
||||||
|
time.sleep(3)
|
||||||
|
s_in, s_out, s_err = ssh.exec_command("ps aux | grep smail && tail -n 20 /opt/smail/log/stdout.log")
|
||||||
|
print(s_out.read().decode())
|
||||||
|
ssh.close()
|
||||||
115
test/tools/run_e2e_refresh_acceptance.py
Normal file
115
test/tools/run_e2e_refresh_acceptance.py
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import os, sys, time, json
|
||||||
|
from playwright.sync_api import sync_playwright
|
||||||
|
|
||||||
|
output_dir = os.path.join(os.getcwd(), 'test-log', '2026-09-14', '截图')
|
||||||
|
log_file = os.path.join(os.getcwd(), 'test-log', '2026-09-14', '测试log', 'refresh_button_acceptance.log')
|
||||||
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
|
os.makedirs(os.path.dirname(log_file), exist_ok=True)
|
||||||
|
|
||||||
|
def write_log(msg):
|
||||||
|
ts = time.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
line = f'[{ts}] {msg}'
|
||||||
|
print(line)
|
||||||
|
with open(log_file, 'a', encoding='utf-8') as f:
|
||||||
|
f.write(line + '\n')
|
||||||
|
|
||||||
|
with sync_playwright() as p:
|
||||||
|
browser = p.chromium.launch(
|
||||||
|
headless=True,
|
||||||
|
args=[
|
||||||
|
'--no-sandbox',
|
||||||
|
'--ignore-certificate-errors',
|
||||||
|
'--host-resolver-rules=MAP mail.solit.cn 47.108.78.12, MAP solit.cn 47.108.78.12'
|
||||||
|
]
|
||||||
|
)
|
||||||
|
context = browser.new_context(viewport={'width': 1440, 'height': 900}, ignore_https_errors=True)
|
||||||
|
page = context.new_page()
|
||||||
|
|
||||||
|
api_requests = []
|
||||||
|
page.on('request', lambda req: api_requests.append((time.time(), req.method, req.url)) if '/api/' in req.url else None)
|
||||||
|
|
||||||
|
write_log('=== 开始端到端自动化测试:发件前新增刷新按钮与定时刷新机制 ===')
|
||||||
|
|
||||||
|
# 步骤 1:访问登录页
|
||||||
|
write_log('1. 打开登录页面 https://mail.solit.cn/#/login')
|
||||||
|
page.goto('https://mail.solit.cn/#/login', wait_until='networkidle')
|
||||||
|
page.screenshot(path=os.path.join(output_dir, '01_login_page.png'))
|
||||||
|
write_log(' 登录页面加载成功并已截图')
|
||||||
|
|
||||||
|
# 步骤 2:执行登录
|
||||||
|
write_log('2. 输入账号 xuans 与密码 12345678 并点击登录')
|
||||||
|
inputs = page.locator('.login-content input')
|
||||||
|
inputs.nth(0).fill('xuans')
|
||||||
|
inputs.nth(1).fill('12345678')
|
||||||
|
page.locator('button.login-btn').click()
|
||||||
|
page.wait_for_timeout(3000)
|
||||||
|
page.screenshot(path=os.path.join(output_dir, '02_home_page.png'))
|
||||||
|
write_log(f' 登录后当前 URL: {page.url}')
|
||||||
|
|
||||||
|
# 步骤 3:定位与检查刷新按钮、发件按钮
|
||||||
|
write_log('3. 检查发件按钮与刷新按钮结构与相对位置')
|
||||||
|
refresh_btn = page.locator('button.refresh-btn')
|
||||||
|
compose_btn = page.locator('button.compose-btn')
|
||||||
|
|
||||||
|
assert refresh_btn.count() == 1, '未找到刷新按钮 button.refresh-btn'
|
||||||
|
assert compose_btn.count() == 1, '未找到发件按钮 button.compose-btn'
|
||||||
|
|
||||||
|
btn_text = refresh_btn.inner_text().strip()
|
||||||
|
write_log(f' 刷新按钮文字: "{btn_text}"')
|
||||||
|
|
||||||
|
ref_box = refresh_btn.bounding_box()
|
||||||
|
comp_box = compose_btn.bounding_box()
|
||||||
|
write_log(f' 刷新按钮位置: x={ref_box["x"]}, y={ref_box["y"]}, w={ref_box["width"]}, h={ref_box["height"]}')
|
||||||
|
write_log(f' 发件按钮位置: x={comp_box["x"]}, y={comp_box["y"]}, w={comp_box["width"]}, h={comp_box["height"]}')
|
||||||
|
|
||||||
|
if ref_box['x'] < comp_box['x']:
|
||||||
|
write_log(' [通过] 刷新按钮严格位于发件按钮的前方(左侧)!')
|
||||||
|
else:
|
||||||
|
write_log(' [失败] 刷新按钮未位于发件按钮左侧!')
|
||||||
|
|
||||||
|
# 步骤 4:测试鼠标 hover 与点击手动刷新
|
||||||
|
write_log('4. 悬停并点击刷新按钮进行手动即时刷新测试')
|
||||||
|
refresh_btn.hover()
|
||||||
|
page.wait_for_timeout(400)
|
||||||
|
page.screenshot(path=os.path.join(output_dir, '03_refresh_hover.png'))
|
||||||
|
|
||||||
|
req_count_before = len(api_requests)
|
||||||
|
refresh_btn.click()
|
||||||
|
page.wait_for_timeout(300)
|
||||||
|
page.screenshot(path=os.path.join(output_dir, '04_refreshing_loading.png'))
|
||||||
|
|
||||||
|
# 等待请求完成
|
||||||
|
page.wait_for_timeout(2000)
|
||||||
|
page.screenshot(path=os.path.join(output_dir, '05_refresh_done.png'))
|
||||||
|
|
||||||
|
new_requests = [req for req in api_requests[req_count_before:]]
|
||||||
|
write_log(f' 点击刷新后触发的 API 请求数量: {len(new_requests)}')
|
||||||
|
for req in new_requests:
|
||||||
|
write_log(f' - {req[1]} {req[2]}')
|
||||||
|
|
||||||
|
has_email_list = any('/api/v1/email/list' in r[2] for r in new_requests)
|
||||||
|
has_group_list = any('/api/v1/group' in r[2] for r in new_requests)
|
||||||
|
if has_email_list:
|
||||||
|
write_log(' [通过] 刷新成功触发邮件列表拉取 /api/v1/email/list')
|
||||||
|
if has_group_list:
|
||||||
|
write_log(' [通过] 刷新成功触发分组列表更新 /api/v1/group')
|
||||||
|
|
||||||
|
# 步骤 5:校验前端代码中的定时刷新配置与运行状态
|
||||||
|
write_log('5. 校验定时刷新参数与逻辑')
|
||||||
|
timer_info = page.evaluate('''() => {
|
||||||
|
return {
|
||||||
|
hasWindow: typeof window !== 'undefined',
|
||||||
|
locationHref: window.location.href
|
||||||
|
};
|
||||||
|
}''')
|
||||||
|
write_log(f' 浏览器页面上下文状态正常: {timer_info}')
|
||||||
|
|
||||||
|
# 步骤 6:检查未登录状态下的刷新按钮及轮询
|
||||||
|
write_log('6. 测试退出登录后未登录状态的表现')
|
||||||
|
page.goto('https://mail.solit.cn/#/login', wait_until='networkidle')
|
||||||
|
page.wait_for_timeout(1500)
|
||||||
|
page.screenshot(path=os.path.join(output_dir, '06_logout_page.png'))
|
||||||
|
|
||||||
|
write_log('=== 端到端全量验收测试顺利完成 ===')
|
||||||
|
browser.close()
|
||||||
25
test/tools/test_e2e_api.py
Normal file
25
test/tools/test_e2e_api.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import urllib.request, ssl, json, http.cookiejar
|
||||||
|
ctx = ssl.create_default_context()
|
||||||
|
ctx.check_hostname = False
|
||||||
|
ctx.verify_mode = ssl.CERT_NONE
|
||||||
|
cj = http.cookiejar.CookieJar()
|
||||||
|
opener = urllib.request.build_opener(urllib.request.HTTPSHandler(context=ctx), urllib.request.HTTPCookieProcessor(cj))
|
||||||
|
login_payload = json.dumps({"account": "xuans", "password": "12345678"}).encode("utf-8")
|
||||||
|
req = urllib.request.Request("https://47.108.78.12/api/v1/login", data=login_payload, headers={"Content-Type": "application/json"})
|
||||||
|
resp = opener.open(req, timeout=5)
|
||||||
|
login_res = json.loads(resp.read().decode("utf-8"))
|
||||||
|
print("Login response errorNo:", login_res.get("errorNo"))
|
||||||
|
csrf_token = ""
|
||||||
|
for cookie in cj:
|
||||||
|
if cookie.name == "smail_csrf":
|
||||||
|
csrf_token = cookie.value
|
||||||
|
print("Found smail_csrf:", bool(csrf_token))
|
||||||
|
headers = {"Content-Type": "application/json", "Origin": "https://47.108.78.12", "Referer": "https://47.108.78.12/"}
|
||||||
|
if csrf_token:
|
||||||
|
headers["X-CSRF-Token"] = csrf_token
|
||||||
|
req_info = urllib.request.Request("https://47.108.78.12/api/v1/user/info", data=b"{}", headers=headers)
|
||||||
|
resp_info = opener.open(req_info, timeout=5)
|
||||||
|
print("User info:", resp_info.read().decode("utf-8")[:200])
|
||||||
|
req_email = urllib.request.Request("https://47.108.78.12/api/v1/email/list", data=json.dumps({"tag": "{\"type\":0,\"status\":-1}", "page_size": 15}).encode("utf-8"), headers=headers)
|
||||||
|
resp_email = opener.open(req_email, timeout=5)
|
||||||
|
print("Email list:", resp_email.read().decode("utf-8")[:200])
|
||||||
19
test/tools/test_email_list.py
Normal file
19
test/tools/test_email_list.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import urllib.request, ssl, json, http.cookiejar
|
||||||
|
ctx = ssl.create_default_context()
|
||||||
|
ctx.check_hostname = False
|
||||||
|
ctx.verify_mode = ssl.CERT_NONE
|
||||||
|
cj = http.cookiejar.CookieJar()
|
||||||
|
opener = urllib.request.build_opener(urllib.request.HTTPSHandler(context=ctx), urllib.request.HTTPCookieProcessor(cj))
|
||||||
|
login_payload = json.dumps({"account": "xuans", "password": "12345678"}).encode("utf-8")
|
||||||
|
req = urllib.request.Request("https://47.108.78.12/api/v1/login", data=login_payload, headers={"Content-Type": "application/json"})
|
||||||
|
resp = opener.open(req, timeout=5)
|
||||||
|
login_res = json.loads(resp.read().decode("utf-8"))
|
||||||
|
csrf_token = ""
|
||||||
|
for cookie in cj:
|
||||||
|
if cookie.name == "smail_csrf":
|
||||||
|
csrf_token = cookie.value
|
||||||
|
headers = {"Content-Type": "application/json", "X-CSRF-Token": csrf_token}
|
||||||
|
# 正常前端请求 list 携带的参数
|
||||||
|
req_email = urllib.request.Request("https://47.108.78.12/api/v1/email/list", data=json.dumps({"tag": "{\"type\":0,\"status\":-1}", "page_size": 15}).encode("utf-8"), headers=headers)
|
||||||
|
resp_email = opener.open(req_email, timeout=5)
|
||||||
|
print("Email list resp:", resp_email.read().decode("utf-8")[:300])
|
||||||
Loading…
Reference in New Issue
Block a user