This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
// Local-only visual QA: no request reaches the real backend.
|
||||
import { preview } from 'vite';
|
||||
|
||||
let notificationRead = false;
|
||||
|
||||
const server = await preview({
|
||||
configFile: false,
|
||||
build: { outDir: 'dist/build/h5' },
|
||||
@@ -19,6 +21,14 @@ const server = await preview({
|
||||
expiry_reminder: { remaining_days: 0, expires_at: '2026-10-13T16:00:00Z', level: 'red', level_name: '即将到期' }
|
||||
};
|
||||
if (req.url.startsWith('/__mock/notifications')) data = { count: 0, items: [], total: 0 };
|
||||
if (process.env.H5_MOCK_NOTIFICATIONS === '1') {
|
||||
if (req.url.startsWith('/__mock/notifications/unread-count')) data = { count: notificationRead ? 0 : 1 };
|
||||
else if (/^\/__mock\/notifications\/42\/read/.test(req.url)) { notificationRead = true; data = { success: true }; }
|
||||
else if (req.url.startsWith('/__mock/notifications/read-all')) { notificationRead = true; data = { updated_count: 1 }; }
|
||||
else if (req.url.startsWith('/__mock/notifications')) data = {
|
||||
items: [{ id: 42, title: '套餐临期提醒', body: '您的套餐即将到期,请及时续费。', category: 'expiry', severity: 'warning', is_read: notificationRead, created_at: '2026-10-13T08:00:00+08:00' }], total: 1
|
||||
};
|
||||
}
|
||||
const conflict = req.url.startsWith('/__mock/auth/bind-phone');
|
||||
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
||||
res.end(JSON.stringify({ code: conflict ? 1192 : 0, msg: conflict ? '该资产已绑定手机号,如需更换请联系管理员' : 'success', data }));
|
||||
|
||||
@@ -351,13 +351,43 @@ test('home shows shared reminder once and excludes only expiry from old notifica
|
||||
};
|
||||
const asset = (await r.load('store/asset.js')).useAssetStore();
|
||||
asset.receiveExpiryReminder({ token: 'session-A', identifier: 'A' }, { remaining_days: 0, level: 'red' });
|
||||
const page = await r.load('pages/index/index.vue', ['visibleExpiryReminder', 'loadUnreadNotifications', 'notificationItems', 'closeExpiryReminder']);
|
||||
r.shown[0](); await r.tick();
|
||||
const page = await r.load('pages/index/index.vue', ['visibleExpiryReminder', 'loadUnreadNotifications', 'notificationItems', 'notificationPopupShow', 'closeExpiryReminder', 'onPopupCandidateClose']);
|
||||
await r.shown[0]();
|
||||
assert.equal(page.visibleExpiryReminder.value, null);
|
||||
assert.equal(asset.state.expiryReminder.shown, false);
|
||||
page.onPopupCandidateClose('empty'); await r.tick();
|
||||
assert.equal(page.visibleExpiryReminder.value.remaining_days, 0);
|
||||
await page.loadUnreadNotifications();
|
||||
assert.deepEqual(plain(page.notificationItems.value).map(item => item.id), [2]);
|
||||
assert.equal(page.notificationPopupShow.value, false);
|
||||
page.closeExpiryReminder();
|
||||
r.hidden[0](); r.shown[0](); await r.tick();
|
||||
assert.equal(page.notificationPopupShow.value, true);
|
||||
r.hidden[0](); await r.shown[0](); page.onPopupCandidateClose('empty'); await r.tick();
|
||||
assert.equal(page.visibleExpiryReminder.value, null);
|
||||
});
|
||||
|
||||
test('expiry popup waits for an open notification and closing it does not mark expiry read', async () => {
|
||||
const r = runtime();
|
||||
let markReads = 0;
|
||||
r.api.assetApi = { getInfo: async () => ({ identifier: 'A' }) };
|
||||
r.api.notificationApi = {
|
||||
getList: async () => ({ items: [{ id: 2, category: 'approval', is_read: false }] }),
|
||||
getUnreadCount: async () => ({ count: 1 }), markRead: async () => { markReads++; }
|
||||
};
|
||||
const page = await r.load('pages/index/index.vue', ['visibleExpiryReminder', 'notificationPopupShow', 'onPopupCandidateClose', 'closeNotificationPopup', 'closeExpiryReminder']);
|
||||
await r.shown[0](); page.onPopupCandidateClose('empty');
|
||||
await new Promise(resolve => setImmediate(resolve));
|
||||
assert.equal(page.notificationPopupShow.value, true);
|
||||
const asset = (await r.load('store/asset.js')).useAssetStore();
|
||||
asset.receiveExpiryReminder({ token: 'session-A', identifier: 'A' }, { remaining_days: 7, level: 'purple' });
|
||||
await r.tick();
|
||||
assert.equal(page.visibleExpiryReminder.value, null);
|
||||
assert.equal(asset.state.expiryReminder.shown, false);
|
||||
page.closeNotificationPopup();
|
||||
assert.equal(page.visibleExpiryReminder.value.remaining_days, 7);
|
||||
const previousReads = markReads;
|
||||
page.closeExpiryReminder();
|
||||
assert.equal(markReads, previousReads);
|
||||
assert.equal(page.visibleExpiryReminder.value, null);
|
||||
});
|
||||
|
||||
@@ -378,6 +408,26 @@ test('notification center refreshes server scope after read-all; failures keep u
|
||||
}
|
||||
});
|
||||
|
||||
test('clicking a notification card updates its read badge only after successful mark-read', async () => {
|
||||
for (const fail of [false, true]) {
|
||||
const r = runtime();
|
||||
let marked = false;
|
||||
r.api.notificationApi = {
|
||||
getUnreadCount: async () => ({ count: marked ? 0 : 1 }),
|
||||
markRead: async id => { assert.equal(id, 42); if (fail) throw new Error('offline'); marked = true; }
|
||||
};
|
||||
const page = await r.load('pages/notifications/notifications.vue', ['notifications', 'markItemRead', 'unreadCount']);
|
||||
page.notifications.push({ id: 42, is_read: false, title: '套餐提醒' });
|
||||
page.unreadCount.value = 1;
|
||||
await page.markItemRead(page.notifications[0]);
|
||||
assert.equal(page.notifications[0].is_read, !fail);
|
||||
assert.equal(page.unreadCount.value, fail ? 1 : 0);
|
||||
}
|
||||
const source = readFileSync(resolve(root, 'pages/notifications/notifications.vue'), 'utf8');
|
||||
assert.match(source, /item\.is_read \? '已读' : '未读'/);
|
||||
assert.doesNotMatch(source, /border-left|unread-dot/);
|
||||
});
|
||||
|
||||
test('notification center rejects an old asset response after switching context', async () => {
|
||||
const r = runtime();
|
||||
const oldList = deferred();
|
||||
|
||||
Reference in New Issue
Block a user