refactor: replace "folder" with "directory" across file handling components and tests

This commit is contained in:
Justin Lin committed 2025-04-24 21:24:55 +08:00
1 parent 7e50c8e06c
commit cdd22634e0
18 files changed
+149 -103

No files matched your search

+2 -2
View File
@@ -22,8 +22,8 @@ function nodeClass() {
<template> <template>
<a href="#" :class="nodeClass()"> <a href="#" :class="nodeClass()">
<font-awesome-icon <font-awesome-icon
v-if="node.type === 'folder'" v-if="node.type === 'directory'"
:icon="['far', 'folder']" :icon="['far', 'directory']"
class="text-text-primary size-8" class="text-text-primary size-8"
/> />
<font-awesome-icon <font-awesome-icon
+1 -1
View File
@@ -9,7 +9,7 @@ async function collectFilesInfo(inputPath, level = 0, hiddenFiles = false) {
{ {
path: inputPath, path: inputPath,
name, name,
type: stats.isDirectory() ? 'folder' : 'file', type: stats.isDirectory() ? 'directory' : 'file',
discovered, discovered,
}, },
] ]
+3 -3
View File
@@ -2,14 +2,14 @@ import { basename } from './utils.js'
export interface FileObject { export interface FileObject {
path: string path: string
type: string type: 'file' | 'directory'
discovered: boolean discovered: boolean
} }
export default class File implements FileObject { export default class File implements FileObject {
path: string path: string
name: string name: string
type: string type: 'file' | 'directory'
discovered: boolean discovered: boolean
constructor(obj: FileObject) { constructor(obj: FileObject) {
@@ -20,7 +20,7 @@ export default class File implements FileObject {
} }
isDirectory() { isDirectory() {
return this.type === 'folder' return this.type === 'directory'
} }
contains(file: FileObject) { contains(file: FileObject) {
+1 -1
View File
@@ -20,7 +20,7 @@ export default class FileGraph extends FileNode {
remove(files: FileObject[]) { remove(files: FileObject[]) {
// Logic to remove a file node from the graph // Logic to remove a file node from the graph
// If the file node is a folder, also remove all children // If the file node is a directory, also remove all children
// If the file node doesn't exist, throw an error // If the file node doesn't exist, throw an error
files.forEach((file) => { files.forEach((file) => {
+1 -1
View File
@@ -45,7 +45,7 @@ export default class FileNode extends File {
} else if (makeParents) { } else if (makeParents) {
const parent = new FileNode({ const parent = new FileNode({
path: parentPath, path: parentPath,
type: 'folder', type: 'directory',
discovered: false, discovered: false,
children: [file], children: [file],
}) })
+1 -1
View File
@@ -4,7 +4,7 @@ import File from '../src/file.js'
describe('File', () => { describe('File', () => {
let file: File let file: File
beforeEach(() => { beforeEach(() => {
file = new File({ path: '/home/user', type: 'folder', discovered: true }) file = new File({ path: '/home/user', type: 'directory', discovered: true })
}) })
const filesToCompare = [ const filesToCompare = [
+17 -16
View File
@@ -1,6 +1,7 @@
import { describe, test, expect } from '@jest/globals' import { describe, test, expect } from '@jest/globals'
import FileGraph from '../src/fileGraph.js' import FileGraph from '../src/fileGraph.js'
import FileNode from '../src/fileNode.js' import FileNode from '../src/fileNode.js'
import { FileObject } from '../src/file.js'
describe('FileGraph', () => { describe('FileGraph', () => {
let root: FileNode let root: FileNode
@@ -8,7 +9,7 @@ describe('FileGraph', () => {
beforeEach(() => { beforeEach(() => {
root = new FileNode({ root = new FileNode({
path: '/', path: '/',
type: 'folder', type: 'directory',
discovered: true, discovered: true,
}) })
}) })
@@ -16,7 +17,7 @@ describe('FileGraph', () => {
test('add files to graph', () => { test('add files to graph', () => {
const fileGraph = new FileGraph(root) const fileGraph = new FileGraph(root)
const filesToAdd = [ const filesToAdd: FileObject[] = [
{ {
path: '/home/user/file1.txt', path: '/home/user/file1.txt',
type: 'file', type: 'file',
@@ -24,7 +25,7 @@ describe('FileGraph', () => {
}, },
{ {
path: '/home/user/folder1', path: '/home/user/folder1',
type: 'folder', type: 'directory',
discovered: true, discovered: true,
}, },
] ]
@@ -41,12 +42,12 @@ describe('FileGraph', () => {
const filesToAdd = [ const filesToAdd = [
{ {
path: '/home/user/folder1', path: '/home/user/folder1',
type: 'folder', type: 'directory',
discovered: true, discovered: true,
}, },
{ {
path: '/home/user/folder2', path: '/home/user/folder2',
type: 'folder', type: 'directory',
discovered: true, discovered: true,
}, },
{ {
@@ -61,22 +62,22 @@ describe('FileGraph', () => {
}, },
] ]
fileGraph.add(filesToAdd) fileGraph.add(filesToAdd as FileObject[])
const filesToRemove = [ const filesToRemove = [
{ {
path: '/home/user/folder1/file1.txt', path: '/home/user/folder1/file1.txt',
type: 'folder', type: 'directory',
discovered: true, discovered: true,
}, },
{ {
path: '/home/user/folder2', path: '/home/user/folder2',
type: 'folder', type: 'directory',
discovered: true, discovered: true,
}, },
] ]
fileGraph.remove(filesToRemove) fileGraph.remove(filesToRemove as FileObject[])
expect(fileGraph.children[0].children[0].children.length).toBe(1) expect(fileGraph.children[0].children[0].children.length).toBe(1)
expect(fileGraph.children[0].children[0].children[0].name).toBe('folder1') expect(fileGraph.children[0].children[0].children[0].name).toBe('folder1')
@@ -90,7 +91,7 @@ describe('FileGraph', () => {
const discoverFiles = [ const discoverFiles = [
{ {
path: '/home/user/folder1', path: '/home/user/folder1',
type: 'folder', type: 'directory',
discovered: true, discovered: true,
}, },
{ {
@@ -110,7 +111,7 @@ describe('FileGraph', () => {
}, },
] ]
fileGraph.discover(discoverFiles) fileGraph.discover(discoverFiles as FileObject[])
expect(fileGraph.children[0].children[0].children[0].children.length).toBe( expect(fileGraph.children[0].children[0].children[0].children.length).toBe(
3 3
) )
@@ -122,12 +123,12 @@ describe('FileGraph', () => {
const filesToAdd = [ const filesToAdd = [
{ {
path: '/home/user/folder1', path: '/home/user/folder1',
type: 'folder', type: 'directory',
discovered: true, discovered: true,
}, },
{ {
path: '/home/user/folder2', path: '/home/user/folder2',
type: 'folder', type: 'directory',
discovered: true, discovered: true,
}, },
{ {
@@ -142,12 +143,12 @@ describe('FileGraph', () => {
}, },
] ]
fileGraph.add(filesToAdd) fileGraph.add(filesToAdd as FileObject[])
const newFiles = [ const newFiles = [
{ {
path: '/home/user/folder1', path: '/home/user/folder1',
type: 'folder', type: 'directory',
discovered: true, discovered: true,
}, },
{ {
@@ -167,7 +168,7 @@ describe('FileGraph', () => {
}, },
] ]
fileGraph.discover(newFiles) fileGraph.discover(newFiles as FileObject[])
expect(fileGraph.children[0].children[0].children.length).toBe(2) expect(fileGraph.children[0].children[0].children.length).toBe(2)
expect(fileGraph.children[0].children[0].children[0].children.length).toBe( expect(fileGraph.children[0].children[0].children[0].children.length).toBe(
+30 -30
View File
@@ -43,7 +43,7 @@ describe('FileNode', () => {
rootNode = new FileNode({ rootNode = new FileNode({
path: '/home/rudeus', path: '/home/rudeus',
name: 'rudeus', name: 'rudeus',
type: 'folder', type: 'directory',
discovered: true, discovered: true,
}) })
}) })
@@ -51,7 +51,7 @@ describe('FileNode', () => {
test('should throw error when adding duplicate item', () => { test('should throw error when adding duplicate item', () => {
const child = { const child = {
path: '/home/rudeus/test', path: '/home/rudeus/test',
type: 'folder', type: 'directory',
discovered: true, discovered: true,
} }
@@ -70,7 +70,7 @@ describe('FileNode', () => {
const child = { const child = {
path: '/home/rudeus/alphabet/test', path: '/home/rudeus/alphabet/test',
name: 'test', name: 'test',
type: 'folder', type: 'directory',
discovered: true, discovered: true,
} }
@@ -91,7 +91,7 @@ describe('FileNode', () => {
{ {
path: '/home/rudeus/test', path: '/home/rudeus/test',
name: 'test', name: 'test',
type: 'folder', type: 'directory',
discovered: true, discovered: true,
}, },
{ {
@@ -131,21 +131,21 @@ describe('FileNode', () => {
test('merge-subtree', () => { test('merge-subtree', () => {
const root = { const root = {
path: '/', path: '/',
type: 'folder', type: 'directory',
discovered: true, discovered: true,
} }
const files1 = [ const files1 = [
root, root,
{ path: '/a', type: 'file', discovered: true }, { path: '/a', type: 'file', discovered: true },
{ path: '/b', type: 'folder', discovered: true }, { path: '/b', type: 'directory', discovered: true },
{ path: '/b/c', type: 'folder', discovered: true }, { path: '/b/c', type: 'directory', discovered: true },
{ path: '/b/d', type: 'file', discovered: true }, { path: '/b/d', type: 'file', discovered: true },
{ path: '/b/c/f', type: 'file', discovered: true }, { path: '/b/c/f', type: 'file', discovered: true },
] ]
const files2 = [ const files2 = [
{ path: '/b', type: 'folder', discovered: true }, { path: '/b', type: 'directory', discovered: true },
{ path: '/b/c', type: 'folder', discovered: true }, { path: '/b/c', type: 'directory', discovered: true },
{ path: '/b/e', type: 'file', discovered: true }, { path: '/b/e', type: 'file', discovered: true },
{ path: '/b/c/g', type: 'file', discovered: true }, { path: '/b/c/g', type: 'file', discovered: true },
] ]
@@ -153,8 +153,8 @@ describe('FileNode', () => {
const expected = [ const expected = [
root, root,
{ path: '/a', type: 'file', discovered: true }, { path: '/a', type: 'file', discovered: true },
{ path: '/b', type: 'folder', discovered: true }, { path: '/b', type: 'directory', discovered: true },
{ path: '/b/c', type: 'folder', discovered: true }, { path: '/b/c', type: 'directory', discovered: true },
{ path: '/b/e', type: 'file', discovered: true }, { path: '/b/e', type: 'file', discovered: true },
{ path: '/b/c/g', type: 'file', discovered: true }, { path: '/b/c/g', type: 'file', discovered: true },
] ]
@@ -168,29 +168,29 @@ describe('FileNode', () => {
// The original tree should keep the children that are not in the node to merge // The original tree should keep the children that are not in the node to merge
const root = { const root = {
path: '/', path: '/',
type: 'folder', type: 'directory',
discovered: true, discovered: true,
} }
const files1 = [ const files1 = [
root, root,
{ path: '/a', type: 'file', discovered: true }, { path: '/a', type: 'file', discovered: true },
{ path: '/b', type: 'folder', discovered: true }, { path: '/b', type: 'directory', discovered: true },
{ path: '/b/c', type: 'folder', discovered: true }, { path: '/b/c', type: 'directory', discovered: true },
{ path: '/b/d', type: 'file', discovered: true }, { path: '/b/d', type: 'file', discovered: true },
{ path: '/b/c/f', type: 'file', discovered: true }, { path: '/b/c/f', type: 'file', discovered: true },
] ]
const files2 = [ const files2 = [
{ path: '/b', type: 'folder', discovered: true }, { path: '/b', type: 'directory', discovered: true },
{ path: '/b/c', type: 'folder', discovered: true }, { path: '/b/c', type: 'directory', discovered: true },
{ path: '/b/e', type: 'file', discovered: true }, { path: '/b/e', type: 'file', discovered: true },
] ]
const expected = [ const expected = [
root, root,
{ path: '/a', type: 'file', discovered: true }, { path: '/a', type: 'file', discovered: true },
{ path: '/b', type: 'folder', discovered: true }, { path: '/b', type: 'directory', discovered: true },
{ path: '/b/c', type: 'folder', discovered: true }, { path: '/b/c', type: 'directory', discovered: true },
{ path: '/b/e', type: 'file', discovered: true }, { path: '/b/e', type: 'file', discovered: true },
{ path: '/b/c/f', type: 'file', discovered: true }, { path: '/b/c/f', type: 'file', discovered: true },
] ]
@@ -201,19 +201,19 @@ describe('FileNode', () => {
test('merge-hidden', () => { test('merge-hidden', () => {
const root = { const root = {
path: '/', path: '/',
type: 'folder', type: 'directory',
discovered: true, discovered: true,
} }
const files1 = [ const files1 = [
root, root,
{ path: '/a', type: 'file', discovered: true }, { path: '/a', type: 'file', discovered: true },
{ path: '/.b', type: 'file', discovered: false }, { path: '/.b', type: 'file', discovered: false },
{ path: '/c', type: 'folder', discovered: true }, { path: '/c', type: 'directory', discovered: true },
{ path: '/c/.d', type: 'file', discovered: false }, { path: '/c/.d', type: 'file', discovered: false },
] ]
const files2 = [ const files2 = [
{ path: '/c', type: 'folder', discovered: true }, { path: '/c', type: 'directory', discovered: true },
{ path: '/c/.d', type: 'file', discovered: true }, { path: '/c/.d', type: 'file', discovered: true },
] ]
@@ -221,7 +221,7 @@ describe('FileNode', () => {
root, root,
{ path: '/a', type: 'file', discovered: true }, { path: '/a', type: 'file', discovered: true },
{ path: '/.b', type: 'file', discovered: false }, { path: '/.b', type: 'file', discovered: false },
{ path: '/c', type: 'folder', discovered: true }, { path: '/c', type: 'directory', discovered: true },
{ path: '/c/.d', type: 'file', discovered: true }, { path: '/c/.d', type: 'file', discovered: true },
] ]
@@ -231,24 +231,24 @@ describe('FileNode', () => {
test('remove-child', () => { test('remove-child', () => {
const root = { const root = {
path: '/', path: '/',
type: 'folder', type: 'directory',
discovered: true, discovered: true,
} }
const files = [ const files = [
root, root,
{ path: '/a', type: 'file', discovered: true }, { path: '/a', type: 'file', discovered: true },
{ path: '/b', type: 'folder', discovered: true }, { path: '/b', type: 'directory', discovered: true },
{ path: '/b/c', type: 'folder', discovered: true }, { path: '/b/c', type: 'directory', discovered: true },
{ path: '/b/d', type: 'file', discovered: true }, { path: '/b/d', type: 'file', discovered: true },
{ path: '/b/c/f', type: 'file', discovered: true }, { path: '/b/c/f', type: 'file', discovered: true },
] ]
const fileToRemove = { path: '/b/c', type: 'folder', discovered: true } const fileToRemove = { path: '/b/c', type: 'directory', discovered: true }
const expected = [ const expected = [
root, root,
{ path: '/a', type: 'file', discovered: true }, { path: '/a', type: 'file', discovered: true },
{ path: '/b', type: 'folder', discovered: true }, { path: '/b', type: 'directory', discovered: true },
{ path: '/b/d', type: 'file', discovered: true }, { path: '/b/d', type: 'file', discovered: true },
] ]
@@ -259,7 +259,7 @@ describe('FileNode', () => {
test('load nested fileNode', () => { test('load nested fileNode', () => {
const data = { const data = {
path: '/', path: '/',
type: 'folder', type: 'directory',
discovered: true, discovered: true,
children: [ children: [
{ {
@@ -269,12 +269,12 @@ test('load nested fileNode', () => {
}, },
{ {
path: '/b', path: '/b',
type: 'folder', type: 'directory',
discovered: true, discovered: true,
children: [ children: [
{ {
path: '/b/c', path: '/b/c',
type: 'folder', type: 'directory',
discovered: true, discovered: true,
children: [ children: [
{ {
+8 -38
View File
@@ -61,20 +61,13 @@
}, },
"type": { "type": {
"type": "string", "type": "string",
"enum": [ "enum": ["file", "directory"]
"file",
"folder"
]
}, },
"exists": { "exists": {
"type": "boolean" "type": "boolean"
} }
}, },
"required": [ "required": ["path", "type", "exists"],
"path",
"type",
"exists"
],
"additionalProperties": false "additionalProperties": false
} }
}, },
@@ -95,10 +88,7 @@
"properties": { "properties": {
"type": { "type": {
"type": "string", "type": "string",
"enum": [ "enum": ["narrative", "dialogue"],
"narrative",
"dialogue"
],
"default": "narrative" "default": "narrative"
}, },
"content": { "content": {
@@ -108,9 +98,7 @@
"type": "string" "type": "string"
} }
}, },
"required": [ "required": ["content"],
"content"
],
"additionalProperties": false "additionalProperties": false
}, },
"exceptions": { "exceptions": {
@@ -132,20 +120,12 @@
"default": false "default": false
} }
}, },
"required": [ "required": ["id", "condition", "response"],
"id",
"condition",
"response"
],
"additionalProperties": false "additionalProperties": false
} }
} }
}, },
"required": [ "required": ["id", "condition", "response"],
"id",
"condition",
"response"
],
"additionalProperties": false "additionalProperties": false
} }
}, },
@@ -168,22 +148,12 @@
"default": [] "default": []
} }
}, },
"required": [ "required": ["id", "condition", "response"],
"id",
"condition",
"response"
],
"additionalProperties": false "additionalProperties": false
} }
} }
}, },
"required": [ "required": ["id", "title", "image", "instruction", "stages"],
"id",
"title",
"image",
"instruction",
"stages"
],
"additionalProperties": false, "additionalProperties": false,
"$schema": "http://json-schema.org/draft-07/schema#" "$schema": "http://json-schema.org/draft-07/schema#"
} }
+1 -1
View File
@@ -2,7 +2,7 @@ import { z } from 'zod'
export const fileInputSchema = z.object({ export const fileInputSchema = z.object({
path: z.string(), path: z.string(),
type: z.enum(['file', 'folder']), type: z.enum(['file', 'directory']),
}) })
export type IFileInput = z.infer<typeof fileInputSchema> export type IFileInput = z.infer<typeof fileInputSchema>
+3 -3
View File
@@ -15,7 +15,7 @@ export class MockFileChecker implements IFileExistenceChecker {
}, },
{ {
path: '/home/user/Downloads', path: '/home/user/Downloads',
type: 'folder', type: 'directory',
}, },
] ]
@@ -57,7 +57,7 @@ test('file matcher', async () => {
await checkFiles(checker, [ await checkFiles(checker, [
{ {
path: '/home/user/Downloads', path: '/home/user/Downloads',
type: 'folder', type: 'directory',
exists: true, exists: true,
}, },
]) ])
@@ -71,7 +71,7 @@ test('file matcher', async () => {
}, },
{ {
path: '/home/user/Downloads', path: '/home/user/Downloads',
type: 'folder', type: 'directory',
exists: true, exists: true,
}, },
]) ])
+1 -1
View File
@@ -66,7 +66,7 @@ export const Session = model<ISession>(
type: nodeSchema, type: nodeSchema,
default: { default: {
path: '/', path: '/',
type: 'folder', type: 'directory',
}, },
}, },
}, },
+1 -1
View File
@@ -5,7 +5,7 @@ export interface File {
const fileTypes = new Map([ const fileTypes = new Map([
['file', '-f'], ['file', '-f'],
['folder', '-d'], ['directory', '-d'],
['link', '-L'], ['link', '-L'],
['socket', '-S'], ['socket', '-S'],
]) ])
+1 -1
View File
@@ -12,7 +12,7 @@ describe('buildFileCheckCmd function', () => {
output: ['test', '-f', '/etc/passwd'], output: ['test', '-f', '/etc/passwd'],
}, },
{ {
input: { path: '/etc', type: 'folder' }, input: { path: '/etc', type: 'directory' },
output: ['test', '-d', '/etc'], output: ['test', '-d', '/etc'],
}, },
{ {
+2 -2
View File
@@ -5,8 +5,8 @@ instruction: |
在這個關卡中,你將會學到: 在這個關卡中,你將會學到:
1. 如何使用 `echo` 在終端機上顯示文字。 1. 如何使用 `echo` 在終端機上顯示文字。
# requirements: requirements:
# - get-started - get-started
stages: stages:
- id: start - id: start
+4
View File
@@ -0,0 +1,4 @@
echo start
echo hello
echo next
echo finish
+71
View File
@@ -0,0 +1,71 @@
id: get-started
image: base
title: 終端機的基本知識
instruction: |
歡迎來到 Linux Odyssey!
在這個關卡中,你將會學到:
1. 如何使用這個網站
2. 如何使用終端機
stages:
- id: start
name: 按下 `開始` 按鈕開始
condition:
command: ^echo start$
response:
content: |
在畫面的中下方的區塊是終端機,你可以在終端機裡輸入指令來操作電腦。
在終端機中,你所看到的 `commander:~ $` 叫做命令提示字串。
命令提示字串最後的錢字號 `$` 叫命令提示符,你必須將指令輸入在它的後方。
當你輸入完指令並按下 Enter 之後,終端機會執行你輸入的指令,並且會從終端機的下一行開始輸出指令的執行結果。
直到你看到新的 `commander:~ $` 出現時,代表你可以輸入下一個指令了。
現在請你輸入 `echo hello` 來看看會發生什麼事。
hint: |
在 `commander:~ $` 後面輸入 `echo hello` 並按下 Enter
- id: echo-hello
name: 在終端機輸入 `echo hello`
requirements:
- start
condition:
command: ^echo hello$
response:
content: |
你應該可以看到終端機上顯示了以下內容:
```
commander:~ $ echo hello
hello
```
第一行的 `commander:~ $` 是命令提示字串,接在後方的 `echo hello` 是你輸入的指令。
第二行的 `hello` 則是終端機的輸出結果。
你現在知道了如何在終端機上輸入指令,以及查看終端機輸出的位置。
現在請輸入 `echo next` 來繼續下一個步驟。
hint: |
輸入 `echo next` 繼續下一個步驟
- id: echo-next
name: 在終端機輸入 `echo next`
requirements:
- echo-hello
condition:
command: ^echo next$
response:
content: |
`Ctrl + C` 在終端機中是一個很重要的快捷鍵,它可以用來中斷正在執行的指令,並產生一個新的命令提示字串,讓你可以繼續輸入指令。
如果指令執行很久沒有反應,或你因為其他原因想要中斷指令,你可以按下 `Ctrl + C` 來中斷指令。
又或者你輸入了錯誤的指令,卻懶得慢慢刪除,你也可以按下 `Ctrl + C` 來產生新的命令提示字串重新輸入指令。
如果你已經理解了剛才的說明,請輸入 `echo finish` 來結束這一關。
hint: |
按下 `Ctrl + C` 能夠中斷正在執行的指令
按下 `Ctrl + C` 能夠產生新的命令提示字串
輸入 `echo finish` 來結束這一關
- id: END
name: 結束關卡
requirements:
- echo-next
condition:
command: ^echo finish$
response:
content: |
恭喜你,你已經完成了這個關卡!
+1 -1
View File
@@ -40,7 +40,7 @@ export async function createContainer(
) )
} }
if (!config.isProduction && config.docker.mountQuest && imageId !== 'base') { if (!config.isProduction && config.docker.mountQuest && imageId !== 'base') {
logger.info('Mounting quest folder', questId) logger.info('Mounting quest directory', questId)
binds = [ binds = [
`${config.docker.hostProjectRoot}/quests/${questId}/home:/home/commander`, `${config.docker.hostProjectRoot}/quests/${questId}/home:/home/commander`,
`${config.docker.hostProjectRoot}/packages/container:/usr/local/lib/container`, `${config.docker.hostProjectRoot}/packages/container:/usr/local/lib/container`,