/** * Property-based tests for config state management * **Feature: miniapp-frontend, Property 5: Popup Priority Logic** * **Feature: miniapp-frontend, Property 6: Daily Popup Display** * **Feature: miniapp-frontend, Property 7: Member Ad Visibility** * **Validates: Requirements 3.1, 3.2, 3.3, 3.4** */ import { describe, it, expect, beforeEach } from 'vitest' import * as fc from 'fast-check' import { setActivePinia, createPinia } from 'pinia' // Import mock before other modules import '../mocks/uni.js' import { useConfigStore, getTodayDateString } from '../../store/config.js' describe('Config Store Property Tests', () => { beforeEach(() => { // Create a fresh pinia instance for each test setActivePinia(createPinia()) // Clear storage uni._clearStorage() }) /** * Property 5: Popup Priority Logic * *For any* user state where genderPreference is not set AND isProfileCompleted is false, * the gender selection popup SHALL have highest display priority over other popups. * **Validates: Requirements 3.1** */ it('Property 5: Popup Priority Logic - gender popup has highest priority when conditions met', () => { fc.assert( fc.property( // Generate user states where gender is not set (0) and profile is not completed fc.record({ genderPreference: fc.constant(0), isProfileCompleted: fc.constant(false), isMember: fc.boolean() }), (userState) => { const configStore = useConfigStore() // Set up daily popup to ensure it would show if gender popup didn't take priority configStore.setDailyPopup({ id: 1, title: 'Test Popup' }) configStore.lastPopupDate = '' // Ensure daily popup would show // Check popup display configStore.checkPopupDisplay(userState) // Gender popup should be shown const genderPopupShown = configStore.showGenderPopup === true // Other popups should NOT be shown (gender has priority) const dailyPopupNotShown = configStore.showDailyPopup === false const memberAdNotShown = configStore.showMemberAd === false return genderPopupShown && dailyPopupNotShown && memberAdNotShown } ), { numRuns: 100 } ) }) /** * Property 5 (continued): Gender popup should NOT show when gender is selected OR profile is completed */ it('Property 5: Gender popup should not show when gender is selected or profile is completed', () => { fc.assert( fc.property( fc.oneof( // Gender is selected (1 or 2) fc.record({ genderPreference: fc.integer({ min: 1, max: 2 }), isProfileCompleted: fc.boolean(), isMember: fc.boolean() }), // Profile is completed fc.record({ genderPreference: fc.integer({ min: 0, max: 2 }), isProfileCompleted: fc.constant(true), isMember: fc.boolean() }) ), (userState) => { const configStore = useConfigStore() // Check popup display configStore.checkPopupDisplay(userState) // Gender popup should NOT be shown return configStore.showGenderPopup === false } ), { numRuns: 100 } ) }) /** * Property 6: Daily Popup Display * *For any* date comparison where the last shown date is before today, * the daily popup SHALL be displayed; otherwise it SHALL NOT be displayed. * **Validates: Requirements 3.2** */ it('Property 6: Daily Popup Display - should show when last shown date is before today', () => { fc.assert( fc.property( // Generate dates that are NOT today fc.date({ min: new Date('2020-01-01'), max: new Date('2025-12-30') }) .map(d => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`) .filter(dateStr => dateStr !== getTodayDateString()), (lastShownDate) => { const configStore = useConfigStore() const today = getTodayDateString() // Use the pure function to test const shouldShow = configStore.shouldShowDailyPopup(lastShownDate, today) // Should show because last shown date is not today return shouldShow === true } ), { numRuns: 100 } ) }) /** * Property 6 (continued): Should NOT show when already shown today */ it('Property 6: Daily Popup Display - should not show when already shown today', () => { const configStore = useConfigStore() const today = getTodayDateString() // When last shown date is today, should not show const shouldShow = configStore.shouldShowDailyPopup(today, today) expect(shouldShow).toBe(false) }) /** * Property 7: Member Ad Visibility * *For any* user state where isMember is false AND member ad has not been closed today, * the member advertisement SHALL be visible. * **Validates: Requirements 3.3, 3.4** */ it('Property 7: Member Ad Visibility - should show for non-members who have not closed ad today', () => { fc.assert( fc.property( // Generate dates that are NOT today (ad was closed on a different day) fc.oneof( fc.constant(''), // Never closed fc.date({ min: new Date('2020-01-01'), max: new Date('2025-12-30') }) .map(d => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`) .filter(dateStr => dateStr !== getTodayDateString()) ), (adClosedDate) => { const configStore = useConfigStore() const today = getTodayDateString() // Non-member, ad not closed today const shouldShow = configStore.shouldShowMemberAd(false, adClosedDate, today) return shouldShow === true } ), { numRuns: 100 } ) }) /** * Property 7 (continued): Should NOT show for members */ it('Property 7: Member Ad Visibility - should not show for members', () => { fc.assert( fc.property( fc.string(), // Any ad closed date (adClosedDate) => { const configStore = useConfigStore() const today = getTodayDateString() // Member should never see ad const shouldShow = configStore.shouldShowMemberAd(true, adClosedDate, today) return shouldShow === false } ), { numRuns: 100 } ) }) /** * Property 7 (continued): Should NOT show when ad was closed today */ it('Property 7: Member Ad Visibility - should not show when ad was closed today', () => { const configStore = useConfigStore() const today = getTodayDateString() // Non-member but ad was closed today const shouldShow = configStore.shouldShowMemberAd(false, today, today) expect(shouldShow).toBe(false) }) /** * Property: Closing member ad should prevent it from showing again today * Note: This test uses displayMode 2 (close for today) behavior */ it('Closing member ad should prevent it from showing again today', () => { fc.assert( fc.property( fc.record({ genderPreference: fc.integer({ min: 1, max: 2 }), // Gender selected isProfileCompleted: fc.constant(true), // Profile completed isMember: fc.constant(false) // Non-member }), (userState) => { const configStore = useConfigStore() const today = getTodayDateString() // Setup: Configure member ad with displayMode 2 (close for today) configStore.setMemberAdConfig({ status: 1, displayMode: 2 }) configStore.lastPopupDate = today // Daily popup already shown configStore.memberAdClosedDate = '' // Ad not closed configStore.memberAdClosedForever = false // First check - ad should show configStore.checkPopupDisplay(userState) const adShownFirst = configStore.showMemberAd === true // Close the ad configStore.closeMemberAd() // Check again - ad should not show configStore.checkPopupDisplay(userState) const adNotShownAfterClose = configStore.showMemberAd === false return adShownFirst && adNotShownAfterClose } ), { numRuns: 100 } ) }) })