Skip to content

Commit

Permalink
Make userFileStore handle opened files (#300)
Browse files Browse the repository at this point in the history
  • Loading branch information
huchenlei authored Aug 5, 2024
1 parent 0b6ca50 commit 3fc847c
Show file tree
Hide file tree
Showing 2 changed files with 276 additions and 7 deletions.
114 changes: 107 additions & 7 deletions src/stores/userFileStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,26 @@ import { api } from '@/scripts/api'
import { TreeNode } from 'primevue/treenode'
import { defaultGraph } from '@/scripts/defaultGraph'

export interface OpenFile {
path: string
content: string
isModified: boolean
originalContent: string
}

export const useUserFileStore = defineStore('userFile', {
state: () => ({
files: [] as string[]
files: [] as string[],
openFiles: [] as OpenFile[]
}),
getters: {
workflowsTree(): TreeNode {
getOpenFile: (state) => (path: string) => {
return state.openFiles.find((file) => file.path === path)
},
modifiedFiles: (state) => {
return state.openFiles.filter((file) => file.isModified)
},
workflowsTree(state): TreeNode {
const rootPath = 'workflows'
const root: TreeNode = {
key: rootPath,
Expand All @@ -18,7 +32,7 @@ export const useUserFileStore = defineStore('userFile', {
children: []
}

for (const filePath of this.files) {
for (const filePath of state.files) {
const parts = filePath.split('/')
let current = root
let key = rootPath
Expand Down Expand Up @@ -48,16 +62,99 @@ export const useUserFileStore = defineStore('userFile', {
}
},
actions: {
async openFile(path: string) {
if (this.getOpenFile(path)) return

const { success, data } = await this.getFileData(path)
if (success) {
this.openFiles.push({
path,
content: data,
isModified: false,
originalContent: data
})
}
},
closeFile(path: string) {
const index = this.openFiles.findIndex(
(file: OpenFile) => file.path === path
)
if (index !== -1) {
this.openFiles.splice(index, 1)
}
},
updateFileContent(path: string, newContent: string) {
const file = this.getOpenFile(path)
if (file) {
file.content = newContent
file.isModified = file.content !== file.originalContent
}
},
async saveOpenFile(path: string) {
const file = this.getOpenFile(path)
console.error(file)
if (file && file.isModified) {
const result = await this.saveFile(path, file.content)
console.error(result)
if (result.success) {
file.isModified = false
file.originalContent = file.content
}
return result
}
return { success: true }
},
discardChanges(path: string) {
const file = this.getOpenFile(path)
if (file) {
file.content = file.originalContent
file.isModified = false
}
},
async loadFiles(dir: string = 'workflows') {
this.files = (await api.listUserData(dir, true, false)).map(
(filePath: string) => filePath.replaceAll('\\', '/')
)

// Update openFiles to reflect changes in the file system
this.openFiles = await Promise.all(
this.openFiles.map(async (openFile) => {
if (!this.files.includes(openFile.path)) {
// File has been deleted from the file system
return null
}

// Check if file content has changed
const { success, data } = await this.getFileData(openFile.path)
if (success && data !== openFile.originalContent) {
// File has been modified in the file system
return {
...openFile,
content: data,
originalContent: data,
isModified: openFile.content !== data
}
}

return openFile
})
)

// Remove null entries (deleted files)
this.openFiles = this.openFiles.filter((file) => file !== null)
},
async renameFile(oldPath: string, newPath: string) {
const resp = await api.moveUserData(oldPath, newPath)
if (resp.status !== 200) {
return { success: false, message: resp.statusText }
}
// Update openFiles state
const openFile = this.openFiles.find(
(file: OpenFile) => file.path === oldPath
)
if (openFile) {
openFile.path = newPath
}
await this.loadFiles()
return { success: true }
},
Expand All @@ -69,13 +166,16 @@ export const useUserFileStore = defineStore('userFile', {
message: `Error removing user data file '${path}': ${resp.status} ${resp.statusText}`
}
}
// Remove from openFiles if it's open
const index = this.openFiles.findIndex(
(file: OpenFile) => file.path === path
)
if (index !== -1) {
this.openFiles.splice(index, 1)
}
await this.loadFiles()
return { success: true }
},
async moveFile(sourcePath: string, destPath: string) {
await api.moveUserData(sourcePath, destPath)
await this.loadFiles()
},
async saveFile(path: string, data: string) {
const resp = await api.storeUserData(path, data, {
stringify: false,
Expand Down
169 changes: 169 additions & 0 deletions tests-ui/tests/stores/userFileStore.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import { setActivePinia, createPinia } from 'pinia'
import { useUserFileStore } from '@/stores/userFileStore'
import { api } from '@/scripts/api'

// Mock the api
jest.mock('@/scripts/api', () => ({
api: {
listUserData: jest.fn(),
moveUserData: jest.fn(),
deleteUserData: jest.fn(),
storeUserData: jest.fn(),
getUserData: jest.fn()
}
}))

describe('useUserFileStore', () => {
let store: ReturnType<typeof useUserFileStore>

beforeEach(() => {
setActivePinia(createPinia())
store = useUserFileStore()
})

it('should open a file', async () => {
const mockFileData = { success: true, data: 'file content' }
;(api.getUserData as jest.Mock).mockResolvedValue({
status: 200,
json: () => mockFileData.data
})

await store.openFile('test.txt')

expect(store.openFiles).toHaveLength(1)
expect(store.openFiles[0]).toEqual({
path: 'test.txt',
content: 'file content',
isModified: false,
originalContent: 'file content'
})
})

it('should close a file', () => {
store.openFiles = [
{
path: 'test.txt',
content: 'content',
isModified: false,
originalContent: 'content'
}
]

store.closeFile('test.txt')

expect(store.openFiles).toHaveLength(0)
})

it('should update file content', () => {
store.openFiles = [
{
path: 'test.txt',
content: 'old content',
isModified: false,
originalContent: 'old content'
}
]

store.updateFileContent('test.txt', 'new content')

expect(store.openFiles[0].content).toBe('new content')
expect(store.openFiles[0].isModified).toBe(true)
})

it('should save an open file', async () => {
store.openFiles = [
{
path: 'test.txt',
content: 'modified content',
isModified: true,
originalContent: 'original content'
}
]
;(api.storeUserData as jest.Mock).mockResolvedValue({ status: 200 })
;(api.listUserData as jest.Mock).mockResolvedValue(['test.txt'])
;(api.getUserData as jest.Mock).mockResolvedValue({
status: 200,
json: () => 'modified content'
})

await store.saveOpenFile('test.txt')

expect(store.openFiles[0].isModified).toBe(false)
expect(store.openFiles[0].originalContent).toBe('modified content')
})

it('should discard changes', () => {
store.openFiles = [
{
path: 'test.txt',
content: 'modified content',
isModified: true,
originalContent: 'original content'
}
]

store.discardChanges('test.txt')

expect(store.openFiles[0].content).toBe('original content')
expect(store.openFiles[0].isModified).toBe(false)
})

it('should load files', async () => {
;(api.listUserData as jest.Mock).mockResolvedValue([
'file1.txt',
'file2.txt'
])

await store.loadFiles()

expect(store.files).toEqual(['file1.txt', 'file2.txt'])
})

it('should rename a file', async () => {
store.openFiles = [
{
path: 'oldfile.txt',
content: 'content',
isModified: false,
originalContent: 'content'
}
]
;(api.moveUserData as jest.Mock).mockResolvedValue({ status: 200 })
;(api.listUserData as jest.Mock).mockResolvedValue(['newfile.txt'])

await store.renameFile('oldfile.txt', 'newfile.txt')

expect(store.openFiles[0].path).toBe('newfile.txt')
expect(store.files).toEqual(['newfile.txt'])
})

it('should delete a file', async () => {
store.openFiles = [
{
path: 'file.txt',
content: 'content',
isModified: false,
originalContent: 'content'
}
]
;(api.deleteUserData as jest.Mock).mockResolvedValue({ status: 204 })
;(api.listUserData as jest.Mock).mockResolvedValue([])

await store.deleteFile('file.txt')

expect(store.openFiles).toHaveLength(0)
expect(store.files).toEqual([])
})

it('should get file data', async () => {
const mockFileData = { content: 'file content' }
;(api.getUserData as jest.Mock).mockResolvedValue({
status: 200,
json: () => mockFileData
})

const result = await store.getFileData('test.txt')

expect(result).toEqual({ success: true, data: mockFileData })
})
})

0 comments on commit 3fc847c

Please sign in to comment.