refactor(gui): move membership summary to window title
This commit is contained in:
+30
-41
@@ -130,28 +130,10 @@ class MainWindow(QMainWindow):
|
|||||||
settings_tab = self._settings_tab()
|
settings_tab = self._settings_tab()
|
||||||
if hasattr(settings_tab, "settingsSaved"):
|
if hasattr(settings_tab, "settingsSaved"):
|
||||||
settings_tab.settingsSaved.connect(self._on_settings_saved)
|
settings_tab.settingsSaved.connect(self._on_settings_saved)
|
||||||
subscription_label_text = (
|
|
||||||
"会员:正在验证"
|
|
||||||
if SUBSCRIPTION_CHECK_ENABLED
|
|
||||||
else "会员:订阅检测已暂停"
|
|
||||||
)
|
|
||||||
self._subscription_label = QLabel(subscription_label_text)
|
|
||||||
self._subscription_label.setObjectName("subscriptionStatusLabel")
|
|
||||||
self._subscription_label.setAccessibleName("会员状态")
|
|
||||||
self._subscription_label.setStyleSheet("color: #0969da;")
|
|
||||||
header = QWidget()
|
|
||||||
header.setObjectName("appHeader")
|
|
||||||
header.setAccessibleName("会员状态栏")
|
|
||||||
header_layout = QHBoxLayout(header)
|
|
||||||
header_layout.setContentsMargins(18, 4, 18, 2)
|
|
||||||
header_layout.setSpacing(0)
|
|
||||||
header_layout.addStretch(1)
|
|
||||||
header_layout.addWidget(self._subscription_label)
|
|
||||||
central = QWidget()
|
central = QWidget()
|
||||||
central_layout = QVBoxLayout(central)
|
central_layout = QVBoxLayout(central)
|
||||||
central_layout.setContentsMargins(0, 0, 0, 0)
|
central_layout.setContentsMargins(0, 0, 0, 0)
|
||||||
central_layout.setSpacing(0)
|
central_layout.setSpacing(0)
|
||||||
central_layout.addWidget(header)
|
|
||||||
central_layout.addWidget(self.tabs, 1)
|
central_layout.addWidget(self.tabs, 1)
|
||||||
self.setCentralWidget(central)
|
self.setCentralWidget(central)
|
||||||
self.show_status("就绪", level="muted")
|
self.show_status("就绪", level="muted")
|
||||||
@@ -244,16 +226,17 @@ class MainWindow(QMainWindow):
|
|||||||
def begin_subscription_check(self):
|
def begin_subscription_check(self):
|
||||||
"""Refresh membership state without blocking the Qt GUI thread."""
|
"""Refresh membership state without blocking the Qt GUI thread."""
|
||||||
|
|
||||||
|
self._set_membership_window_title()
|
||||||
if not SUBSCRIPTION_CHECK_ENABLED:
|
if not SUBSCRIPTION_CHECK_ENABLED:
|
||||||
self._set_product_access(True)
|
self._set_product_access(True)
|
||||||
self._set_subscription_label("会员:订阅检测已暂停", "muted")
|
self.show_status("会员订阅检测已暂停", level="muted")
|
||||||
return
|
return
|
||||||
self._subscription_request_token += 1
|
self._subscription_request_token += 1
|
||||||
token = self._subscription_request_token
|
token = self._subscription_request_token
|
||||||
if self._subscription_worker is not None:
|
if self._subscription_worker is not None:
|
||||||
self._subscription_worker.cancel()
|
self._subscription_worker.cancel()
|
||||||
self._set_product_access(not SUBSCRIPTION_ENFORCEMENT_ENABLED)
|
self._set_product_access(not SUBSCRIPTION_ENFORCEMENT_ENABLED)
|
||||||
self._set_subscription_label("会员:正在验证", "info")
|
self.show_status("正在验证会员状态", level="info")
|
||||||
worker = SubscriptionCheckWorker(
|
worker = SubscriptionCheckWorker(
|
||||||
config=self.config,
|
config=self.config,
|
||||||
cmhub_config_path=self.config.get("cmhub_config_path"),
|
cmhub_config_path=self.config.get("cmhub_config_path"),
|
||||||
@@ -309,26 +292,22 @@ class MainWindow(QMainWindow):
|
|||||||
self._subscription_status = status
|
self._subscription_status = status
|
||||||
if status.state == subscription.STATUS_ACTIVE:
|
if status.state == subscription.STATUS_ACTIVE:
|
||||||
expiry = subscription.format_expiry(status.expires_at)
|
expiry = subscription.format_expiry(status.expires_at)
|
||||||
self._set_subscription_label(
|
self._set_membership_window_title(
|
||||||
"%s · %s · 有效至%s" % (status.account_name, status.plan_name, expiry),
|
status.account_name,
|
||||||
"success",
|
status.plan_name,
|
||||||
|
"有效至%s" % expiry,
|
||||||
)
|
)
|
||||||
elif status.state == subscription.STATUS_GRACE:
|
elif status.state == subscription.STATUS_GRACE:
|
||||||
expiry = subscription.format_expiry(status.grace_expires_at) or subscription.format_expiry(status.expires_at)
|
expiry = subscription.format_expiry(
|
||||||
self._set_subscription_label(
|
status.grace_expires_at
|
||||||
"%s · %s · 宽限至%s" % (status.account_name, status.plan_name, expiry),
|
) or subscription.format_expiry(status.expires_at)
|
||||||
"warning",
|
self._set_membership_window_title(
|
||||||
)
|
status.account_name,
|
||||||
elif SUBSCRIPTION_ENFORCEMENT_ENABLED:
|
status.plan_name,
|
||||||
self._set_subscription_label(
|
"宽限至%s" % expiry,
|
||||||
"会员:%s" % status.user_message,
|
|
||||||
self._subscription_level(status),
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self._set_subscription_label(
|
self._set_membership_window_title()
|
||||||
"会员观察:%s(当前不影响使用)" % status.user_message,
|
|
||||||
self._subscription_level(status),
|
|
||||||
)
|
|
||||||
|
|
||||||
access_allowed = (
|
access_allowed = (
|
||||||
status.allows_product_workflows
|
status.allows_product_workflows
|
||||||
@@ -363,11 +342,21 @@ class MainWindow(QMainWindow):
|
|||||||
return "warning"
|
return "warning"
|
||||||
return "danger"
|
return "danger"
|
||||||
|
|
||||||
def _set_subscription_label(self, text, level):
|
def _set_membership_window_title(
|
||||||
color = _status_level_color(level)
|
self,
|
||||||
self._subscription_label.setText(str(text))
|
account_name="",
|
||||||
self._subscription_label.setStyleSheet("color: %s;" % color)
|
plan_name="",
|
||||||
self._subscription_label.setToolTip(str(text))
|
expiry_text="",
|
||||||
|
):
|
||||||
|
account = " ".join(str(account_name or "").split())
|
||||||
|
plan = " ".join(str(plan_name or "").split())
|
||||||
|
expiry = " ".join(str(expiry_text or "").split())
|
||||||
|
if account and plan and expiry:
|
||||||
|
self.setWindowTitle(
|
||||||
|
"%s | %s · %s · %s" % (display_name(), account, plan, expiry)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
self.setWindowTitle(display_name())
|
||||||
|
|
||||||
def _set_product_access(self, enabled):
|
def _set_product_access(self, enabled):
|
||||||
for index, title in enumerate(TAB_TITLES):
|
for index, title in enumerate(TAB_TITLES):
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ imported → collected → generated → applied
|
|||||||
- 应用配置(模型选择、生成参数、目录、Chrome 路径)→ `data/config.json`。
|
- 应用配置(模型选择、生成参数、目录、Chrome 路径)→ `data/config.json`。
|
||||||
- AI 模型清单(direct 内部兼容模式 url/模型/密钥/类型/连接超时)→ `data/config/ai_models.json`(API Key 本地明文保存,必须 gitignore,UI 打码显示;普通设置页不再暴露 direct 切换入口)。
|
- AI 模型清单(direct 内部兼容模式 url/模型/密钥/类型/连接超时)→ `data/config/ai_models.json`(API Key 本地明文保存,必须 gitignore,UI 打码显示;普通设置页不再暴露 direct 切换入口)。
|
||||||
- cmhub 网关 Key → `data/config/cmhub.json`,schema `{ "api_key": "..." }`;`config.json` 只保存 Base URL、别名和超时,不保存 Key。
|
- cmhub 网关 Key → `data/config/cmhub.json`,schema `{ "api_key": "..." }`;`config.json` 只保存 Base URL、别名和超时,不保存 Key。
|
||||||
- 订阅状态 → 仅进程内 `SubscriptionStatus`;顶部 GUI 对有效/宽限状态展示服务端返回的账号显示名、套餐名和有效期,其他状态显示脱敏中文观察结果,不写入 SQLite、诊断日志或导出。观察期不显示 `notice_id` 模态通知、不禁用 Tab、不自动切换设置且新提交预检始终放行;`404` 表示服务端尚未启用订阅接口,网络异常不被误判为会员到期。
|
- 订阅状态 → 仅进程内 `SubscriptionStatus`;有效/宽限状态把服务端返回的账号显示名、套餐名和有效期追加到 Windows 原生窗口标题,其他状态恢复纯应用名称并在底部状态栏显示脱敏中文结果。Tabs 上方不保留会员状态行,状态不写入 SQLite、诊断日志或导出。观察期不显示 `notice_id` 模态通知、不禁用 Tab、不自动切换设置且新提交预检始终放行;`404` 表示服务端尚未启用订阅接口,网络异常不被误判为会员到期。
|
||||||
- cmhub 模型目录与 AI帮写/正式套图预估价格 → 仅内存短期缓存;预估值只供用户确认,实际扣点仍以网关响应 metadata 为准。
|
- cmhub 模型目录与 AI帮写/正式套图预估价格 → 仅内存短期缓存;预估值只供用户确认,实际扣点仍以网关响应 metadata 为准。
|
||||||
- 业务数据(账号、任务、各阶段结果)→ SQLite `data/cmshopee.db`。
|
- 业务数据(账号、任务、各阶段结果)→ SQLite `data/cmshopee.db`。
|
||||||
- 图片(采集的旧封面、AI 生成的新封面)→ `data/images/`(路径记在 DB)。
|
- 图片(采集的旧封面、AI 生成的新封面)→ `data/images/`(路径记在 DB)。
|
||||||
|
|||||||
+3
-3
@@ -29,9 +29,9 @@
|
|||||||
|
|
||||||
- 当前处于真实接口观察期:`SUBSCRIPTION_CHECK_ENABLED = True`、`SUBSCRIPTION_ENFORCEMENT_ENABLED = False`。客户端真实查询并展示订阅状态,但查询中、未配置 Key、Key 无效、账号禁用、未订阅、到期、撤销或暂时不可用均不禁用六个工作流、不自动切换「设置」、不阻止新提交。
|
- 当前处于真实接口观察期:`SUBSCRIPTION_CHECK_ENABLED = True`、`SUBSCRIPTION_ENFORCEMENT_ENABLED = False`。客户端真实查询并展示订阅状态,但查询中、未配置 Key、Key 无效、账号禁用、未订阅、到期、撤销或暂时不可用均不禁用六个工作流、不自动切换「设置」、不阻止新提交。
|
||||||
- 强制升级检查完成、主窗口显示后,后台用 `data/config/cmhub.json` 的 API Key 请求 `GET /api/v1/cmshopee/subscription/status`;不会阻塞 Qt 主线程或把 Key 放入 URL、状态栏、日志和错误提示。
|
- 强制升级检查完成、主窗口显示后,后台用 `data/config/cmhub.json` 的 API Key 请求 `GET /api/v1/cmshopee/subscription/status`;不会阻塞 Qt 主线程或把 Key 放入 URL、状态栏、日志和错误提示。
|
||||||
- 应用名称只由 Windows 原生标题栏显示,Tabs 上方不再重复品牌标题;该轻量区域只在右侧承载会员状态。订阅有效时显示“账号名 · 套餐名 · 有效至日期”,宽限期显示宽限截止日;其他状态显示中文观察结果并明确“当前不影响使用”。不得把长会员文本拼进 Windows 原生窗口标题,也不得展示 API Key。
|
- Tabs 上方不保留应用内标题或会员状态行。Windows 原生标题默认只显示应用名称;订阅有效时追加“账号名 · 套餐名 · 有效至日期”,宽限期追加宽限截止日。检测中和其他状态立即恢复纯应用名称,中文观察结果及“当前不影响使用”只显示在底部状态栏。标题不得展示 API Key、接口地址、会员中心地址、通知标识或原始错误。
|
||||||
- 观察期不显示 `notice_id` 对应的“已启用会员订阅”模态通知框;保存 cmhub 设置后会立即重新检查,但陈旧线程结果不能覆盖新状态。
|
- 观察期不显示 `notice_id` 对应的“已启用会员订阅”模态通知框;保存 cmhub 设置后会立即重新检查,但陈旧线程结果不能覆盖新状态。
|
||||||
- 当前服务端未部署订阅接口时,`404` 视为旧服务兼容:顶部显示“会员服务尚未启用”,六个工作流继续按旧行为运行,不弹阻断窗口。网络或格式错误显示“暂时无法确认会员状态”,不能误报为 Key 无效或会员到期。
|
- 当前服务端未部署订阅接口时,`404` 视为旧服务兼容:底部状态栏显示“会员服务尚未启用”,六个工作流继续按旧行为运行,不弹阻断窗口。网络或格式错误显示“暂时无法确认会员状态”,不能误报为 Key 无效或会员到期。
|
||||||
- ② AI生成、⑥商品套图和「AI帮写」继续调用同一订阅预检入口,但观察模式始终放行;cmhub 产品接口仍必须服务端最终裁决。已提交的异步任务继续查询、下载和查看。自定义网关直连的观察结果同样不能作为不可绕过的授权保护。
|
- ② AI生成、⑥商品套图和「AI帮写」继续调用同一订阅预检入口,但观察模式始终放行;cmhub 产品接口仍必须服务端最终裁决。已提交的异步任务继续查询、下载和查看。自定义网关直连的观察结果同样不能作为不可绕过的授权保护。
|
||||||
- 后续正式门禁需另建任务,先根据观察结果确认存量 Key/套餐覆盖率、网络失败策略和回滚方案;启用后才恢复 T-686 的“非有效状态只保留设置 Tab”和一次性订阅通知逻辑。
|
- 后续正式门禁需另建任务,先根据观察结果确认存量 Key/套餐覆盖率、网络失败策略和回滚方案;启用后才恢复 T-686 的“非有效状态只保留设置 Tab”和一次性订阅通知逻辑。
|
||||||
|
|
||||||
@@ -258,7 +258,7 @@
|
|||||||
|
|
||||||
| 组件 | 归属 | 说明 |
|
| 组件 | 归属 | 说明 |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `MainWindow(QMainWindow)` | 根窗口 | 持有 `QTabWidget`、状态栏、顶部会员状态、订阅受限恢复入口和全局消息 |
|
| `MainWindow(QMainWindow)` | 根窗口 | 持有 `QTabWidget`、原生窗口标题会员摘要、底部状态栏、订阅受限恢复入口和全局消息 |
|
||||||
| `CollectTab(QWidget)` | ① | 导入、任务表、采集、回写 |
|
| `CollectTab(QWidget)` | ① | 导入、任务表、采集、回写 |
|
||||||
| `GenerateTab(QWidget)` | ② | 左提示词管理 + 右筛选/任务列表;双击看新旧封面;先确认商品状态生成范围,再按本轮「生成内容」下拉接入 `GenerateWorker` |
|
| `GenerateTab(QWidget)` | ② | 左提示词管理 + 右筛选/任务列表;双击看新旧封面;先确认商品状态生成范围,再按本轮「生成内容」下拉接入 `GenerateWorker` |
|
||||||
| `ApplyTab(QWidget)` | ③ | 已生成任务筛选 +「更新内容」下拉 + 商品状态优先/内容完整性预检剔除 +「检查本轮更新」+ 分批开始更新确认 + 检查/真实更新运行日志 + 结果回写与结束汇总 |
|
| `ApplyTab(QWidget)` | ③ | 已生成任务筛选 +「更新内容」下拉 + 商品状态优先/内容完整性预检剔除 +「检查本轮更新」+ 分批开始更新确认 + 检查/真实更新运行日志 + 结果回写与结束汇总 |
|
||||||
|
|||||||
+6
-2
@@ -3,7 +3,7 @@ id: T-698
|
|||||||
title: 会员信息移入 Windows 原生标题栏
|
title: 会员信息移入 Windows 原生标题栏
|
||||||
phase: 7
|
phase: 7
|
||||||
deps: [T-697]
|
deps: [T-697]
|
||||||
status: TODO
|
status: DONE
|
||||||
created: 2026-07-23
|
created: 2026-07-23
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -58,4 +58,8 @@ T-697 已移除 Tabs 上方重复的应用名称,但仍保留一整行应用
|
|||||||
|
|
||||||
## 执行记录
|
## 执行记录
|
||||||
|
|
||||||
- 待实现。
|
- 2026-07-23:删除 `subscriptionStatusLabel`、`appHeader` 及其布局,主窗口中央区域直接从六个 Tabs 开始,不保留顶部空白状态行。
|
||||||
|
- 新增主线程原生标题更新方法:有效状态显示“应用名称 | 账号 · 套餐 · 有效至日期”,宽限状态显示“宽限至日期”;账号、套餐和日期做单行空白归一化。开始重新检测及任一非有效状态会立即恢复纯应用名称。
|
||||||
|
- 检测中、观察模式异常和正式门禁原因继续由底部状态栏展示;T-696 的真实查询、观察放行、保存后重查、陈旧结果隔离和通知抑制逻辑不变。窗口标题不读取或显示 API Key、接口/会员中心地址、通知标识和原始错误。
|
||||||
|
- 更新架构、路由和 GUI 回归测试,覆盖顶部状态行不存在、有效/宽限标题、异常重置、检测中反馈及敏感字段不进入标题。
|
||||||
|
- 验证通过:主窗口定向 GUI 测试 5 项、`py -3.10 -m unittest discover -s tests`(683 项)、`py -3.10 -m ruff check app tests main.py`、`py -3.10 -m compileall app main.py`、`git diff --check`。
|
||||||
|
|||||||
+44
-19
@@ -635,21 +635,9 @@ class GuiTests(TempDirMixin, unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(gui.display_name(), window.windowTitle())
|
self.assertEqual(gui.display_name(), window.windowTitle())
|
||||||
self.assertIsNone(window.findChild(QLabel, "appHeaderTitle"))
|
self.assertIsNone(window.findChild(QLabel, "appHeaderTitle"))
|
||||||
subscription_label = window.findChild(QLabel, "subscriptionStatusLabel")
|
self.assertIsNone(window.findChild(QLabel, "subscriptionStatusLabel"))
|
||||||
self.assertIsNotNone(subscription_label)
|
self.assertEqual(1, window.centralWidget().layout().count())
|
||||||
header = window.centralWidget().layout().itemAt(0).widget()
|
self.assertIs(window.tabs, window.centralWidget().layout().itemAt(0).widget())
|
||||||
self.assertEqual("appHeader", header.objectName())
|
|
||||||
self.assertEqual("会员状态栏", header.accessibleName())
|
|
||||||
margins = header.layout().contentsMargins()
|
|
||||||
self.assertEqual(
|
|
||||||
(18, 4, 18, 2),
|
|
||||||
(
|
|
||||||
margins.left(),
|
|
||||||
margins.top(),
|
|
||||||
margins.right(),
|
|
||||||
margins.bottom(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
self.assertEqual(6, window.tabs.count())
|
self.assertEqual(6, window.tabs.count())
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
[
|
[
|
||||||
@@ -11185,11 +11173,44 @@ class GuiTests(TempDirMixin, unittest.TestCase):
|
|||||||
|
|
||||||
window._apply_subscription_status(status)
|
window._apply_subscription_status(status)
|
||||||
|
|
||||||
self.assertIn("主账号 · 专业版 · 有效至2026-08-20", window._subscription_label.text())
|
self.assertEqual(
|
||||||
|
"%s | 主账号 · 专业版 · 有效至2026-08-20" % gui.display_name(),
|
||||||
|
window.windowTitle(),
|
||||||
|
)
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
all(window.tabs.isTabEnabled(index) for index in range(window.tabs.count()))
|
all(window.tabs.isTabEnabled(index) for index in range(window.tabs.count()))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_main_window_membership_title_uses_grace_and_resets_for_invalid_status(self):
|
||||||
|
with self.make_temp_dir() as temp_dir:
|
||||||
|
window = MainWindow(config=self.make_config(temp_dir))
|
||||||
|
self.addCleanup(window.close)
|
||||||
|
grace = subscription.SubscriptionStatus(
|
||||||
|
subscription.STATUS_GRACE,
|
||||||
|
account_name="主账号",
|
||||||
|
plan_name="测试",
|
||||||
|
expires_at="2026-08-21T23:59:59+08:00",
|
||||||
|
grace_expires_at="2026-08-23T23:59:59+08:00",
|
||||||
|
manage_url="https://cm.example.com/subscription",
|
||||||
|
notice_id="private-notice",
|
||||||
|
)
|
||||||
|
|
||||||
|
window._apply_subscription_status(grace)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
"%s | 主账号 · 测试 · 宽限至2026-08-23" % gui.display_name(),
|
||||||
|
window.windowTitle(),
|
||||||
|
)
|
||||||
|
self.assertNotIn("cm.example.com", window.windowTitle())
|
||||||
|
self.assertNotIn("private-notice", window.windowTitle())
|
||||||
|
|
||||||
|
window._apply_subscription_status(
|
||||||
|
subscription.SubscriptionStatus(subscription.STATUS_UNAVAILABLE)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(gui.display_name(), window.windowTitle())
|
||||||
|
self.assertIn("当前不影响使用", window.statusBar().currentMessage())
|
||||||
|
|
||||||
def test_main_window_restricts_tabs_when_subscription_is_invalid(self):
|
def test_main_window_restricts_tabs_when_subscription_is_invalid(self):
|
||||||
with self.make_temp_dir() as temp_dir:
|
with self.make_temp_dir() as temp_dir:
|
||||||
window = MainWindow(config=self.make_config(temp_dir))
|
window = MainWindow(config=self.make_config(temp_dir))
|
||||||
@@ -11252,7 +11273,8 @@ class GuiTests(TempDirMixin, unittest.TestCase):
|
|||||||
|
|
||||||
worker_factory.assert_called_once()
|
worker_factory.assert_called_once()
|
||||||
self.assertTrue(thread.started)
|
self.assertTrue(thread.started)
|
||||||
self.assertEqual("会员:正在验证", window._subscription_label.text())
|
self.assertEqual(gui.display_name(), window.windowTitle())
|
||||||
|
self.assertEqual("正在验证会员状态", window.statusBar().currentMessage())
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
all(window.tabs.isTabEnabled(index) for index in range(window.tabs.count()))
|
all(window.tabs.isTabEnabled(index) for index in range(window.tabs.count()))
|
||||||
)
|
)
|
||||||
@@ -11262,7 +11284,7 @@ class GuiTests(TempDirMixin, unittest.TestCase):
|
|||||||
window._apply_subscription_status(expired)
|
window._apply_subscription_status(expired)
|
||||||
|
|
||||||
notice.assert_not_called()
|
notice.assert_not_called()
|
||||||
self.assertIn("当前不影响使用", window._subscription_label.text())
|
self.assertEqual(gui.display_name(), window.windowTitle())
|
||||||
self.assertIn("当前不影响使用", window.statusBar().currentMessage())
|
self.assertIn("当前不影响使用", window.statusBar().currentMessage())
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
all(window.tabs.isTabEnabled(index) for index in range(window.tabs.count()))
|
all(window.tabs.isTabEnabled(index) for index in range(window.tabs.count()))
|
||||||
@@ -11285,7 +11307,10 @@ class GuiTests(TempDirMixin, unittest.TestCase):
|
|||||||
window._apply_subscription_status(status)
|
window._apply_subscription_status(status)
|
||||||
|
|
||||||
notice.assert_not_called()
|
notice.assert_not_called()
|
||||||
self.assertIn("主账号 · 测试 · 有效至2026-08-21", window._subscription_label.text())
|
self.assertEqual(
|
||||||
|
"%s | 主账号 · 测试 · 有效至2026-08-21" % gui.display_name(),
|
||||||
|
window.windowTitle(),
|
||||||
|
)
|
||||||
self.assertIn("观察模式", window.statusBar().currentMessage())
|
self.assertIn("观察模式", window.statusBar().currentMessage())
|
||||||
|
|
||||||
with mock.patch.object(window, "begin_subscription_check") as check:
|
with mock.patch.object(window, "begin_subscription_check") as check:
|
||||||
|
|||||||
Reference in New Issue
Block a user