Skip to content

Commit 40fc337

Browse files
fix(site): fix react state violation in filetree create/update utils (coder#20483)
1 parent f6df4c0 commit 40fc337

File tree

2 files changed

+27
-15
lines changed

2 files changed

+27
-15
lines changed

site/src/utils/filetree.test.ts

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,20 @@ import {
1010
} from "./filetree";
1111

1212
test("createFile() set file into the file tree", () => {
13-
let fileTree: FileTree = {
13+
const fileTree: FileTree = {
1414
"main.tf": "terraform",
1515
images: { "java.Dockerfile": "java dockerfile" },
1616
};
17-
fileTree = createFile(
17+
const updatedFileTree = createFile(
1818
"images/python.Dockerfile",
1919
fileTree,
2020
"python dockerfile",
2121
);
22-
expect((fileTree.images as FileTree)["python.Dockerfile"]).toEqual(
22+
expect((updatedFileTree.images as FileTree)["python.Dockerfile"]).toEqual(
2323
"python dockerfile",
2424
);
25+
// Verify the original FileTree was not modified.
26+
expect((fileTree.images as FileTree)["python.Dockerfile"]).toBeUndefined();
2527
});
2628

2729
test("getFileContent() return the file content from the file tree", () => {
@@ -35,50 +37,56 @@ test("getFileContent() return the file content from the file tree", () => {
3537
});
3638

3739
test("removeFile() removes a file from a folder", () => {
38-
let fileTree: FileTree = {
40+
const fileTree: FileTree = {
3941
"main.tf": "terraform content",
4042
images: {
4143
"java.Dockerfile": "java dockerfile",
4244
"python.Dockerfile": "python Dockerfile",
4345
},
4446
};
45-
fileTree = removeFile("images/python.Dockerfile", fileTree);
47+
const updatedFileTree = removeFile("images/python.Dockerfile", fileTree);
4648
const expectedFileTree = {
4749
"main.tf": "terraform content",
4850
images: {
4951
"java.Dockerfile": "java dockerfile",
5052
},
5153
};
52-
expect(expectedFileTree).toEqual(fileTree);
54+
expect(updatedFileTree).toEqual(expectedFileTree);
55+
// Verify the original FileTree was not modified.
56+
expect((fileTree.images as FileTree)["python.Dockerfile"]).toEqual(
57+
"python Dockerfile",
58+
);
5359
});
5460

5561
test("removeFile() removes a file from root", () => {
56-
let fileTree: FileTree = {
62+
const fileTree: FileTree = {
5763
"main.tf": "terraform content",
5864
images: {
5965
"java.Dockerfile": "java dockerfile",
6066
"python.Dockerfile": "python Dockerfile",
6167
},
6268
};
63-
fileTree = removeFile("main.tf", fileTree);
69+
const updatedFileTree = removeFile("main.tf", fileTree);
6470
const expectedFileTree = {
6571
images: {
6672
"java.Dockerfile": "java dockerfile",
6773
"python.Dockerfile": "python Dockerfile",
6874
},
6975
};
70-
expect(expectedFileTree).toEqual(fileTree);
76+
expect(updatedFileTree).toEqual(expectedFileTree);
77+
// Verify the original FileTree was not modified.
78+
expect(fileTree["main.tf"]).toEqual("terraform content");
7179
});
7280

7381
test("moveFile() moves a file from in file tree", () => {
74-
let fileTree: FileTree = {
82+
const fileTree: FileTree = {
7583
"main.tf": "terraform content",
7684
images: {
7785
"java.Dockerfile": "java dockerfile",
7886
"python.Dockerfile": "python Dockerfile",
7987
},
8088
};
81-
fileTree = moveFile(
89+
const updatedFileTree = moveFile(
8290
"images/java.Dockerfile",
8391
"other/java.Dockerfile",
8492
fileTree,
@@ -92,7 +100,9 @@ test("moveFile() moves a file from in file tree", () => {
92100
"java.Dockerfile": "java dockerfile",
93101
},
94102
};
95-
expect(fileTree).toEqual(expectedFileTree);
103+
expect(updatedFileTree).toEqual(expectedFileTree);
104+
// Verify the original FileTree was not modified.
105+
expect(fileTree["main.tf"]).toEqual("terraform content");
96106
});
97107

98108
test("existsFile() returns if there is or not a file", () => {

site/src/utils/filetree.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ export const createFile = (
2020
throw new Error(pathError);
2121
}
2222

23-
return set(fileTree, path.split("/"), value);
23+
const updatedFileTree = structuredClone(fileTree);
24+
return set(updatedFileTree, path.split("/"), value);
2425
};
2526

2627
export const validatePath = (
@@ -43,15 +44,16 @@ export const updateFile = (
4344
content: FileTree | string,
4445
fileTree: FileTree,
4546
): FileTree => {
46-
return set(fileTree, path.split("/"), content);
47+
const updatedFileTree = structuredClone(fileTree);
48+
return set(updatedFileTree, path.split("/"), content);
4749
};
4850

4951
export const existsFile = (path: string, fileTree: FileTree) => {
5052
return has(fileTree, path.split("/"));
5153
};
5254

5355
export const removeFile = (path: string, fileTree: FileTree) => {
54-
const updatedFileTree = { ...fileTree };
56+
const updatedFileTree = structuredClone(fileTree);
5557
unset(updatedFileTree, path.split("/"));
5658
return updatedFileTree;
5759
};

0 commit comments

Comments
 (0)