mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
feat: refactor banner service and add comprehensive tests
- Remove legacy community support banner tracking variables and logic - Simplify banner dismissal handling by checking dismissal state before marking - Replace timer-based community support banner with immediate registration - Clean up unused constants and legacy storage keys - Add comprehensive test suite with mocked dependencies - Improve code maintainability and test coverage
This commit is contained in:
@@ -12,7 +12,6 @@ const COMMUNITY_SUPPORT_FIRST_SEEN_AT_KEY = 'community_support_banner_first_seen
|
||||
const COMMUNITY_SUPPORT_VERSION_KEY = 'community_support_banner_state_version';
|
||||
// Increment this version to reset the banner schedule after significant updates
|
||||
const COMMUNITY_SUPPORT_STATE_VERSION = 'v2';
|
||||
const COMMUNITY_SUPPORT_SHOWN_KEY_LEGACY = 'community_support_banner_shown';
|
||||
const KO_FI_URL = 'https://ko-fi.com/pixelpawsai';
|
||||
const AFDIAN_URL = 'https://afdian.com/a/pixelpawsai';
|
||||
const BANNER_HISTORY_KEY = 'banner_history';
|
||||
@@ -28,8 +27,6 @@ class BannerService {
|
||||
this.banners = new Map();
|
||||
this.container = null;
|
||||
this.initialized = false;
|
||||
this.communitySupportBannerTimer = null;
|
||||
this.communitySupportBannerRegistered = false;
|
||||
this.recentHistory = this.loadBannerHistory();
|
||||
this.bannerHistoryViewedAt = this.loadBannerHistoryViewedAt();
|
||||
|
||||
@@ -114,7 +111,9 @@ class BannerService {
|
||||
*/
|
||||
dismissBanner(bannerId) {
|
||||
const dismissedBanners = getStorageItem('dismissed_banners', []);
|
||||
if (!dismissedBanners.includes(bannerId)) {
|
||||
let bannerAlreadyDismissed = dismissedBanners.includes(bannerId);
|
||||
|
||||
if (!bannerAlreadyDismissed) {
|
||||
dismissedBanners.push(bannerId);
|
||||
setStorageItem('dismissed_banners', dismissedBanners);
|
||||
}
|
||||
@@ -135,7 +134,9 @@ class BannerService {
|
||||
}, 300);
|
||||
}
|
||||
|
||||
this.markBannerDismissed(bannerId);
|
||||
if (!bannerAlreadyDismissed) {
|
||||
this.markBannerDismissed(bannerId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -232,11 +233,6 @@ class BannerService {
|
||||
}
|
||||
|
||||
prepareCommunitySupportBanner() {
|
||||
if (this.communitySupportBannerTimer) {
|
||||
clearTimeout(this.communitySupportBannerTimer);
|
||||
this.communitySupportBannerTimer = null;
|
||||
}
|
||||
|
||||
if (this.isBannerDismissed(COMMUNITY_SUPPORT_BANNER_ID)) {
|
||||
return;
|
||||
}
|
||||
@@ -250,29 +246,17 @@ class BannerService {
|
||||
}
|
||||
|
||||
const availableAt = firstSeenAt + COMMUNITY_SUPPORT_BANNER_DELAY_MS;
|
||||
const delay = Math.max(availableAt - now, 0);
|
||||
|
||||
if (delay === 0) {
|
||||
|
||||
if (now >= availableAt) {
|
||||
this.registerCommunitySupportBanner();
|
||||
} else {
|
||||
this.communitySupportBannerTimer = setTimeout(() => {
|
||||
this.registerCommunitySupportBanner();
|
||||
}, delay);
|
||||
}
|
||||
}
|
||||
|
||||
registerCommunitySupportBanner() {
|
||||
if (this.communitySupportBannerRegistered || this.isBannerDismissed(COMMUNITY_SUPPORT_BANNER_ID)) {
|
||||
if (this.isBannerDismissed(COMMUNITY_SUPPORT_BANNER_ID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.communitySupportBannerTimer) {
|
||||
clearTimeout(this.communitySupportBannerTimer);
|
||||
this.communitySupportBannerTimer = null;
|
||||
}
|
||||
|
||||
this.communitySupportBannerRegistered = true;
|
||||
|
||||
// Determine support URL based on user language
|
||||
const currentLanguage = state.global.settings.language;
|
||||
const supportUrl = currentLanguage === 'zh-CN' ? AFDIAN_URL : KO_FI_URL;
|
||||
@@ -330,7 +314,6 @@ class BannerService {
|
||||
|
||||
setStorageItem(COMMUNITY_SUPPORT_VERSION_KEY, COMMUNITY_SUPPORT_STATE_VERSION);
|
||||
setStorageItem(COMMUNITY_SUPPORT_FIRST_SEEN_AT_KEY, Date.now());
|
||||
removeStorageItem(COMMUNITY_SUPPORT_SHOWN_KEY_LEGACY);
|
||||
}
|
||||
|
||||
loadBannerHistory() {
|
||||
|
||||
219
tests/frontend/managers/BannerService.test.js
Normal file
219
tests/frontend/managers/BannerService.test.js
Normal file
@@ -0,0 +1,219 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { bannerService } from '../../../static/js/managers/BannerService.js';
|
||||
import * as storageHelpers from '../../../static/js/utils/storageHelpers.js';
|
||||
import * as i18nHelpers from '../../../static/js/utils/i18nHelpers.js';
|
||||
import { state } from '../../../static/js/state/index.js';
|
||||
|
||||
// Mock storage helpers
|
||||
vi.mock('../../../static/js/utils/storageHelpers.js', () => ({
|
||||
getStorageItem: vi.fn(),
|
||||
setStorageItem: vi.fn(),
|
||||
removeStorageItem: vi.fn()
|
||||
}));
|
||||
|
||||
// Mock i18n helpers
|
||||
vi.mock('../../../static/js/utils/i18nHelpers.js', () => ({
|
||||
translate: vi.fn((key, params, defaultValue) => defaultValue || key)
|
||||
}));
|
||||
|
||||
// Mock state
|
||||
vi.mock('../../../static/js/state/index.js', () => ({
|
||||
state: {
|
||||
global: {
|
||||
settings: {
|
||||
language: 'en'
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
describe('BannerService', () => {
|
||||
beforeEach(() => {
|
||||
// Clear all mocks
|
||||
vi.clearAllMocks();
|
||||
|
||||
// Reset banner service state
|
||||
bannerService.banners.clear();
|
||||
bannerService.initialized = false;
|
||||
|
||||
// Clear DOM
|
||||
document.body.innerHTML = '<div id="banner-container"></div>';
|
||||
});
|
||||
|
||||
describe('Community Support Banner', () => {
|
||||
const COMMUNITY_SUPPORT_BANNER_ID = 'community-support';
|
||||
const COMMUNITY_SUPPORT_FIRST_SEEN_AT_KEY = 'community_support_banner_first_seen_at';
|
||||
const COMMUNITY_SUPPORT_VERSION_KEY = 'community_support_banner_state_version';
|
||||
|
||||
beforeEach(() => {
|
||||
// Mock the version check to avoid resetting state
|
||||
storageHelpers.getStorageItem.mockImplementation((key, defaultValue) => {
|
||||
if (key === COMMUNITY_SUPPORT_VERSION_KEY) {
|
||||
return 'v2'; // Current version
|
||||
}
|
||||
return defaultValue;
|
||||
});
|
||||
|
||||
// Initialize the banner service
|
||||
bannerService.initializeCommunitySupportState();
|
||||
});
|
||||
|
||||
it('should not show community support banner before 5 days have passed', () => {
|
||||
const now = Date.now();
|
||||
const firstSeenAt = now - (3 * 24 * 60 * 60 * 1000); // 3 days ago
|
||||
|
||||
storageHelpers.getStorageItem.mockImplementation((key, defaultValue) => {
|
||||
if (key === COMMUNITY_SUPPORT_FIRST_SEEN_AT_KEY) {
|
||||
return firstSeenAt;
|
||||
}
|
||||
if (key === COMMUNITY_SUPPORT_VERSION_KEY) {
|
||||
return 'v2';
|
||||
}
|
||||
if (key === 'dismissed_banners') {
|
||||
return [];
|
||||
}
|
||||
return defaultValue;
|
||||
});
|
||||
|
||||
// Mock Date.now to control time
|
||||
const originalNow = Date.now;
|
||||
global.Date.now = vi.fn(() => now);
|
||||
|
||||
try {
|
||||
bannerService.prepareCommunitySupportBanner();
|
||||
|
||||
// Banner should not be registered yet
|
||||
expect(bannerService.banners.has(COMMUNITY_SUPPORT_BANNER_ID)).toBe(false);
|
||||
} finally {
|
||||
global.Date.now = originalNow;
|
||||
}
|
||||
});
|
||||
|
||||
it('should show community support banner after 5 days have passed', () => {
|
||||
const now = Date.now();
|
||||
const firstSeenAt = now - (6 * 24 * 60 * 60 * 1000); // 6 days ago
|
||||
|
||||
storageHelpers.getStorageItem.mockImplementation((key, defaultValue) => {
|
||||
if (key === COMMUNITY_SUPPORT_FIRST_SEEN_AT_KEY) {
|
||||
return firstSeenAt;
|
||||
}
|
||||
if (key === COMMUNITY_SUPPORT_VERSION_KEY) {
|
||||
return 'v2';
|
||||
}
|
||||
if (key === 'dismissed_banners') {
|
||||
return [];
|
||||
}
|
||||
return defaultValue;
|
||||
});
|
||||
|
||||
// Mock Date.now to control time
|
||||
const originalNow = Date.now;
|
||||
global.Date.now = vi.fn(() => now);
|
||||
|
||||
try {
|
||||
bannerService.prepareCommunitySupportBanner();
|
||||
|
||||
// Banner should be registered
|
||||
expect(bannerService.banners.has(COMMUNITY_SUPPORT_BANNER_ID)).toBe(true);
|
||||
} finally {
|
||||
global.Date.now = originalNow;
|
||||
}
|
||||
});
|
||||
|
||||
it('should not show community support banner if it has been dismissed', () => {
|
||||
const now = Date.now();
|
||||
const firstSeenAt = now - (6 * 24 * 60 * 60 * 1000); // 6 days ago
|
||||
|
||||
storageHelpers.getStorageItem.mockImplementation((key, defaultValue) => {
|
||||
if (key === COMMUNITY_SUPPORT_FIRST_SEEN_AT_KEY) {
|
||||
return firstSeenAt;
|
||||
}
|
||||
if (key === COMMUNITY_SUPPORT_VERSION_KEY) {
|
||||
return 'v2';
|
||||
}
|
||||
if (key === 'dismissed_banners') {
|
||||
return [COMMUNITY_SUPPORT_BANNER_ID]; // Dismissed
|
||||
}
|
||||
return defaultValue;
|
||||
});
|
||||
|
||||
// Mock Date.now to control time
|
||||
const originalNow = Date.now;
|
||||
global.Date.now = vi.fn(() => now);
|
||||
|
||||
try {
|
||||
bannerService.prepareCommunitySupportBanner();
|
||||
|
||||
// Banner should not be registered because it's dismissed
|
||||
expect(bannerService.banners.has(COMMUNITY_SUPPORT_BANNER_ID)).toBe(false);
|
||||
} finally {
|
||||
global.Date.now = originalNow;
|
||||
}
|
||||
});
|
||||
|
||||
it('should set first seen time if not already set', () => {
|
||||
const now = Date.now();
|
||||
|
||||
storageHelpers.getStorageItem.mockImplementation((key, defaultValue) => {
|
||||
if (key === COMMUNITY_SUPPORT_FIRST_SEEN_AT_KEY) {
|
||||
return null; // Not set
|
||||
}
|
||||
if (key === COMMUNITY_SUPPORT_VERSION_KEY) {
|
||||
return 'v2';
|
||||
}
|
||||
if (key === 'dismissed_banners') {
|
||||
return [];
|
||||
}
|
||||
return defaultValue;
|
||||
});
|
||||
|
||||
// Mock Date.now to control time
|
||||
const originalNow = Date.now;
|
||||
global.Date.now = vi.fn(() => now);
|
||||
|
||||
try {
|
||||
bannerService.prepareCommunitySupportBanner();
|
||||
|
||||
// Should have set the first seen time
|
||||
expect(storageHelpers.setStorageItem).toHaveBeenCalledWith(
|
||||
COMMUNITY_SUPPORT_FIRST_SEEN_AT_KEY,
|
||||
now
|
||||
);
|
||||
} finally {
|
||||
global.Date.now = originalNow;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('Banner Dismissal', () => {
|
||||
it('should add banner to dismissed_banners array when dismissed', () => {
|
||||
storageHelpers.getStorageItem.mockImplementation((key, defaultValue) => {
|
||||
if (key === 'dismissed_banners') {
|
||||
return [];
|
||||
}
|
||||
return defaultValue;
|
||||
});
|
||||
|
||||
bannerService.dismissBanner('test-banner');
|
||||
|
||||
expect(storageHelpers.setStorageItem).toHaveBeenCalledWith(
|
||||
'dismissed_banners',
|
||||
['test-banner']
|
||||
);
|
||||
});
|
||||
|
||||
it('should not add duplicate banner IDs to dismissed_banners array', () => {
|
||||
storageHelpers.getStorageItem.mockImplementation((key, defaultValue) => {
|
||||
if (key === 'dismissed_banners') {
|
||||
return ['test-banner'];
|
||||
}
|
||||
return defaultValue;
|
||||
});
|
||||
|
||||
bannerService.dismissBanner('test-banner');
|
||||
|
||||
// Should not have been called again since it's already dismissed
|
||||
expect(storageHelpers.setStorageItem).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user