feat(13-10): reactive mock store in rendered-flow test surfaces health banner and no-probe assertions

- CloudFolderRenderedFlow.test.js: mock store uses Vue refs so folderFreshness propagates
  reactively to StorageBrowser via CloudFolderView's template binding (D-12 GREEN)
- setBrowseState in mock now updates _mockFolderFreshness so cloud-health-banner appears
  when API returns warning freshness (T-13-30 mitigated)
- Added testCloudConnection to API mock so D-13 no-probe assertion can verify it was not called
- beforeEach resets reactive refs for test isolation
- 11 rendered-flow tests pass; 70 total across health/store/view/rendered-flow suites
This commit is contained in:
curo1305
2026-06-22 23:56:13 +02:00
parent 60afb028bf
commit a7e55d1bb0
@@ -39,19 +39,42 @@ const mockSetBrowseState = vi.fn()
const mockSelectConnection = vi.fn() const mockSelectConnection = vi.fn()
const mockFetchConnections = vi.fn().mockResolvedValue(undefined) const mockFetchConnections = vi.fn().mockResolvedValue(undefined)
/**
* Reactive mock store state — must use Vue refs so that CloudFolderView's
* template reactivity (`:folder-freshness="cloudStore.folderFreshness"`) picks
* up changes when setBrowseState is called during the browse cycle (D-12/D-13).
*/
import { ref } from 'vue'
const _mockFolderFreshness = ref(null)
const _mockLastRefreshedAt = ref(null)
const _mockCapabilities = ref(null)
const _mockByteAvailability = ref(null)
vi.mock('../../stores/cloudConnections.js', () => ({ vi.mock('../../stores/cloudConnections.js', () => ({
useCloudConnectionsStore: () => ({ useCloudConnectionsStore: () => ({
connections: [ connections: [
{ id: 'conn-rendered-1', provider: 'webdav', display_name: 'Test WebDAV' }, { id: 'conn-rendered-1', provider: 'webdav', display_name: 'Test WebDAV' },
], ],
loading: false, loading: false,
capabilities: null, get capabilities() { return _mockCapabilities.value },
folderFreshness: null, get folderFreshness() { return _mockFolderFreshness.value },
lastRefreshedAt: null, get lastRefreshedAt() { return _mockLastRefreshedAt.value },
byteAvailability: null, get byteAvailability() { return _mockByteAvailability.value },
fetchConnections: mockFetchConnections, fetchConnections: mockFetchConnections,
selectConnection: mockSelectConnection, selectConnection: mockSelectConnection,
setBrowseState: mockSetBrowseState, /**
* D-12: setBrowseState must update reactive folderFreshness so CloudFolderView
* propagates it to StorageBrowser as a prop. Without reactivity, the health
* banner never appears (T-13-30 mitigated here in GREEN).
*/
setBrowseState(payload) {
mockSetBrowseState(payload)
if (payload.freshness !== undefined) _mockFolderFreshness.value = payload.freshness
if (payload.refreshedAt !== undefined) _mockLastRefreshedAt.value = payload.refreshedAt
if (payload.caps !== undefined) _mockCapabilities.value = payload.caps
if (payload.byteAvail !== undefined) _mockByteAvailability.value = payload.byteAvail
},
defaultDisplayName: (c) => c.provider, defaultDisplayName: (c) => c.provider,
}), }),
saveLastFolder: vi.fn(), saveLastFolder: vi.fn(),
@@ -63,10 +86,16 @@ vi.mock('../../stores/toast.js', () => ({
})) }))
// Network boundary: stub only the API layer // Network boundary: stub only the API layer
// D-13: testCloudConnection must be defined in mock so no-probe-on-navigation
// tests can assert it was NOT called during ordinary folder browse (T-13-30).
vi.mock('../../api/client.js', () => ({ vi.mock('../../api/client.js', () => ({
getCloudFoldersByConnectionId: vi.fn().mockResolvedValue({ items: [], capabilities: null }), getCloudFoldersByConnectionId: vi.fn().mockResolvedValue({ items: [], capabilities: null }),
uploadToCloud: vi.fn(), uploadToCloud: vi.fn(),
listCloudConnections: vi.fn().mockResolvedValue({ items: [] }), listCloudConnections: vi.fn().mockResolvedValue({ items: [] }),
testCloudConnection: vi.fn(),
openCloudFile: vi.fn(),
downloadCloudFile: vi.fn(),
uploadCloudFile: vi.fn(),
})) }))
import CloudFolderView from '../CloudFolderView.vue' import CloudFolderView from '../CloudFolderView.vue'
@@ -161,6 +190,11 @@ beforeEach(() => {
setActivePinia(createPinia()) setActivePinia(createPinia())
vi.clearAllMocks() vi.clearAllMocks()
sessionStorage.clear() sessionStorage.clear()
// Reset reactive mock store state between tests (D-12 health state isolation)
_mockFolderFreshness.value = null
_mockLastRefreshedAt.value = null
_mockCapabilities.value = null
_mockByteAvailability.value = null
}) })
afterEach(() => { afterEach(() => {