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:
@@ -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(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user