@webiny/app-file-manager 6.4.7 → 6.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +17 -17
- package/presentation/FileDetails/FileDetailsPresenter.d.ts +4 -3
- package/presentation/FileDetails/FileDetailsPresenter.js +46 -35
- package/presentation/FileDetails/FileDetailsPresenter.js.map +1 -1
- package/presentation/FileDetails/FileDetailsPresenter.test.js +54 -3
- package/presentation/FileDetails/FileDetailsPresenter.test.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webiny/app-file-manager",
|
|
3
|
-
"version": "6.4.
|
|
3
|
+
"version": "6.4.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./index.js",
|
|
@@ -16,22 +16,22 @@
|
|
|
16
16
|
"@apollo/react-common": "3.1.4",
|
|
17
17
|
"@apollo/react-components": "3.1.5",
|
|
18
18
|
"@types/react": "18.3.31",
|
|
19
|
-
"@webiny/admin-ui": "6.4.
|
|
20
|
-
"@webiny/app": "6.4.
|
|
21
|
-
"@webiny/app-aco": "6.4.
|
|
22
|
-
"@webiny/app-admin": "6.4.
|
|
23
|
-
"@webiny/app-headless-cms": "6.4.
|
|
24
|
-
"@webiny/app-headless-cms-common": "6.4.
|
|
25
|
-
"@webiny/app-websockets": "6.4.
|
|
19
|
+
"@webiny/admin-ui": "6.4.8",
|
|
20
|
+
"@webiny/app": "6.4.8",
|
|
21
|
+
"@webiny/app-aco": "6.4.8",
|
|
22
|
+
"@webiny/app-admin": "6.4.8",
|
|
23
|
+
"@webiny/app-headless-cms": "6.4.8",
|
|
24
|
+
"@webiny/app-headless-cms-common": "6.4.8",
|
|
25
|
+
"@webiny/app-websockets": "6.4.8",
|
|
26
26
|
"@webiny/di": "1.0.2",
|
|
27
|
-
"@webiny/feature": "6.4.
|
|
28
|
-
"@webiny/form": "6.4.
|
|
29
|
-
"@webiny/icons": "6.4.
|
|
30
|
-
"@webiny/plugins": "6.4.
|
|
31
|
-
"@webiny/react-composition": "6.4.
|
|
32
|
-
"@webiny/react-properties": "6.4.
|
|
33
|
-
"@webiny/sdk": "6.4.
|
|
34
|
-
"@webiny/validation": "6.4.
|
|
27
|
+
"@webiny/feature": "6.4.8",
|
|
28
|
+
"@webiny/form": "6.4.8",
|
|
29
|
+
"@webiny/icons": "6.4.8",
|
|
30
|
+
"@webiny/plugins": "6.4.8",
|
|
31
|
+
"@webiny/react-composition": "6.4.8",
|
|
32
|
+
"@webiny/react-properties": "6.4.8",
|
|
33
|
+
"@webiny/sdk": "6.4.8",
|
|
34
|
+
"@webiny/validation": "6.4.8",
|
|
35
35
|
"apollo-cache": "1.3.5",
|
|
36
36
|
"apollo-client": "2.6.10",
|
|
37
37
|
"apollo-link": "1.2.14",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@svgr/webpack": "8.1.0",
|
|
56
|
-
"@webiny/build-tools": "6.4.
|
|
56
|
+
"@webiny/build-tools": "6.4.8",
|
|
57
57
|
"rimraf": "6.1.3",
|
|
58
58
|
"typescript": "6.0.3",
|
|
59
59
|
"vitest": "4.1.10"
|
|
@@ -1,25 +1,26 @@
|
|
|
1
|
+
import { WcpService } from "@webiny/app-admin/features/wcp/abstractions.js";
|
|
1
2
|
import { type IFileDetailsPresenter, type IFileDetailsViewModel } from "./abstractions.js";
|
|
2
3
|
import { GetFileUseCase } from "../../features/getFile/abstractions.js";
|
|
3
4
|
import { UpdateFileUseCase } from "../../features/updateFile/abstractions.js";
|
|
4
|
-
import { DeleteFileUseCase } from "../../features/deleteFile/abstractions.js";
|
|
5
5
|
import { FormModelFactory } from "@webiny/app-admin/features/formModel/abstractions.js";
|
|
6
6
|
import { FileManagerPermissions } from "../../features/permissions/abstractions.js";
|
|
7
7
|
import { GetSettingsRepository } from "../../features/settings/abstractions.js";
|
|
8
8
|
declare class FileDetailsPresenterImpl implements IFileDetailsPresenter {
|
|
9
9
|
private getFileUseCase;
|
|
10
10
|
private updateFileUseCase;
|
|
11
|
-
private deleteFileUseCase;
|
|
12
11
|
private formModelFactory;
|
|
13
12
|
private permissions;
|
|
14
13
|
private settingsRepository;
|
|
14
|
+
private wcp;
|
|
15
15
|
private file;
|
|
16
16
|
private loading;
|
|
17
17
|
private form;
|
|
18
|
-
constructor(getFileUseCase: GetFileUseCase.Interface, updateFileUseCase: UpdateFileUseCase.Interface,
|
|
18
|
+
constructor(getFileUseCase: GetFileUseCase.Interface, updateFileUseCase: UpdateFileUseCase.Interface, formModelFactory: FormModelFactory.Interface, permissions: FileManagerPermissions.Interface, settingsRepository: GetSettingsRepository.Interface, wcp: WcpService.Interface);
|
|
19
19
|
get vm(): IFileDetailsViewModel;
|
|
20
20
|
loadFile(id: string): Promise<void>;
|
|
21
21
|
saveFile(): Promise<boolean>;
|
|
22
22
|
private buildForm;
|
|
23
|
+
private canUsePrivateFiles;
|
|
23
24
|
private buildPreviewUrl;
|
|
24
25
|
}
|
|
25
26
|
export declare const FileDetailsPresenter: typeof FileDetailsPresenterImpl & {
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { computed, makeAutoObservable, runInAction } from "mobx";
|
|
2
|
+
import { WcpService } from "@webiny/app-admin/features/wcp/abstractions.js";
|
|
2
3
|
import { FileDetailsPresenter } from "./abstractions.js";
|
|
3
4
|
import { GetFileUseCase } from "../../features/getFile/abstractions.js";
|
|
4
5
|
import { UpdateFileUseCase } from "../../features/updateFile/abstractions.js";
|
|
5
|
-
import { DeleteFileUseCase } from "../../features/deleteFile/abstractions.js";
|
|
6
6
|
import { FormModelFactory } from "@webiny/app-admin/features/formModel/abstractions.js";
|
|
7
7
|
import { FileManagerPermissions } from "../../features/permissions/abstractions.js";
|
|
8
8
|
import { GetSettingsRepository } from "../../features/settings/abstractions.js";
|
|
9
9
|
class FileDetailsPresenterImpl {
|
|
10
|
-
constructor(getFileUseCase, updateFileUseCase,
|
|
10
|
+
constructor(getFileUseCase, updateFileUseCase, formModelFactory, permissions, settingsRepository, wcp){
|
|
11
11
|
this.getFileUseCase = getFileUseCase;
|
|
12
12
|
this.updateFileUseCase = updateFileUseCase;
|
|
13
|
-
this.deleteFileUseCase = deleteFileUseCase;
|
|
14
13
|
this.formModelFactory = formModelFactory;
|
|
15
14
|
this.permissions = permissions;
|
|
16
15
|
this.settingsRepository = settingsRepository;
|
|
16
|
+
this.wcp = wcp;
|
|
17
17
|
this.file = null;
|
|
18
18
|
this.loading = null;
|
|
19
19
|
this.form = this.buildForm();
|
|
@@ -45,17 +45,18 @@ class FileDetailsPresenterImpl {
|
|
|
45
45
|
if (result.success) {
|
|
46
46
|
this.file = result.file;
|
|
47
47
|
this.form = this.buildForm();
|
|
48
|
-
|
|
48
|
+
const formData = {
|
|
49
49
|
name: result.file.name ?? "",
|
|
50
50
|
description: result.file.description ?? "",
|
|
51
|
-
tags: result.file.tags ?? []
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
tags: result.file.tags ?? []
|
|
52
|
+
};
|
|
53
|
+
if (this.canUsePrivateFiles()) formData.accessControl = result.file.accessControl?.type ?? "public";
|
|
54
|
+
this.form.setData(formData);
|
|
54
55
|
if (!this.permissions.canEdit("file", result.file)) {
|
|
55
56
|
this.form.field("name").setDisabled(true);
|
|
56
57
|
this.form.field("description").setDisabled(true);
|
|
57
58
|
this.form.field("tags").setDisabled(true);
|
|
58
|
-
this.form.field("accessControl").setDisabled(true);
|
|
59
|
+
if (this.canUsePrivateFiles()) this.form.field("accessControl").setDisabled(true);
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
});
|
|
@@ -73,16 +74,17 @@ class FileDetailsPresenterImpl {
|
|
|
73
74
|
this.loading = "Saving changes...";
|
|
74
75
|
});
|
|
75
76
|
try {
|
|
77
|
+
const updateData = {
|
|
78
|
+
name: data.name,
|
|
79
|
+
description: data.description,
|
|
80
|
+
tags: data.tags
|
|
81
|
+
};
|
|
82
|
+
if (this.canUsePrivateFiles()) updateData.accessControl = {
|
|
83
|
+
type: data.accessControl
|
|
84
|
+
};
|
|
76
85
|
await this.updateFileUseCase.execute({
|
|
77
86
|
id: this.file.id,
|
|
78
|
-
data:
|
|
79
|
-
name: data.name,
|
|
80
|
-
description: data.description,
|
|
81
|
-
tags: data.tags,
|
|
82
|
-
accessControl: {
|
|
83
|
-
type: data.accessControl
|
|
84
|
-
}
|
|
85
|
-
}
|
|
87
|
+
data: updateData
|
|
86
88
|
});
|
|
87
89
|
return true;
|
|
88
90
|
} finally{
|
|
@@ -93,29 +95,38 @@ class FileDetailsPresenterImpl {
|
|
|
93
95
|
}
|
|
94
96
|
buildForm() {
|
|
95
97
|
return this.formModelFactory.create({
|
|
96
|
-
fields: (fields)=>
|
|
98
|
+
fields: (fields)=>{
|
|
99
|
+
const fieldDefs = {
|
|
97
100
|
name: fields.text().label("Name").required().placeholder("Enter name"),
|
|
98
101
|
description: fields.text().label("Description").renderer("textarea").placeholder("Enter description"),
|
|
99
|
-
tags: fields.text().label("Tags").list().renderer("tags").placeholder("Add tags")
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
102
|
+
tags: fields.text().label("Tags").list().renderer("tags").placeholder("Add tags")
|
|
103
|
+
};
|
|
104
|
+
if (this.canUsePrivateFiles()) fieldDefs.accessControl = fields.text().label("Access Control").help("Control who can access this file.").options([
|
|
105
|
+
{
|
|
106
|
+
label: "Public",
|
|
107
|
+
value: "public"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
label: "Private (Authenticated)",
|
|
111
|
+
value: "private-authenticated"
|
|
112
|
+
}
|
|
113
|
+
]).defaultValue("public");
|
|
114
|
+
return fieldDefs;
|
|
115
|
+
},
|
|
116
|
+
layout: (layout)=>{
|
|
117
|
+
const rows = [
|
|
112
118
|
layout.row("name"),
|
|
113
119
|
layout.row("description"),
|
|
114
|
-
layout.row("tags")
|
|
115
|
-
|
|
116
|
-
|
|
120
|
+
layout.row("tags")
|
|
121
|
+
];
|
|
122
|
+
if (this.canUsePrivateFiles()) rows.push(layout.row("accessControl"));
|
|
123
|
+
return rows;
|
|
124
|
+
}
|
|
117
125
|
});
|
|
118
126
|
}
|
|
127
|
+
canUsePrivateFiles() {
|
|
128
|
+
return this.wcp.getProject().canUsePrivateFiles();
|
|
129
|
+
}
|
|
119
130
|
buildPreviewUrl() {
|
|
120
131
|
if (!this.file?.key) return null;
|
|
121
132
|
const srcPrefix = this.settingsRepository.settings?.srcPrefix ?? "";
|
|
@@ -127,10 +138,10 @@ const FileDetailsPresenter_FileDetailsPresenter = FileDetailsPresenter.createImp
|
|
|
127
138
|
dependencies: [
|
|
128
139
|
GetFileUseCase,
|
|
129
140
|
UpdateFileUseCase,
|
|
130
|
-
DeleteFileUseCase,
|
|
131
141
|
FormModelFactory,
|
|
132
142
|
FileManagerPermissions,
|
|
133
|
-
GetSettingsRepository
|
|
143
|
+
GetSettingsRepository,
|
|
144
|
+
WcpService
|
|
134
145
|
]
|
|
135
146
|
});
|
|
136
147
|
export { FileDetailsPresenter_FileDetailsPresenter as FileDetailsPresenter };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentation/FileDetails/FileDetailsPresenter.js","sources":["../../../src/presentation/FileDetails/FileDetailsPresenter.ts"],"sourcesContent":["import { makeAutoObservable, runInAction, computed } from \"mobx\";\nimport {\n FileDetailsPresenter as Abstraction,\n type IFileDetailsPresenter,\n type IFileDetailsViewModel\n} from \"./abstractions.js\";\nimport { GetFileUseCase } from \"../../features/getFile/abstractions.js\";\nimport { UpdateFileUseCase } from \"../../features/updateFile/abstractions.js\";\nimport { DeleteFileUseCase } from \"../../features/deleteFile/abstractions.js\";\nimport { FormModelFactory } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport type { IFormModel } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport { FileManagerPermissions } from \"../../features/permissions/abstractions.js\";\nimport { GetSettingsRepository } from \"../../features/settings/abstractions.js\";\nimport type { FmFile } from \"../../features/shared/types.js\";\n\nclass FileDetailsPresenterImpl implements IFileDetailsPresenter {\n private file: FmFile | null = null;\n private loading: string | null = null;\n private form: IFormModel;\n\n constructor(\n private getFileUseCase: GetFileUseCase.Interface,\n private updateFileUseCase: UpdateFileUseCase.Interface,\n private deleteFileUseCase: DeleteFileUseCase.Interface,\n private formModelFactory: FormModelFactory.Interface,\n private permissions: FileManagerPermissions.Interface,\n private settingsRepository: GetSettingsRepository.Interface\n ) {\n // Build an empty form initially.\n this.form = this.buildForm();\n\n makeAutoObservable(this, { vm: computed });\n }\n\n get vm(): IFileDetailsViewModel {\n return {\n file: this.file,\n loading: this.loading,\n form: this.form.vm,\n previewUrl: this.buildPreviewUrl(),\n permissions: {\n canEdit: this.permissions.canEdit(\"file\", this.file ?? undefined),\n canDelete: this.permissions.canDelete(\"file\", this.file ?? undefined)\n }\n };\n }\n\n async loadFile(id: string): Promise<void> {\n runInAction(() => {\n this.loading = \"Loading file...\";\n });\n\n try {\n const result = await this.getFileUseCase.execute({ id });\n runInAction(() => {\n if (result.success) {\n this.file = result.file;\n this.form = this.buildForm();\n this.form.setData({\n name: result.file.name ?? \"\",\n description: result.file.description ?? \"\",\n tags: result.file.tags ?? [],\n accessControl: result.file.accessControl?.type ?? \"public\"\n });\n\n if (!this.permissions.canEdit(\"file\", result.file)) {\n this.form.field(\"name\").setDisabled(true);\n this.form.field(\"description\").setDisabled(true);\n this.form.field(\"tags\").setDisabled(true);\n this.form.field(\"accessControl\").setDisabled(true);\n }\n }\n });\n } finally {\n runInAction(() => {\n this.loading = null;\n });\n }\n }\n\n async saveFile(): Promise<boolean> {\n if (!this.file) {\n return false;\n }\n\n const data = await this.form.submit();\n if (!data) {\n return false;\n }\n\n runInAction(() => {\n this.loading = \"Saving changes...\";\n });\n\n try {\n await this.updateFileUseCase.execute({\n id: this.file.id,\n data: {\n name: data.name as string,\n description: data.description as string,\n tags: data.tags as string[],\n accessControl: {\n type: data.accessControl as \"public\" | \"private-authenticated\"\n }\n }\n });\n return true;\n } finally {\n runInAction(() => {\n this.loading = null;\n });\n }\n }\n\n private buildForm(): IFormModel {\n return this.formModelFactory.create({\n fields: fields => ({\n name: fields.text().label(\"Name\").required().placeholder(\"Enter name\"),\n description: fields\n .text()\n .label(\"Description\")\n .renderer(\"textarea\")\n .placeholder(\"Enter description\"),\n tags: fields.text().label(\"Tags\").list().renderer(\"tags\").placeholder(\"Add tags\"),\n accessControl: fields\n .text()\n .label(\"Access Control\")\n .help(\"Control who can access this file.\")\n .options([\n { label: \"Public\", value: \"public\" },\n { label: \"Private (Authenticated)\", value: \"private-authenticated\" }\n ])\n .defaultValue(\"public\")\n }),\n layout: layout => [\n layout.row(\"name\"),\n layout.row(\"description\"),\n layout.row(\"tags\"),\n layout.row(\"accessControl\")\n ]\n });\n }\n\n private buildPreviewUrl(): string | null {\n if (!this.file?.key) {\n return null;\n }\n const srcPrefix = this.settingsRepository.settings?.srcPrefix ?? \"\";\n return `${srcPrefix}${this.file.key}`;\n }\n}\n\nexport const FileDetailsPresenter = Abstraction.createImplementation({\n implementation: FileDetailsPresenterImpl,\n dependencies: [\n GetFileUseCase,\n UpdateFileUseCase,\n DeleteFileUseCase,\n FormModelFactory,\n FileManagerPermissions,\n GetSettingsRepository\n ]\n});\n"],"names":["FileDetailsPresenterImpl","getFileUseCase","updateFileUseCase","deleteFileUseCase","formModelFactory","permissions","settingsRepository","makeAutoObservable","computed","undefined","id","runInAction","result","data","fields","layout","srcPrefix","FileDetailsPresenter","Abstraction","GetFileUseCase","UpdateFileUseCase","DeleteFileUseCase","FormModelFactory","FileManagerPermissions","GetSettingsRepository"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"presentation/FileDetails/FileDetailsPresenter.js","sources":["../../../src/presentation/FileDetails/FileDetailsPresenter.ts"],"sourcesContent":["import { makeAutoObservable, runInAction, computed } from \"mobx\";\nimport { WcpService } from \"@webiny/app-admin/features/wcp/abstractions.js\";\nimport {\n FileDetailsPresenter as Abstraction,\n type IFileDetailsPresenter,\n type IFileDetailsViewModel\n} from \"./abstractions.js\";\nimport { GetFileUseCase } from \"../../features/getFile/abstractions.js\";\nimport { UpdateFileUseCase } from \"../../features/updateFile/abstractions.js\";\nimport { FormModelFactory } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport type { IFormModel } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport { FileManagerPermissions } from \"../../features/permissions/abstractions.js\";\nimport { GetSettingsRepository } from \"../../features/settings/abstractions.js\";\nimport type { FmFile } from \"../../features/shared/types.js\";\nimport type { UpdateFileData } from \"../../features/updateFile/abstractions.js\";\n\nclass FileDetailsPresenterImpl implements IFileDetailsPresenter {\n private file: FmFile | null = null;\n private loading: string | null = null;\n private form: IFormModel;\n\n constructor(\n private getFileUseCase: GetFileUseCase.Interface,\n private updateFileUseCase: UpdateFileUseCase.Interface,\n private formModelFactory: FormModelFactory.Interface,\n private permissions: FileManagerPermissions.Interface,\n private settingsRepository: GetSettingsRepository.Interface,\n private wcp: WcpService.Interface\n ) {\n // Build an empty form initially.\n this.form = this.buildForm();\n\n makeAutoObservable(this, { vm: computed });\n }\n\n get vm(): IFileDetailsViewModel {\n return {\n file: this.file,\n loading: this.loading,\n form: this.form.vm,\n previewUrl: this.buildPreviewUrl(),\n permissions: {\n canEdit: this.permissions.canEdit(\"file\", this.file ?? undefined),\n canDelete: this.permissions.canDelete(\"file\", this.file ?? undefined)\n }\n };\n }\n\n async loadFile(id: string): Promise<void> {\n runInAction(() => {\n this.loading = \"Loading file...\";\n });\n\n try {\n const result = await this.getFileUseCase.execute({ id });\n runInAction(() => {\n if (result.success) {\n this.file = result.file;\n this.form = this.buildForm();\n\n const formData: Record<string, unknown> = {\n name: result.file.name ?? \"\",\n description: result.file.description ?? \"\",\n tags: result.file.tags ?? []\n };\n\n if (this.canUsePrivateFiles()) {\n formData.accessControl = result.file.accessControl?.type ?? \"public\";\n }\n\n this.form.setData(formData);\n\n if (!this.permissions.canEdit(\"file\", result.file)) {\n this.form.field(\"name\").setDisabled(true);\n this.form.field(\"description\").setDisabled(true);\n this.form.field(\"tags\").setDisabled(true);\n if (this.canUsePrivateFiles()) {\n this.form.field(\"accessControl\").setDisabled(true);\n }\n }\n }\n });\n } finally {\n runInAction(() => {\n this.loading = null;\n });\n }\n }\n\n async saveFile(): Promise<boolean> {\n if (!this.file) {\n return false;\n }\n\n const data = await this.form.submit();\n if (!data) {\n return false;\n }\n\n runInAction(() => {\n this.loading = \"Saving changes...\";\n });\n\n try {\n const updateData: UpdateFileData = {\n name: data.name as string,\n description: data.description as string,\n tags: data.tags as string[]\n };\n\n if (this.canUsePrivateFiles()) {\n updateData.accessControl = {\n type: data.accessControl as \"public\" | \"private-authenticated\"\n };\n }\n\n await this.updateFileUseCase.execute({\n id: this.file.id,\n data: updateData\n });\n return true;\n } finally {\n runInAction(() => {\n this.loading = null;\n });\n }\n }\n\n private buildForm(): IFormModel {\n return this.formModelFactory.create({\n fields: fields => {\n const fieldDefs: Record<string, any> = {\n name: fields.text().label(\"Name\").required().placeholder(\"Enter name\"),\n description: fields\n .text()\n .label(\"Description\")\n .renderer(\"textarea\")\n .placeholder(\"Enter description\"),\n tags: fields\n .text()\n .label(\"Tags\")\n .list()\n .renderer(\"tags\")\n .placeholder(\"Add tags\")\n };\n\n if (this.canUsePrivateFiles()) {\n fieldDefs.accessControl = fields\n .text()\n .label(\"Access Control\")\n .help(\"Control who can access this file.\")\n .options([\n { label: \"Public\", value: \"public\" },\n {\n label: \"Private (Authenticated)\",\n value: \"private-authenticated\"\n }\n ])\n .defaultValue(\"public\");\n }\n\n return fieldDefs;\n },\n layout: layout => {\n const rows = [layout.row(\"name\"), layout.row(\"description\"), layout.row(\"tags\")];\n\n if (this.canUsePrivateFiles()) {\n rows.push(layout.row(\"accessControl\"));\n }\n\n return rows;\n }\n });\n }\n\n private canUsePrivateFiles(): boolean {\n return this.wcp.getProject().canUsePrivateFiles();\n }\n\n private buildPreviewUrl(): string | null {\n if (!this.file?.key) {\n return null;\n }\n const srcPrefix = this.settingsRepository.settings?.srcPrefix ?? \"\";\n return `${srcPrefix}${this.file.key}`;\n }\n}\n\nexport const FileDetailsPresenter = Abstraction.createImplementation({\n implementation: FileDetailsPresenterImpl,\n dependencies: [\n GetFileUseCase,\n UpdateFileUseCase,\n FormModelFactory,\n FileManagerPermissions,\n GetSettingsRepository,\n WcpService\n ]\n});\n"],"names":["FileDetailsPresenterImpl","getFileUseCase","updateFileUseCase","formModelFactory","permissions","settingsRepository","wcp","makeAutoObservable","computed","undefined","id","runInAction","result","formData","data","updateData","fields","fieldDefs","layout","rows","srcPrefix","FileDetailsPresenter","Abstraction","GetFileUseCase","UpdateFileUseCase","FormModelFactory","FileManagerPermissions","GetSettingsRepository","WcpService"],"mappings":"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"}
|
|
@@ -8,6 +8,7 @@ import { FileManagerPermissions } from "../../features/permissions/abstractions.
|
|
|
8
8
|
import { GetSettingsRepository } from "../../features/settings/abstractions.js";
|
|
9
9
|
import { FileDetailsPresenter } from "./abstractions.js";
|
|
10
10
|
import { FileDetailsPresenter as external_FileDetailsPresenter_js_FileDetailsPresenter } from "./FileDetailsPresenter.js";
|
|
11
|
+
import { WcpService } from "@webiny/app-admin/features/wcp/abstractions.js";
|
|
11
12
|
function createMockFormModel(overrides) {
|
|
12
13
|
const vm = {
|
|
13
14
|
layout: [],
|
|
@@ -22,7 +23,9 @@ function createMockFormModel(overrides) {
|
|
|
22
23
|
};
|
|
23
24
|
const data = {};
|
|
24
25
|
return {
|
|
25
|
-
field: vi.fn()
|
|
26
|
+
field: vi.fn().mockReturnValue({
|
|
27
|
+
setDisabled: vi.fn()
|
|
28
|
+
}),
|
|
26
29
|
fields: vi.fn(),
|
|
27
30
|
layout: vi.fn(),
|
|
28
31
|
getData: vi.fn(()=>({
|
|
@@ -104,6 +107,16 @@ function createMockSettingsRepository() {
|
|
|
104
107
|
}
|
|
105
108
|
};
|
|
106
109
|
}
|
|
110
|
+
function createMockWcpService(canUsePrivateFiles = true) {
|
|
111
|
+
return {
|
|
112
|
+
getProject: vi.fn().mockReturnValue({
|
|
113
|
+
canUsePrivateFiles: vi.fn().mockReturnValue(canUsePrivateFiles)
|
|
114
|
+
}),
|
|
115
|
+
isLoaded: vi.fn().mockReturnValue(true),
|
|
116
|
+
canUseFeature: vi.fn().mockReturnValue(false),
|
|
117
|
+
loadProject: vi.fn().mockResolvedValue(void 0)
|
|
118
|
+
};
|
|
119
|
+
}
|
|
107
120
|
function createTestFile(overrides) {
|
|
108
121
|
return {
|
|
109
122
|
id: "file-1",
|
|
@@ -138,14 +151,15 @@ function createTestFile(overrides) {
|
|
|
138
151
|
...overrides
|
|
139
152
|
};
|
|
140
153
|
}
|
|
141
|
-
function createMocks() {
|
|
154
|
+
function createMocks(canUsePrivateFiles = true) {
|
|
142
155
|
return {
|
|
143
156
|
getFileUseCase: createMockGetFileUseCase(),
|
|
144
157
|
updateFileUseCase: createMockUpdateFileUseCase(),
|
|
145
158
|
deleteFileUseCase: createMockDeleteFileUseCase(),
|
|
146
159
|
formModelFactory: createMockFormModelFactory(),
|
|
147
160
|
permissions: createMockPermissions(),
|
|
148
|
-
settingsRepository: createMockSettingsRepository()
|
|
161
|
+
settingsRepository: createMockSettingsRepository(),
|
|
162
|
+
wcpService: createMockWcpService(canUsePrivateFiles)
|
|
149
163
|
};
|
|
150
164
|
}
|
|
151
165
|
function createContainer(mocks) {
|
|
@@ -156,6 +170,7 @@ function createContainer(mocks) {
|
|
|
156
170
|
container.registerInstance(FormModelFactory, mocks.formModelFactory);
|
|
157
171
|
container.registerInstance(FileManagerPermissions, mocks.permissions);
|
|
158
172
|
container.registerInstance(GetSettingsRepository, mocks.settingsRepository);
|
|
173
|
+
container.registerInstance(WcpService, mocks.wcpService);
|
|
159
174
|
container.register(external_FileDetailsPresenter_js_FileDetailsPresenter).inSingletonScope();
|
|
160
175
|
return container;
|
|
161
176
|
}
|
|
@@ -275,6 +290,42 @@ describe("FileDetailsPresenter", ()=>{
|
|
|
275
290
|
expect(loadingDuringExecution).toBe("Loading file...");
|
|
276
291
|
expect(presenter.vm.loading).toBeNull();
|
|
277
292
|
});
|
|
293
|
+
describe("when Private Files is NOT licensed", ()=>{
|
|
294
|
+
let unlicensedMocks;
|
|
295
|
+
let unlicensedPresenter;
|
|
296
|
+
beforeEach(()=>{
|
|
297
|
+
unlicensedMocks = createMocks(false);
|
|
298
|
+
const container = createContainer(unlicensedMocks);
|
|
299
|
+
unlicensedPresenter = container.resolve(FileDetailsPresenter);
|
|
300
|
+
});
|
|
301
|
+
it("should NOT include accessControl in the save payload", async ()=>{
|
|
302
|
+
await unlicensedPresenter.loadFile("file-1");
|
|
303
|
+
const mockForm = unlicensedMocks.formModelFactory.create.mock.results[1].value;
|
|
304
|
+
mockForm.submit.mockResolvedValue({
|
|
305
|
+
name: "updated.jpg",
|
|
306
|
+
description: "",
|
|
307
|
+
tags: [
|
|
308
|
+
"updated"
|
|
309
|
+
]
|
|
310
|
+
});
|
|
311
|
+
await unlicensedPresenter.saveFile();
|
|
312
|
+
const callArgs = unlicensedMocks.updateFileUseCase.execute.mock.calls[0][0];
|
|
313
|
+
expect(callArgs.data).not.toHaveProperty("accessControl");
|
|
314
|
+
});
|
|
315
|
+
it("should NOT set accessControl in form data during loadFile", async ()=>{
|
|
316
|
+
await unlicensedPresenter.loadFile("file-1");
|
|
317
|
+
const mockForm = unlicensedMocks.formModelFactory.create.mock.results[1].value;
|
|
318
|
+
const setDataCall = mockForm.setData.mock.calls[0][0];
|
|
319
|
+
expect(setDataCall).not.toHaveProperty("accessControl");
|
|
320
|
+
});
|
|
321
|
+
it("should NOT disable accessControl field when user lacks edit permission", async ()=>{
|
|
322
|
+
unlicensedMocks.permissions.canEdit.mockReturnValue(false);
|
|
323
|
+
await unlicensedPresenter.loadFile("file-1");
|
|
324
|
+
const mockForm = unlicensedMocks.formModelFactory.create.mock.results[1].value;
|
|
325
|
+
expect(mockForm.field).toHaveBeenCalledTimes(3);
|
|
326
|
+
expect(mockForm.field).not.toHaveBeenCalledWith("accessControl");
|
|
327
|
+
});
|
|
328
|
+
});
|
|
278
329
|
});
|
|
279
330
|
|
|
280
331
|
//# sourceMappingURL=FileDetailsPresenter.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentation/FileDetails/FileDetailsPresenter.test.js","sources":["../../../src/presentation/FileDetails/FileDetailsPresenter.test.ts"],"sourcesContent":["// @vitest-environment jsdom\nimport { describe, it, expect, vi, beforeEach } from \"vitest\";\nimport { Container } from \"@webiny/di\";\nimport { GetFileUseCase } from \"../../features/getFile/abstractions.js\";\nimport { UpdateFileUseCase } from \"../../features/updateFile/abstractions.js\";\nimport { DeleteFileUseCase } from \"../../features/deleteFile/abstractions.js\";\nimport { FormModelFactory } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport type { IFormModel } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport type { IFormVM } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport { FileManagerPermissions } from \"../../features/permissions/abstractions.js\";\nimport { GetSettingsRepository } from \"../../features/settings/abstractions.js\";\nimport { FileDetailsPresenter as Abstraction, type IFileDetailsPresenter } from \"./abstractions.js\";\nimport { FileDetailsPresenter } from \"./FileDetailsPresenter.js\";\nimport type { FmFile } from \"../../features/shared/types.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction createMockFormModel(overrides?: Partial<IFormVM>): IFormModel {\n const vm: IFormVM = {\n layout: [],\n errors: [],\n isDirty: false,\n isValid: true,\n submitCount: 0,\n focusField: vi.fn(),\n getData: vi.fn(() => ({})),\n setData: vi.fn(),\n ...overrides\n };\n\n const data: Record<string, unknown> = {};\n\n return {\n field: vi.fn() as any,\n fields: vi.fn() as any,\n layout: vi.fn() as any,\n getData: vi.fn(() => ({ ...data })),\n setData: vi.fn((incoming: Record<string, unknown>) => {\n Object.assign(data, incoming);\n }),\n reset: vi.fn(),\n validate: vi.fn().mockResolvedValue(true),\n submit: vi.fn().mockResolvedValue({ ...data }),\n get isDirty() {\n return vm.isDirty;\n },\n get isValid() {\n return vm.isValid;\n },\n get errors() {\n return vm.errors;\n },\n get vm() {\n return vm;\n }\n } as unknown as IFormModel;\n}\n\nfunction createMockFormModelFactory(): FormModelFactory.Interface {\n const mockForm = createMockFormModel();\n return {\n create: vi.fn().mockReturnValue(mockForm)\n };\n}\n\nfunction createMockGetFileUseCase(): GetFileUseCase.Interface {\n return {\n execute: vi.fn().mockResolvedValue({ success: true, file: createTestFile() })\n };\n}\n\nfunction createMockUpdateFileUseCase(): UpdateFileUseCase.Interface {\n return {\n execute: vi.fn().mockResolvedValue({ success: true, file: createTestFile() })\n };\n}\n\nfunction createMockDeleteFileUseCase(): DeleteFileUseCase.Interface {\n return {\n execute: vi.fn().mockResolvedValue({ success: true })\n };\n}\n\nfunction createMockPermissions(): FileManagerPermissions.Interface {\n return {\n canAccess: vi.fn().mockReturnValue(true),\n canRead: vi.fn().mockReturnValue(true),\n canCreate: vi.fn().mockReturnValue(true),\n canEdit: vi.fn().mockReturnValue(true),\n canDelete: vi.fn().mockReturnValue(false),\n canAction: vi.fn().mockReturnValue(true)\n } as unknown as FileManagerPermissions.Interface;\n}\n\nfunction createMockSettingsRepository(): GetSettingsRepository.Interface {\n return {\n execute: vi.fn().mockResolvedValue({\n uploadMinFileSize: \"0\",\n uploadMaxFileSize: \"10485760\",\n srcPrefix: \"https://cdn.example.com/\"\n }),\n save: vi.fn().mockImplementation(async (data: Record<string, unknown>) => data),\n settings: {\n uploadMinFileSize: \"0\",\n uploadMaxFileSize: \"10485760\",\n srcPrefix: \"https://cdn.example.com/\"\n }\n };\n}\n\nfunction createTestFile(overrides?: Partial<FmFile>): FmFile {\n return {\n id: \"file-1\",\n name: \"photo.jpg\",\n key: \"files/photo.jpg\",\n src: \"https://cdn.example.com/files/photo.jpg\",\n type: \"image/jpeg\",\n size: 12345,\n metadata: {},\n tags: [\"nature\", \"landscape\"],\n createdOn: \"2025-01-01T00:00:00Z\",\n savedOn: \"2025-01-01T00:00:00Z\",\n createdBy: { id: \"user-1\", displayName: \"Test User\", type: \"admin\" },\n savedBy: { id: \"user-1\", displayName: \"Test User\", type: \"admin\" },\n location: { folderId: \"root\" },\n accessControl: { type: \"public\" },\n ...overrides\n };\n}\n\n// ---------------------------------------------------------------------------\n// Container setup\n// ---------------------------------------------------------------------------\n\ninterface Mocks {\n getFileUseCase: GetFileUseCase.Interface;\n updateFileUseCase: UpdateFileUseCase.Interface;\n deleteFileUseCase: DeleteFileUseCase.Interface;\n formModelFactory: FormModelFactory.Interface;\n permissions: FileManagerPermissions.Interface;\n settingsRepository: GetSettingsRepository.Interface;\n}\n\nfunction createMocks(): Mocks {\n return {\n getFileUseCase: createMockGetFileUseCase(),\n updateFileUseCase: createMockUpdateFileUseCase(),\n deleteFileUseCase: createMockDeleteFileUseCase(),\n formModelFactory: createMockFormModelFactory(),\n permissions: createMockPermissions(),\n settingsRepository: createMockSettingsRepository()\n };\n}\n\nfunction createContainer(mocks: Mocks) {\n const container = new Container();\n\n container.registerInstance(GetFileUseCase, mocks.getFileUseCase);\n container.registerInstance(UpdateFileUseCase, mocks.updateFileUseCase);\n container.registerInstance(DeleteFileUseCase, mocks.deleteFileUseCase);\n container.registerInstance(FormModelFactory, mocks.formModelFactory);\n container.registerInstance(FileManagerPermissions, mocks.permissions);\n container.registerInstance(GetSettingsRepository, mocks.settingsRepository);\n\n // Register the real FileDetailsPresenter implementation.\n container.register(FileDetailsPresenter).inSingletonScope();\n\n return container;\n}\n\n// ---------------------------------------------------------------------------\n// Tests\n// ---------------------------------------------------------------------------\n\ndescribe(\"FileDetailsPresenter\", () => {\n let mocks: Mocks;\n let presenter: IFileDetailsPresenter;\n\n beforeEach(() => {\n mocks = createMocks();\n const container = createContainer(mocks);\n presenter = container.resolve(Abstraction);\n });\n\n // -----------------------------------------------------------------------\n // Initial state.\n // -----------------------------------------------------------------------\n\n it(\"should have null file and loading=null before loadFile is called\", () => {\n expect(presenter.vm.file).toBeNull();\n expect(presenter.vm.loading).toBeNull();\n });\n\n it(\"should have a form vm with layout and isDirty=false initially\", () => {\n expect(presenter.vm.form).toBeDefined();\n expect(presenter.vm.form.layout).toBeDefined();\n expect(presenter.vm.form.isDirty).toBe(false);\n });\n\n it(\"should have null previewUrl when no file is loaded\", () => {\n expect(presenter.vm.previewUrl).toBeNull();\n });\n\n // -----------------------------------------------------------------------\n // loadFile populates vm with file data and FormModel.\n // -----------------------------------------------------------------------\n\n it(\"should populate vm.file after loadFile\", async () => {\n await presenter.loadFile(\"file-1\");\n\n expect(presenter.vm.file).not.toBeNull();\n expect(presenter.vm.file!.id).toBe(\"file-1\");\n expect(presenter.vm.file!.name).toBe(\"photo.jpg\");\n });\n\n it(\"should set loading=null after loadFile completes\", async () => {\n await presenter.loadFile(\"file-1\");\n\n expect(presenter.vm.loading).toBeNull();\n });\n\n it(\"should call GetFileUseCase.execute with the file id\", async () => {\n await presenter.loadFile(\"file-1\");\n\n expect(mocks.getFileUseCase.execute).toHaveBeenCalledWith({ id: \"file-1\" });\n });\n\n it(\"should rebuild the form and call setData after loadFile\", async () => {\n await presenter.loadFile(\"file-1\");\n\n // FormModelFactory.create should be called twice: once in constructor, once in loadFile.\n expect(mocks.formModelFactory.create).toHaveBeenCalledTimes(2);\n });\n\n it(\"should have form.isDirty=false after loadFile\", async () => {\n await presenter.loadFile(\"file-1\");\n\n expect(presenter.vm.form.isDirty).toBe(false);\n });\n\n // -----------------------------------------------------------------------\n // Form submission triggers UpdateFileUseCase.\n // -----------------------------------------------------------------------\n\n it(\"should call UpdateFileUseCase.execute when saveFile is called\", async () => {\n await presenter.loadFile(\"file-1\");\n\n // Mock the form submit to return data.\n const mockForm = (mocks.formModelFactory.create as ReturnType<typeof vi.fn>).mock.results[1]\n .value as IFormModel;\n (mockForm.submit as ReturnType<typeof vi.fn>).mockResolvedValue({\n name: \"updated.jpg\",\n tags: [\"updated\"],\n accessControl: \"private-authenticated\"\n });\n\n await presenter.saveFile();\n\n expect(mocks.updateFileUseCase.execute).toHaveBeenCalledWith({\n id: \"file-1\",\n data: {\n name: \"updated.jpg\",\n tags: [\"updated\"],\n accessControl: { type: \"private-authenticated\" }\n }\n });\n });\n\n it(\"should not call UpdateFileUseCase when no file is loaded\", async () => {\n await presenter.saveFile();\n\n expect(mocks.updateFileUseCase.execute).not.toHaveBeenCalled();\n });\n\n it(\"should not call UpdateFileUseCase when form submit returns false\", async () => {\n await presenter.loadFile(\"file-1\");\n\n // Mock the form submit to return false (validation failure).\n const mockForm = (mocks.formModelFactory.create as ReturnType<typeof vi.fn>).mock.results[1]\n .value as IFormModel;\n (mockForm.submit as ReturnType<typeof vi.fn>).mockResolvedValue(false);\n\n await presenter.saveFile();\n\n expect(mocks.updateFileUseCase.execute).not.toHaveBeenCalled();\n });\n\n // -----------------------------------------------------------------------\n // Permission flags control action visibility.\n // -----------------------------------------------------------------------\n\n it(\"should expose permission flags in vm.permissions\", () => {\n expect(presenter.vm.permissions.canEdit).toBe(true);\n expect(presenter.vm.permissions.canDelete).toBe(false);\n });\n\n it(\"should reflect updated permission mock values\", () => {\n // Override permissions to both false.\n (mocks.permissions.canEdit as ReturnType<typeof vi.fn>).mockReturnValue(false);\n (mocks.permissions.canDelete as ReturnType<typeof vi.fn>).mockReturnValue(true);\n\n expect(presenter.vm.permissions.canEdit).toBe(false);\n expect(presenter.vm.permissions.canDelete).toBe(true);\n });\n\n // -----------------------------------------------------------------------\n // previewUrl construction from srcPrefix + key.\n // -----------------------------------------------------------------------\n\n it(\"should construct previewUrl from srcPrefix + file.key after loadFile\", async () => {\n await presenter.loadFile(\"file-1\");\n\n expect(presenter.vm.previewUrl).toBe(\"https://cdn.example.com/files/photo.jpg\");\n });\n\n it(\"should return null previewUrl when file has no key\", async () => {\n (mocks.getFileUseCase.execute as ReturnType<typeof vi.fn>).mockResolvedValue({\n success: true,\n file: createTestFile({ key: undefined })\n });\n\n await presenter.loadFile(\"file-1\");\n\n expect(presenter.vm.previewUrl).toBeNull();\n });\n\n // -----------------------------------------------------------------------\n // Loading state transitions.\n // -----------------------------------------------------------------------\n\n it(\"should set loading=true during loadFile and false after\", async () => {\n // Capture loading state during execution.\n let loadingDuringExecution: string | null = null;\n\n (mocks.getFileUseCase.execute as ReturnType<typeof vi.fn>).mockImplementation(async () => {\n loadingDuringExecution = presenter.vm.loading;\n return { success: true, file: createTestFile() };\n });\n\n await presenter.loadFile(\"file-1\");\n\n expect(loadingDuringExecution).toBe(\"Loading file...\");\n expect(presenter.vm.loading).toBeNull();\n });\n});\n"],"names":["createMockFormModel","overrides","vm","vi","data","incoming","Object","createMockFormModelFactory","mockForm","createMockGetFileUseCase","createTestFile","createMockUpdateFileUseCase","createMockDeleteFileUseCase","createMockPermissions","createMockSettingsRepository","createMocks","createContainer","mocks","container","Container","GetFileUseCase","UpdateFileUseCase","DeleteFileUseCase","FormModelFactory","FileManagerPermissions","GetSettingsRepository","FileDetailsPresenter","describe","presenter","beforeEach","Abstraction","it","expect","undefined","loadingDuringExecution"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"presentation/FileDetails/FileDetailsPresenter.test.js","sources":["../../../src/presentation/FileDetails/FileDetailsPresenter.test.ts"],"sourcesContent":["// @vitest-environment jsdom\nimport { describe, it, expect, vi, beforeEach } from \"vitest\";\nimport { Container } from \"@webiny/di\";\nimport { GetFileUseCase } from \"../../features/getFile/abstractions.js\";\nimport { UpdateFileUseCase } from \"../../features/updateFile/abstractions.js\";\nimport { DeleteFileUseCase } from \"../../features/deleteFile/abstractions.js\";\nimport { FormModelFactory } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport type { IFormModel } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport type { IFormVM } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport { FileManagerPermissions } from \"../../features/permissions/abstractions.js\";\nimport { GetSettingsRepository } from \"../../features/settings/abstractions.js\";\nimport { FileDetailsPresenter as Abstraction, type IFileDetailsPresenter } from \"./abstractions.js\";\nimport { FileDetailsPresenter } from \"./FileDetailsPresenter.js\";\nimport { WcpService } from \"@webiny/app-admin/features/wcp/abstractions.js\";\nimport type { FmFile } from \"../../features/shared/types.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction createMockFormModel(overrides?: Partial<IFormVM>): IFormModel {\n const vm: IFormVM = {\n layout: [],\n errors: [],\n isDirty: false,\n isValid: true,\n submitCount: 0,\n focusField: vi.fn(),\n getData: vi.fn(() => ({})),\n setData: vi.fn(),\n ...overrides\n };\n\n const data: Record<string, unknown> = {};\n\n return {\n field: vi.fn().mockReturnValue({ setDisabled: vi.fn() }) as any,\n fields: vi.fn() as any,\n layout: vi.fn() as any,\n getData: vi.fn(() => ({ ...data })),\n setData: vi.fn((incoming: Record<string, unknown>) => {\n Object.assign(data, incoming);\n }),\n reset: vi.fn(),\n validate: vi.fn().mockResolvedValue(true),\n submit: vi.fn().mockResolvedValue({ ...data }),\n get isDirty() {\n return vm.isDirty;\n },\n get isValid() {\n return vm.isValid;\n },\n get errors() {\n return vm.errors;\n },\n get vm() {\n return vm;\n }\n } as unknown as IFormModel;\n}\n\nfunction createMockFormModelFactory(): FormModelFactory.Interface {\n const mockForm = createMockFormModel();\n return {\n create: vi.fn().mockReturnValue(mockForm)\n };\n}\n\nfunction createMockGetFileUseCase(): GetFileUseCase.Interface {\n return {\n execute: vi.fn().mockResolvedValue({ success: true, file: createTestFile() })\n };\n}\n\nfunction createMockUpdateFileUseCase(): UpdateFileUseCase.Interface {\n return {\n execute: vi.fn().mockResolvedValue({ success: true, file: createTestFile() })\n };\n}\n\nfunction createMockDeleteFileUseCase(): DeleteFileUseCase.Interface {\n return {\n execute: vi.fn().mockResolvedValue({ success: true })\n };\n}\n\nfunction createMockPermissions(): FileManagerPermissions.Interface {\n return {\n canAccess: vi.fn().mockReturnValue(true),\n canRead: vi.fn().mockReturnValue(true),\n canCreate: vi.fn().mockReturnValue(true),\n canEdit: vi.fn().mockReturnValue(true),\n canDelete: vi.fn().mockReturnValue(false),\n canAction: vi.fn().mockReturnValue(true)\n } as unknown as FileManagerPermissions.Interface;\n}\n\nfunction createMockSettingsRepository(): GetSettingsRepository.Interface {\n return {\n execute: vi.fn().mockResolvedValue({\n uploadMinFileSize: \"0\",\n uploadMaxFileSize: \"10485760\",\n srcPrefix: \"https://cdn.example.com/\"\n }),\n save: vi.fn().mockImplementation(async (data: Record<string, unknown>) => data),\n settings: {\n uploadMinFileSize: \"0\",\n uploadMaxFileSize: \"10485760\",\n srcPrefix: \"https://cdn.example.com/\"\n }\n };\n}\n\nfunction createMockWcpService(canUsePrivateFiles = true): WcpService.Interface {\n return {\n getProject: vi.fn().mockReturnValue({\n canUsePrivateFiles: vi.fn().mockReturnValue(canUsePrivateFiles)\n }),\n isLoaded: vi.fn().mockReturnValue(true),\n canUseFeature: vi.fn().mockReturnValue(false),\n loadProject: vi.fn().mockResolvedValue(undefined)\n } as unknown as WcpService.Interface;\n}\n\nfunction createTestFile(overrides?: Partial<FmFile>): FmFile {\n return {\n id: \"file-1\",\n name: \"photo.jpg\",\n key: \"files/photo.jpg\",\n src: \"https://cdn.example.com/files/photo.jpg\",\n type: \"image/jpeg\",\n size: 12345,\n metadata: {},\n tags: [\"nature\", \"landscape\"],\n createdOn: \"2025-01-01T00:00:00Z\",\n savedOn: \"2025-01-01T00:00:00Z\",\n createdBy: { id: \"user-1\", displayName: \"Test User\", type: \"admin\" },\n savedBy: { id: \"user-1\", displayName: \"Test User\", type: \"admin\" },\n location: { folderId: \"root\" },\n accessControl: { type: \"public\" },\n ...overrides\n };\n}\n\n// ---------------------------------------------------------------------------\n// Container setup\n// ---------------------------------------------------------------------------\n\ninterface Mocks {\n getFileUseCase: GetFileUseCase.Interface;\n updateFileUseCase: UpdateFileUseCase.Interface;\n deleteFileUseCase: DeleteFileUseCase.Interface;\n formModelFactory: FormModelFactory.Interface;\n permissions: FileManagerPermissions.Interface;\n settingsRepository: GetSettingsRepository.Interface;\n wcpService: WcpService.Interface;\n}\n\nfunction createMocks(canUsePrivateFiles = true): Mocks {\n return {\n getFileUseCase: createMockGetFileUseCase(),\n updateFileUseCase: createMockUpdateFileUseCase(),\n deleteFileUseCase: createMockDeleteFileUseCase(),\n formModelFactory: createMockFormModelFactory(),\n permissions: createMockPermissions(),\n settingsRepository: createMockSettingsRepository(),\n wcpService: createMockWcpService(canUsePrivateFiles)\n };\n}\n\nfunction createContainer(mocks: Mocks) {\n const container = new Container();\n\n container.registerInstance(GetFileUseCase, mocks.getFileUseCase);\n container.registerInstance(UpdateFileUseCase, mocks.updateFileUseCase);\n container.registerInstance(DeleteFileUseCase, mocks.deleteFileUseCase);\n container.registerInstance(FormModelFactory, mocks.formModelFactory);\n container.registerInstance(FileManagerPermissions, mocks.permissions);\n container.registerInstance(GetSettingsRepository, mocks.settingsRepository);\n container.registerInstance(WcpService, mocks.wcpService);\n\n // Register the real FileDetailsPresenter implementation.\n container.register(FileDetailsPresenter).inSingletonScope();\n\n return container;\n}\n\n// ---------------------------------------------------------------------------\n// Tests\n// ---------------------------------------------------------------------------\n\ndescribe(\"FileDetailsPresenter\", () => {\n let mocks: Mocks;\n let presenter: IFileDetailsPresenter;\n\n beforeEach(() => {\n mocks = createMocks();\n const container = createContainer(mocks);\n presenter = container.resolve(Abstraction);\n });\n\n // -----------------------------------------------------------------------\n // Initial state.\n // -----------------------------------------------------------------------\n\n it(\"should have null file and loading=null before loadFile is called\", () => {\n expect(presenter.vm.file).toBeNull();\n expect(presenter.vm.loading).toBeNull();\n });\n\n it(\"should have a form vm with layout and isDirty=false initially\", () => {\n expect(presenter.vm.form).toBeDefined();\n expect(presenter.vm.form.layout).toBeDefined();\n expect(presenter.vm.form.isDirty).toBe(false);\n });\n\n it(\"should have null previewUrl when no file is loaded\", () => {\n expect(presenter.vm.previewUrl).toBeNull();\n });\n\n // -----------------------------------------------------------------------\n // loadFile populates vm with file data and FormModel.\n // -----------------------------------------------------------------------\n\n it(\"should populate vm.file after loadFile\", async () => {\n await presenter.loadFile(\"file-1\");\n\n expect(presenter.vm.file).not.toBeNull();\n expect(presenter.vm.file!.id).toBe(\"file-1\");\n expect(presenter.vm.file!.name).toBe(\"photo.jpg\");\n });\n\n it(\"should set loading=null after loadFile completes\", async () => {\n await presenter.loadFile(\"file-1\");\n\n expect(presenter.vm.loading).toBeNull();\n });\n\n it(\"should call GetFileUseCase.execute with the file id\", async () => {\n await presenter.loadFile(\"file-1\");\n\n expect(mocks.getFileUseCase.execute).toHaveBeenCalledWith({ id: \"file-1\" });\n });\n\n it(\"should rebuild the form and call setData after loadFile\", async () => {\n await presenter.loadFile(\"file-1\");\n\n // FormModelFactory.create should be called twice: once in constructor, once in loadFile.\n expect(mocks.formModelFactory.create).toHaveBeenCalledTimes(2);\n });\n\n it(\"should have form.isDirty=false after loadFile\", async () => {\n await presenter.loadFile(\"file-1\");\n\n expect(presenter.vm.form.isDirty).toBe(false);\n });\n\n // -----------------------------------------------------------------------\n // Form submission triggers UpdateFileUseCase.\n // -----------------------------------------------------------------------\n\n it(\"should call UpdateFileUseCase.execute when saveFile is called\", async () => {\n await presenter.loadFile(\"file-1\");\n\n // Mock the form submit to return data.\n const mockForm = (mocks.formModelFactory.create as ReturnType<typeof vi.fn>).mock.results[1]\n .value as IFormModel;\n (mockForm.submit as ReturnType<typeof vi.fn>).mockResolvedValue({\n name: \"updated.jpg\",\n tags: [\"updated\"],\n accessControl: \"private-authenticated\"\n });\n\n await presenter.saveFile();\n\n expect(mocks.updateFileUseCase.execute).toHaveBeenCalledWith({\n id: \"file-1\",\n data: {\n name: \"updated.jpg\",\n tags: [\"updated\"],\n accessControl: { type: \"private-authenticated\" }\n }\n });\n });\n\n it(\"should not call UpdateFileUseCase when no file is loaded\", async () => {\n await presenter.saveFile();\n\n expect(mocks.updateFileUseCase.execute).not.toHaveBeenCalled();\n });\n\n it(\"should not call UpdateFileUseCase when form submit returns false\", async () => {\n await presenter.loadFile(\"file-1\");\n\n // Mock the form submit to return false (validation failure).\n const mockForm = (mocks.formModelFactory.create as ReturnType<typeof vi.fn>).mock.results[1]\n .value as IFormModel;\n (mockForm.submit as ReturnType<typeof vi.fn>).mockResolvedValue(false);\n\n await presenter.saveFile();\n\n expect(mocks.updateFileUseCase.execute).not.toHaveBeenCalled();\n });\n\n // -----------------------------------------------------------------------\n // Permission flags control action visibility.\n // -----------------------------------------------------------------------\n\n it(\"should expose permission flags in vm.permissions\", () => {\n expect(presenter.vm.permissions.canEdit).toBe(true);\n expect(presenter.vm.permissions.canDelete).toBe(false);\n });\n\n it(\"should reflect updated permission mock values\", () => {\n // Override permissions to both false.\n (mocks.permissions.canEdit as ReturnType<typeof vi.fn>).mockReturnValue(false);\n (mocks.permissions.canDelete as ReturnType<typeof vi.fn>).mockReturnValue(true);\n\n expect(presenter.vm.permissions.canEdit).toBe(false);\n expect(presenter.vm.permissions.canDelete).toBe(true);\n });\n\n // -----------------------------------------------------------------------\n // previewUrl construction from srcPrefix + key.\n // -----------------------------------------------------------------------\n\n it(\"should construct previewUrl from srcPrefix + file.key after loadFile\", async () => {\n await presenter.loadFile(\"file-1\");\n\n expect(presenter.vm.previewUrl).toBe(\"https://cdn.example.com/files/photo.jpg\");\n });\n\n it(\"should return null previewUrl when file has no key\", async () => {\n (mocks.getFileUseCase.execute as ReturnType<typeof vi.fn>).mockResolvedValue({\n success: true,\n file: createTestFile({ key: undefined })\n });\n\n await presenter.loadFile(\"file-1\");\n\n expect(presenter.vm.previewUrl).toBeNull();\n });\n\n // -----------------------------------------------------------------------\n // Loading state transitions.\n // -----------------------------------------------------------------------\n\n it(\"should set loading=true during loadFile and false after\", async () => {\n // Capture loading state during execution.\n let loadingDuringExecution: string | null = null;\n\n (mocks.getFileUseCase.execute as ReturnType<typeof vi.fn>).mockImplementation(async () => {\n loadingDuringExecution = presenter.vm.loading;\n return { success: true, file: createTestFile() };\n });\n\n await presenter.loadFile(\"file-1\");\n\n expect(loadingDuringExecution).toBe(\"Loading file...\");\n expect(presenter.vm.loading).toBeNull();\n });\n\n // -------------------------------------------------------------------\n // When Private Files is NOT licensed.\n // -------------------------------------------------------------------\n\n describe(\"when Private Files is NOT licensed\", () => {\n let unlicensedMocks: Mocks;\n let unlicensedPresenter: IFileDetailsPresenter;\n\n beforeEach(() => {\n unlicensedMocks = createMocks(false);\n const container = createContainer(unlicensedMocks);\n unlicensedPresenter = container.resolve(Abstraction);\n });\n\n it(\"should NOT include accessControl in the save payload\", async () => {\n await unlicensedPresenter.loadFile(\"file-1\");\n\n const mockForm = (unlicensedMocks.formModelFactory.create as ReturnType<typeof vi.fn>)\n .mock.results[1].value as IFormModel;\n (mockForm.submit as ReturnType<typeof vi.fn>).mockResolvedValue({\n name: \"updated.jpg\",\n description: \"\",\n tags: [\"updated\"]\n });\n\n await unlicensedPresenter.saveFile();\n\n const callArgs = (unlicensedMocks.updateFileUseCase.execute as ReturnType<typeof vi.fn>)\n .mock.calls[0][0];\n expect(callArgs.data).not.toHaveProperty(\"accessControl\");\n });\n\n it(\"should NOT set accessControl in form data during loadFile\", async () => {\n await unlicensedPresenter.loadFile(\"file-1\");\n\n const mockForm = (unlicensedMocks.formModelFactory.create as ReturnType<typeof vi.fn>)\n .mock.results[1].value as IFormModel;\n const setDataCall = (mockForm.setData as ReturnType<typeof vi.fn>).mock.calls[0][0];\n expect(setDataCall).not.toHaveProperty(\"accessControl\");\n });\n\n it(\"should NOT disable accessControl field when user lacks edit permission\", async () => {\n (unlicensedMocks.permissions.canEdit as ReturnType<typeof vi.fn>).mockReturnValue(\n false\n );\n\n await unlicensedPresenter.loadFile(\"file-1\");\n\n const mockForm = (unlicensedMocks.formModelFactory.create as ReturnType<typeof vi.fn>)\n .mock.results[1].value as IFormModel;\n expect(mockForm.field).toHaveBeenCalledTimes(3);\n expect(mockForm.field).not.toHaveBeenCalledWith(\"accessControl\");\n });\n });\n});\n"],"names":["createMockFormModel","overrides","vm","vi","data","incoming","Object","createMockFormModelFactory","mockForm","createMockGetFileUseCase","createTestFile","createMockUpdateFileUseCase","createMockDeleteFileUseCase","createMockPermissions","createMockSettingsRepository","createMockWcpService","canUsePrivateFiles","undefined","createMocks","createContainer","mocks","container","Container","GetFileUseCase","UpdateFileUseCase","DeleteFileUseCase","FormModelFactory","FileManagerPermissions","GetSettingsRepository","WcpService","FileDetailsPresenter","describe","presenter","beforeEach","Abstraction","it","expect","loadingDuringExecution","unlicensedMocks","unlicensedPresenter","callArgs","setDataCall"],"mappings":"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"}
|