feat(12.1-03): align cloud browser contract — kind, provider_item_id, server freshness
- CloudFolderView: folders/files classified by kind=folder/file (remove is_dir) - CloudFolderView: navigateTo uses item.provider_item_id for route param (not item.id) - CloudFolderView: handleBreadcrumbNavigate uses named route; opaque refs intact - CloudFolderView: setBrowseState maps server freshness.refresh_state verbatim - CloudFolderView: refreshedAt = server last_refreshed_at (not new Date()) - CloudFolderView: breadcrumb lineage built explicitly from visited nodes - CloudProviderTreeItem: loadChildren filters by kind=folder (not is_dir) - CloudFolderTreeItem: expandable = kind===folder; icon = kind===folder - CloudFolderTreeItem: navigate() uses provider_item_id (not folder.id) - CloudFolderTreeItem: loadChildren uses provider_item_id as parent_ref - CloudFolderTreeItem: isActive checks provider_item_id first All 82 focused tests pass; full suite: 369/369
This commit is contained in:
@@ -189,6 +189,10 @@ describe('renders_kind_folder_and_file_in_shared_browser', () => {
|
||||
capturedFolders = this.folders
|
||||
capturedFiles = this.files
|
||||
},
|
||||
updated() {
|
||||
capturedFolders = this.folders
|
||||
capturedFiles = this.files
|
||||
},
|
||||
}
|
||||
const w = mount(CloudFolderView, {
|
||||
global: { stubs: { StorageBrowser: CapturingStub } },
|
||||
@@ -213,16 +217,20 @@ describe('folder_click_uses_provider_item_id_not_id', () => {
|
||||
capabilities: null,
|
||||
freshness: { refresh_state: 'fresh', last_refreshed_at: '2026-06-22T00:00:00Z' },
|
||||
})
|
||||
let navigateHandler = null
|
||||
let didEmit = false
|
||||
const CapturingStub = {
|
||||
template: '<div data-test="storage-browser" />',
|
||||
props: ['folders', 'files', 'mode', 'breadcrumb', 'uploadQueue', 'loading',
|
||||
'emptyMessage', 'emptyHint', 'capabilities', 'connectionRoot',
|
||||
'folderFreshness', 'lastRefreshedAt', 'byteAvailability'],
|
||||
emits: ['folder-navigate'],
|
||||
mounted() {
|
||||
// Simulate a folder-navigate event with the first folder item
|
||||
this.$emit('folder-navigate', this.folders[0])
|
||||
mounted() {},
|
||||
updated() {
|
||||
// Emit folder-navigate once folders are populated (after load completes)
|
||||
if (this.folders.length > 0 && !didEmit) {
|
||||
didEmit = true
|
||||
this.$emit('folder-navigate', this.folders[0])
|
||||
}
|
||||
},
|
||||
}
|
||||
const w = mount(CloudFolderView, {
|
||||
@@ -230,18 +238,29 @@ describe('folder_click_uses_provider_item_id_not_id', () => {
|
||||
})
|
||||
await flushPromises()
|
||||
// navigateTo must use provider_item_id for the route param, not DocuVault id
|
||||
// The route must contain provider_item_id value, not the uuid id
|
||||
expect(mockPush).toHaveBeenCalled()
|
||||
const pushArg = mockPush.mock.calls[0][0]
|
||||
// Must use provider_item_id in the route, not the DocuVault UUID
|
||||
if (typeof pushArg === 'string') {
|
||||
expect(pushArg).toContain(FOLDER_A.provider_item_id)
|
||||
expect(pushArg).not.toContain(FOLDER_A.id)
|
||||
} else if (typeof pushArg === 'object') {
|
||||
// Named route object — params must carry provider_item_id
|
||||
const paramValues = Object.values(pushArg.params ?? {})
|
||||
expect(paramValues.some(v => String(v).includes(FOLDER_A.provider_item_id) || v === FOLDER_A.provider_item_id)).toBe(true)
|
||||
}
|
||||
// Find the push call that carries provider_item_id (not the session-restore push)
|
||||
const hasPidRef = mockPush.mock.calls.some(call => {
|
||||
const arg = call[0]
|
||||
if (typeof arg === 'string') return arg.includes(FOLDER_A.provider_item_id)
|
||||
if (typeof arg === 'object') {
|
||||
const paramValues = Object.values(arg.params ?? {})
|
||||
return paramValues.some(v => String(v) === FOLDER_A.provider_item_id)
|
||||
}
|
||||
return false
|
||||
})
|
||||
expect(hasPidRef).toBe(true)
|
||||
// Must NOT use DocuVault UUID as route param
|
||||
const hasDocuVaultId = mockPush.mock.calls.some(call => {
|
||||
const arg = call[0]
|
||||
if (typeof arg === 'string') return arg.includes(FOLDER_A.id)
|
||||
if (typeof arg === 'object') {
|
||||
const paramValues = Object.values(arg.params ?? {})
|
||||
return paramValues.some(v => String(v) === FOLDER_A.id)
|
||||
}
|
||||
return false
|
||||
})
|
||||
expect(hasDocuVaultId).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -259,6 +278,7 @@ describe('stable_docuvault_id_remains_row_key', () => {
|
||||
'emptyMessage', 'emptyHint', 'capabilities', 'connectionRoot',
|
||||
'folderFreshness', 'lastRefreshedAt', 'byteAvailability'],
|
||||
mounted() { capturedFolders = this.folders },
|
||||
updated() { capturedFolders = this.folders },
|
||||
}
|
||||
const w = mount(CloudFolderView, {
|
||||
global: { stubs: { StorageBrowser: CapturingStub } },
|
||||
@@ -279,14 +299,22 @@ describe('opaque_reference_round_trip', () => {
|
||||
capabilities: null,
|
||||
freshness: { refresh_state: 'fresh', last_refreshed_at: '2026-06-22T00:00:00Z' },
|
||||
})
|
||||
let capturedFolders = []
|
||||
let didEmit = false
|
||||
const CapturingStub = {
|
||||
template: '<div data-test="storage-browser" />',
|
||||
props: ['folders', 'files', 'mode', 'breadcrumb', 'uploadQueue', 'loading',
|
||||
'emptyMessage', 'emptyHint', 'capabilities', 'connectionRoot',
|
||||
'folderFreshness', 'lastRefreshedAt', 'byteAvailability'],
|
||||
emits: ['folder-navigate'],
|
||||
mounted() {
|
||||
this.$emit('folder-navigate', this.folders[0])
|
||||
mounted() { capturedFolders = this.folders },
|
||||
updated() {
|
||||
capturedFolders = this.folders
|
||||
// Emit folder-navigate once folders are populated (after load completes)
|
||||
if (this.folders.length > 0 && !didEmit) {
|
||||
didEmit = true
|
||||
this.$emit('folder-navigate', this.folders[0])
|
||||
}
|
||||
},
|
||||
}
|
||||
const w = mount(CloudFolderView, {
|
||||
@@ -295,15 +323,19 @@ describe('opaque_reference_round_trip', () => {
|
||||
await flushPromises()
|
||||
// Navigation must use provider_item_id verbatim — no splitting or decoding
|
||||
expect(mockPush).toHaveBeenCalled()
|
||||
const pushArg = mockPush.mock.calls[0][0]
|
||||
// The provider_item_id must not be split or have reserved chars stripped
|
||||
if (typeof pushArg === 'object') {
|
||||
const paramValues = Object.values(pushArg.params ?? {})
|
||||
// At least one param must equal or contain the full opaque ref
|
||||
expect(
|
||||
paramValues.some(v => String(v) === FOLDER_OPAQUE.provider_item_id)
|
||||
).toBe(true)
|
||||
}
|
||||
// Find the push call that was triggered by folder-navigate (not the initial replace)
|
||||
const pushCalls = mockPush.mock.calls
|
||||
// At least one call should carry the opaque provider_item_id
|
||||
const hasOpaqueRef = pushCalls.some(call => {
|
||||
const arg = call[0]
|
||||
if (typeof arg === 'string') return arg.includes(FOLDER_OPAQUE.provider_item_id)
|
||||
if (typeof arg === 'object') {
|
||||
const paramValues = Object.values(arg.params ?? {})
|
||||
return paramValues.some(v => String(v) === FOLDER_OPAQUE.provider_item_id)
|
||||
}
|
||||
return false
|
||||
})
|
||||
expect(hasOpaqueRef).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user