diff --git a/src/client/web/src/client/files_mock.ts b/src/client/web/src/client/files_mock.ts index 062422c..8aea448 100644 --- a/src/client/web/src/client/files_mock.ts +++ b/src/client/web/src/client/files_mock.ts @@ -6,115 +6,164 @@ import { ListSharingsResp, } from "./"; -export class FilesClient { - private url: string; +export interface FilesClientResps { + createMockRespID?: number; + createMockResp?: Response; + deleteMockResp?: Response; + metadataMockResp?: Response; + mkdirMockResp?: Response; + moveMockResp?: Response; + uploadChunkMockResp?: Response; + uploadChunkMockRespID?: number; + uploadStatusMockResp?: Response; + uploadStatusMockRespID?: number; + listMockResp?: Response; + listHomeMockResp?: Response; + listUploadingsMockResp?: Response; + deleteUploadingMockResp?: Response; + addSharingMockResp?: Response; + deleteSharingMockResp?: Response; + listSharingsMockResp?: Response; + isSharingMockResp?: Response; +} - private createMockRespID: number = 0; - private createMockResps: Array>; - private deleteMockResp: Promise; - private metadataMockResp: Promise; - private mkdirMockResp: Promise; - private moveMockResp: Promise; - private uploadChunkMockResps: Array>>; - private uploadChunkMockRespID: number = 0; - private uploadStatusMockResps: Array>>; - private uploadStatusMockRespID: number = 0; - private listMockResp: Promise>; - private listHomeMockResp: Promise>; - private listUploadingsMockResp: Promise>; - private deleteUploadingMockResp: Promise; - private addSharingMockResp: Promise; - private deleteSharingMockResp: Promise; - private listSharingsMockResp: Promise>; - private isSharingMockResp: Promise; +export const resps = { + createMockResp: { status: 200, statusText: "", data: {} }, + deleteMockResp: { status: 200, statusText: "", data: {} }, + metadataMockResp: { status: 200, statusText: "", data: {} }, + mkdirMockResp: { status: 200, statusText: "", data: {} }, + moveMockResp: { status: 200, statusText: "", data: {} }, + uploadChunkMockResp: { + status: 200, + statusText: "", + data: { + path: "mockPath/file", + isDir: false, + fileSize: 5, + uploaded: 3, + }, + }, + uploadChunkMockRespID: 0, + uploadStatusMockResp: { + status: 200, + statusText: "", + data: { + path: "mockPath/file", + isDir: false, + fileSize: 5, + uploaded: 3, + }, + }, + uploadStatusMockRespID: 0, + listMockResp: { + status: 200, + statusText: "", + data: { + cwd: "mock_cwd", + metadatas: [ + { + name: "mock_file", + size: 5, + modTime: "0", + isDir: false, + }, + { + name: "mock_dir", + size: 0, + modTime: "0", + isDir: true, + }, + ], + }, + }, + listHomeMockResp: { + status: 200, + statusText: "", + data: { + cwd: "mock_home/files", + metadatas: [ + { + name: "mock_file", + size: 5, + modTime: "0", + isDir: false, + }, + { + name: "mock_dir", + size: 0, + modTime: "0", + isDir: true, + }, + ], + }, + }, + listUploadingsMockResp: { + status: 200, + statusText: "", + data: { + uploadInfos: [ + { + realFilePath: "mock_ realFilePath1", + size: 5, + uploaded: 3, + }, + { + realFilePath: "mock_ realFilePath2", + size: 5, + uploaded: 3, + }, + ], + }, + }, + deleteUploadingMockResp: { status: 200, statusText: "", data: {} }, + addSharingMockResp: { status: 200, statusText: "", data: {} }, + deleteSharingMockResp: { status: 200, statusText: "", data: {} }, + listSharingsMockResp: { + status: 200, + statusText: "", + data: { + sharingDirs: ["mock_sharingfolder1", "mock_sharingfolder2"], + }, + }, + isSharingMockResp: { status: 200, statusText: "", data: {} }, +}; +export class MockFilesClient { + private url: string; + private resps: FilesClientResps; constructor(url: string) { this.url = url; + this.resps = resps; } - createMock = (resps: Array>) => { - this.createMockResps = resps; + setMock = (resps: FilesClientResps) => { + this.resps = resps; }; - deleteMock = (resp: Promise) => { - this.deleteMockResp = resp; - }; - - metadataMock = (resp: Promise) => { - this.metadataMockResp = resp; - }; - - mkdirMock = (resp: Promise) => { - this.mkdirMockResp = resp; - }; - - moveMock = (resp: Promise) => { - this.moveMockResp = resp; - }; - - uploadChunkMock = (resps: Array>>) => { - this.uploadChunkMockResps = resps; - }; - - uploadStatusMock = (resps: Array>>) => { - this.uploadStatusMockResps = resps; - }; - - listMock = (resp: Promise>) => { - this.listMockResp = resp; - }; - - listHomeMock = (resp: Promise>) => { - this.listMockResp = resp; - }; - - listUploadingsMock = (resp: Promise>) => { - this.listUploadingsMockResp = resp; - }; - - deleteUploadingMock = (resp: Promise) => { - this.deleteUploadingMockResp = resp; - }; - - addSharingMock = (resp: Promise) => { - this.addSharingMockResp = resp; - }; - - deleteSharingMock = (resp: Promise) => { - this.deleteSharingMockResp = resp; - }; - - listSharingsMock = (resp: Promise) => { - this.listSharingsMockResp = resp; - }; - - isSharingMock = (resp: Promise) => { - this.isSharingMockResp = resp; + wrapPromise = (resp: any): Promise => { + return new Promise((resolve) => { + resolve(resp); + }); }; create = (filePath: string, fileSize: number): Promise => { - if (this.createMockRespID < this.createMockResps.length) { - return this.createMockResps[this.createMockRespID++]; - } - throw new Error( - `this.createMockRespID (${this.createMockRespID}) out of bound: ${this.createMockResps.length}` - ); + return this.wrapPromise(this.resps.createMockResp); }; delete = (filePath: string): Promise => { - return this.deleteMockResp; + return this.wrapPromise(this.resps.deleteMockResp); }; metadata = (filePath: string): Promise => { - return this.metadataMockResp; + return this.wrapPromise(this.resps.metadataMockResp); }; mkdir = (dirpath: string): Promise => { - return this.mkdirMockResp; + return this.wrapPromise(this.resps.mkdirMockResp); }; move = (oldPath: string, newPath: string): Promise => { - return this.moveMockResp; + return this.wrapPromise(this.resps.moveMockResp); }; uploadChunk = ( @@ -122,52 +171,42 @@ export class FilesClient { content: string | ArrayBuffer, offset: number ): Promise> => { - if (this.uploadChunkMockRespID < this.uploadChunkMockResps.length) { - return this.uploadChunkMockResps[this.uploadChunkMockRespID++]; - } - throw new Error( - `this.uploadChunkMockRespID (${this.uploadChunkMockRespID}) out of bound: ${this.uploadChunkMockResps.length}` - ); + return this.wrapPromise(this.resps.uploadChunkMockResp); }; uploadStatus = (filePath: string): Promise> => { - if (this.uploadStatusMockRespID < this.uploadStatusMockResps.length) { - return this.uploadStatusMockResps[this.uploadStatusMockRespID++]; - } - throw new Error( - `this.uploadStatusMockRespID (${this.uploadStatusMockRespID}) out of bound: ${this.uploadStatusMockResps.length}` - ); + return this.wrapPromise(this.resps.uploadStatusMockResp); }; list = (dirPath: string): Promise> => { - return this.listMockResp; + return this.wrapPromise(this.resps.listMockResp); }; listHome = (): Promise> => { - return this.listHomeMockResp; + return this.wrapPromise(this.resps.listHomeMockResp); }; listUploadings = (): Promise> => { - return this.listUploadingsMockResp; + return this.wrapPromise(this.resps.listUploadingsMockResp); }; deleteUploading = (filePath: string): Promise> => { - return this.deleteUploadingMockResp; + return this.wrapPromise(this.resps.deleteUploadingMockResp); }; addSharing = (dirPath: string): Promise => { - return this.addSharingMockResp; + return this.wrapPromise(this.resps.addSharingMockResp); }; deleteSharing = (dirPath: string): Promise => { - return this.deleteSharingMockResp; + return this.wrapPromise(this.resps.deleteSharingMockResp); }; listSharings = (): Promise> => { - return this.listSharingsMockResp; + return this.wrapPromise(this.resps.listSharingsMockResp); }; isSharing = (dirPath: string): Promise => { - return this.isSharingMockResp; + return this.wrapPromise(this.resps.isSharingMockResp); }; } diff --git a/src/client/web/src/client/users_mock.ts b/src/client/web/src/client/users_mock.ts index 6b006b8..fb9eca3 100644 --- a/src/client/web/src/client/users_mock.ts +++ b/src/client/web/src/client/users_mock.ts @@ -1,115 +1,157 @@ // TODO: replace this with jest mocks import { Response } from "./"; +export interface UsersClientResps { + loginMockResp: Response; + logoutMockResp: Response; + isAuthedMockResp: Response; + setPwdMockResp: Response; + forceSetPwdMockResp: Response; + addUserMockResp: Response; + delUserMockResp: Response; + listUsersMockResp: Response; + addRoleMockResp: Response; + delRoleMockResp: Response; + listRolesMockResp: Response; + selfMockResp: Response; + getCaptchaIDMockResp: Response; +} + +export const resps = { + loginMockResp: { status: 200, statusText: "", data: {} }, + logoutMockResp: { status: 200, statusText: "", data: {} }, + isAuthedMockResp: { status: 200, statusText: "", data: {} }, + setPwdMockResp: { status: 200, statusText: "", data: {} }, + forceSetPwdMockResp: { status: 200, statusText: "", data: {} }, + addUserMockResp: { status: 200, statusText: "", data: {} }, + delUserMockResp: { status: 200, statusText: "", data: {} }, + listUsersMockResp: { + status: 200, + statusText: "", + data: { + users: [ + { + id: "0", + name: "mock_username0", + pwd: "mock_pwd0", + role: "mock_role0", + usedSpace: "128", + quota: { + spaceLimit: 1024, + uploadSpeedLimit: 1024, + downloadSpeedLimit: 1024, + }, + }, + { + id: "1", + name: "mock_username1", + pwd: "mock_pwd1", + role: "mock_role1", + usedSpace: "256", + quota: { + spaceLimit: 1024, + uploadSpeedLimit: 1024, + downloadSpeedLimit: 1024, + }, + }, + ], + }, + }, + addRoleMockResp: { status: 200, statusText: "", data: {} }, + delRoleMockResp: { status: 200, statusText: "", data: {} }, + listRolesMockResp: { + status: 200, + statusText: "", + data: { + roles: ["admin", "users", "visitor"], + }, + }, + selfMockResp: { + status: 200, + statusText: "", + data: { + id: 0, + name: "mockUser", + role: "admin", + usedSpace: "256", + }, + }, + getCaptchaIDMockResp: { + status: 200, + statusText: "", + data: { + id: "mockCaptchaID", + }, + }, +}; export class MockUsersClient { private url: string; - private loginMockResp: Promise; - private logoutMockResp: Promise; - private isAuthedMockResp: Promise; - private setPwdMockResp: Promise; - private forceSetPwdMockResp: Promise; - private addUserMockResp: Promise; - private delUserMockResp: Promise; - private listUsersMockResp: Promise; - private addRoleMockResp: Promise; - private delRoleMockResp: Promise; - private listRolesMockResp: Promise; - private selfMockResp: Promise; - private getCaptchaIDMockResp: Promise; + private resps: UsersClientResps; constructor(url: string) { this.url = url; + this.resps = resps; } - loginMock = (resp: Promise) => { - this.loginMockResp = resp; + setMock = (resps: UsersClientResps) => { + this.resps = resps; }; - logoutMock = (resp: Promise) => { - this.logoutMockResp = resp; - }; - isAuthedMock = (resp: Promise) => { - this.isAuthedMockResp = resp; - }; - setPwdMock = (resp: Promise) => { - this.setPwdMockResp = resp; - }; - forceSetPwdMock = (resp: Promise) => { - this.forceSetPwdMockResp = resp; - }; - addUserMock = (resp: Promise) => { - this.addUserMockResp = resp; - }; - delUserMock = (resp: Promise) => { - this.delUserMockResp = resp; - }; - listUsersMock = (resp: Promise) => { - this.listUsersMockResp = resp; - }; - addRoleMock = (resp: Promise) => { - this.addRoleMockResp = resp; - }; - delRoleMock = (resp: Promise) => { - this.delRoleMockResp = resp; - }; - listRolesMock = (resp: Promise) => { - this.listRolesMockResp = resp; - }; - selfMock = (resp: Promise) => { - this.selfMockResp = resp; - }; - getCaptchaIDMock = (resp: Promise) => { - this.getCaptchaIDMockResp = resp; + + wrapPromise = (resp: any): Promise => { + return new Promise((resolve) => { + resolve(resp); + }); }; login = (user: string, pwd: string): Promise => { - return this.loginMockResp; + return this.wrapPromise(this.resps.loginMockResp); }; logout = (): Promise => { - return this.logoutMockResp; + return this.wrapPromise(this.resps.logoutMockResp); }; isAuthed = (): Promise => { - return this.isAuthedMockResp; + return this.wrapPromise(this.resps.isAuthedMockResp); }; setPwd = (oldPwd: string, newPwd: string): Promise => { - return this.setPwdMockResp; + return this.wrapPromise(this.resps.setPwdMockResp); }; forceSetPwd = (userID: string, newPwd: string): Promise => { - return this.forceSetPwdMockResp; + return this.wrapPromise(this.resps.forceSetPwdMockResp); }; addUser = (name: string, pwd: string, role: string): Promise => { - return this.addUserMockResp; + return this.wrapPromise(this.resps.addUserMockResp); }; delUser = (userID: string): Promise => { - return this.delUserMockResp; + return this.wrapPromise(this.resps.delUserMockResp); }; listUsers = (): Promise => { - return this.listUsersMockResp; + return this.wrapPromise(this.resps.listUsersMockResp); }; addRole = (role: string): Promise => { - return this.addRoleMockResp; + return this.wrapPromise(this.resps.addRoleMockResp); }; delRole = (role: string): Promise => { - return this.delRoleMockResp; + return this.wrapPromise(this.resps.delRoleMockResp); }; listRoles = (): Promise => { - return this.listRolesMockResp; + return this.wrapPromise(this.resps.listRolesMockResp); }; self = (): Promise => { - return this.selfMockResp; + return this.wrapPromise(this.resps.selfMockResp); }; getCaptchaID = (): Promise => { - return this.getCaptchaIDMockResp; + return this.wrapPromise(this.resps.getCaptchaIDMockResp); }; } diff --git a/src/client/web/src/components/__test__/browser.test.tsx b/src/client/web/src/components/__test__/browser.test.tsx index d9ef6dd..9cc2443 100644 --- a/src/client/web/src/components/__test__/browser.test.tsx +++ b/src/client/web/src/components/__test__/browser.test.tsx @@ -1,283 +1,121 @@ -import * as React from "react"; -import { List, Map } from "immutable"; -import { mock, instance, anyString, anything, when, verify } from "ts-mockito"; +import { mock, instance, verify, when, anything } from "ts-mockito"; +import { List } from "immutable"; -import { ICoreState, newWithWorker, initState } from "../core_state"; -import { - makePromise, - makeNumberResponse, - mockUpdate, - addMockUpdate, - mockFileList, -} from "../../test/helpers"; import { Browser } from "../browser"; -// import { Updater, setUpdater } from "../browser.updater"; +import { ICoreState, newWithWorker } from "../core_state"; +import { updater } from "../state_updater"; +import { MockWorker } from "../../worker/interface"; +import { MockUsersClient, resps as usersResps } from "../../client/users_mock"; +import { MockFilesClient, resps as filesResps } from "../../client/files_mock"; +import { mockFileList } from "../../test/helpers"; -import { updater, Updater, setUpdater } from "../state_updater"; -import { MockUsersClient } from "../../client/users_mock"; -import { UsersClient } from "../../client/users"; -import { FilesClient } from "../../client/files"; -import { FilesClient as MockFilesClient } from "../../client/files_mock"; -import { MetadataResp, UploadInfo } from "../../client"; -import { MockWorker, UploadEntry } from "../../worker/interface"; -import { UploadMgr, setUploadMgr } from "../../worker/upload_mgr"; +describe("Browser", () => { + const initBrowser = (): any => { + const mockWorkerClass = mock(MockWorker); + const mockWorker = instance(mockWorkerClass); -// describe("Browser", () => { -// const mockWorkerClass = mock(MockWorker); -// const mockWorker = instance(mockWorkerClass); + const coreState = newWithWorker(mockWorker); + const usersCl = new MockUsersClient(""); + const filesCl = new MockFilesClient(""); -// test("Updater: addUploads: add each files to UploadMgr", async () => { -// let coreState = initState(); -// const UploadMgrClass = mock(UploadMgr); -// const uploadMgr = instance(UploadMgrClass); -// setUploadMgr(uploadMgr); + updater().init(coreState); + updater().setClients(usersCl, filesCl); -// const filePaths = ["./file1", "./file2"]; -// const fileList = mockFileList(filePaths); -// // const updater = new Updater(); -// updater().setUploadings = (infos: Map) => {}; -// updater().init(coreState); + const browser = new Browser({ + dirPath: coreState.browser.dirPath, + isSharing: coreState.browser.isSharing, + items: coreState.browser.items, + uploadings: coreState.browser.uploadings, + sharings: coreState.browser.sharings, + uploadFiles: coreState.browser.uploadFiles, + uploadValue: coreState.browser.uploadValue, + isVertical: coreState.browser.isVertical, + update: (updater: (prevState: ICoreState) => ICoreState) => {}, + }); -// updater().addUploads(fileList); + return { + browser, + usersCl, + filesCl, + }; + }; -// // it seems that new File will do some file path escaping, so just check call time here -// verify(UploadMgrClass.add(anything(), anything())).times(filePaths.length); -// // filePaths.forEach((filePath, i) => { -// // verify(UploadMgrClass.add(anything(), filePath)).once(); -// // }); -// }); + test("addUploads", async () => { + const { browser, usersCl, filesCl } = initBrowser(); -// test("Updater: deleteUploads: call UploadMgr and api to delete", async () => { -// let coreState = initState(); -// const UploadMgrClass = mock(UploadMgr); -// const uploadMgr = instance(UploadMgrClass); -// setUploadMgr(uploadMgr); + const newSharings = [ + "mock_sharingfolder1", + "mock_sharingfolder2", + "newSharing", + ]; -// // const updater = new Updater(); -// const filesClientClass = mock(FilesClient); -// when(filesClientClass.deleteUploading(anyString())).thenResolve({ -// status: 200, -// statusText: "", -// data: "", -// }); -// const filesClient = instance(filesClientClass); -// const usersClientClass = mock(UsersClient); -// const usersClient = instance(usersClientClass); -// updater().init(coreState); -// updater().setClients(usersClient, filesClient); + filesCl.setMock({ + ...filesResps, + listSharingsMockResp: { + status: 200, + statusText: "", + data: { + sharingDirs: newSharings, + }, + }, + }); -// const filePath = "./path/file"; -// updater().deleteUpload(filePath); + await browser.addSharing(); -// verify(filesClientClass.deleteUploading(filePath)).once(); -// verify(UploadMgrClass.delete(filePath)).once(); -// }); + // TODO: check addSharing's input + expect(updater().props.browser.isSharing).toEqual(true); + expect(updater().props.browser.sharings).toEqual(List(newSharings)); + }); -// test("Updater: delete", async () => { -// const tests = [ -// { -// dirPath: "path/path2", -// items: [ -// { -// name: "file", -// size: 1, -// modTime: "1-1", -// isDir: false, -// }, -// { -// name: "folder", -// size: 0, -// modTime: "1-1", -// isDir: true, -// }, -// ], -// selected: { -// file: true, -// }, -// listResp: { -// status: 200, -// statusText: "", -// data: { -// metadatas: [ -// { -// name: "folder", -// size: 0, -// modTime: "1-1", -// isDir: true, -// }, -// ], -// }, -// }, -// filePath: "path/file", -// }, -// ]; + test("deleteUploads", async () => { + const { browser, usersCl, filesCl } = initBrowser(); -// const usersClient = new MockUsersClient(""); -// const filesClient = new MockFilesClient(""); -// for (let i = 0; i < tests.length; i++) { -// const tc = tests[i]; -// // const updater = new Updater(); -// updater().setClients(usersClient, filesClient); -// filesClient.listMock(makePromise(tc.listResp)); -// filesClient.deleteMock(makeNumberResponse(200)); -// const coreState = newWithWorker(mockWorker); -// updater().init(coreState); + const newSharings = ["mock_sharingfolder1", "mock_sharingfolder2"]; -// await updater().delete( -// List(tc.dirPath.split("/")), -// List(tc.items), -// Map(tc.selected) -// ); + filesCl.setMock({ + ...filesResps, + listSharingsMockResp: { + status: 200, + statusText: "", + data: { + sharingDirs: newSharings, + }, + }, + }); -// const newState = updater().updateBrowser(coreState); + await browser.deleteSharing(); -// // TODO: check inputs of delete -// newState.panel.browser.items.forEach((item, i) => { -// expect(item.name).toEqual(tc.listResp.data.metadatas[i].name); -// expect(item.size).toEqual(tc.listResp.data.metadatas[i].size); -// expect(item.modTime).toEqual(tc.listResp.data.metadatas[i].modTime); -// expect(item.isDir).toEqual(tc.listResp.data.metadatas[i].isDir); -// }); -// } -// }); + // TODO: check delSharing's input + expect(updater().props.browser.isSharing).toEqual(false); + expect(updater().props.browser.sharings).toEqual(List(newSharings)); + }); -// test("Updater: setItems", async () => { -// const tests = [ -// { -// listResp: { -// status: 200, -// statusText: "", -// data: { -// metadatas: [ -// { -// name: "file", -// size: 1, -// modTime: "1-1", -// isDir: false, -// }, -// { -// name: "folder", -// size: 0, -// modTime: "1-1", -// isDir: true, -// }, -// ], -// }, -// }, -// filePath: "path/file", -// }, -// ]; + test("chdir", async () => { + const { browser, usersCl, filesCl } = initBrowser(); -// const usersClient = new MockUsersClient(""); -// const filesClient = new MockFilesClient(""); + const newSharings = ["mock_sharingfolder1", "mock_sharingfolder2"]; + const newCwd = List(["newPos", "subFolder"]); -// for (let i = 0; i < tests.length; i++) { -// const tc = tests[i]; + filesCl.setMock({ + ...filesResps, + listSharingsMockResp: { + status: 200, + statusText: "", + data: { + sharingDirs: newSharings, + }, + }, + }); -// filesClient.listMock(makePromise(tc.listResp)); -// updater().setClients(usersClient, filesClient); -// const coreState = newWithWorker(mockWorker); -// updater().init(coreState); + await browser.chdir(newCwd); -// await updater().setItems(List(tc.filePath.split("/"))); -// const newState = updater().updateBrowser(coreState); - -// newState.panel.browser.items.forEach((item, i) => { -// expect(item.name).toEqual(tc.listResp.data.metadatas[i].name); -// expect(item.size).toEqual(tc.listResp.data.metadatas[i].size); -// expect(item.modTime).toEqual(tc.listResp.data.metadatas[i].modTime); -// expect(item.isDir).toEqual(tc.listResp.data.metadatas[i].isDir); -// }); -// } -// }); - -// test("Updater: moveHere", async () => { -// const tests = [ -// { -// dirPath1: "path/path1", -// dirPath2: "path/path2", -// selected: { -// file1: true, -// file2: true, -// }, -// listResp: { -// status: 200, -// statusText: "", -// data: { -// metadatas: [ -// { -// name: "file1", -// size: 1, -// modTime: "1-1", -// isDir: false, -// }, -// { -// name: "file2", -// size: 2, -// modTime: "1-1", -// isDir: false, -// }, -// ], -// }, -// }, -// }, -// ]; - -// const usersClient = new MockUsersClient(""); -// const filesClient = new MockFilesClient(""); -// for (let i = 0; i < tests.length; i++) { -// const tc = tests[i]; - -// filesClient.listMock(makePromise(tc.listResp)); -// filesClient.moveMock(makeNumberResponse(200)); -// updater().setClients(usersClient, filesClient); - -// const coreState = newWithWorker(mockWorker); -// updater().init(coreState); -// await updater().moveHere(tc.dirPath1, tc.dirPath2, Map(tc.selected)); - -// const newState = updater().updateBrowser(coreState); - -// // TODO: check inputs of move -// newState.panel.browser.items.forEach((item, i) => { -// expect(item.name).toEqual(tc.listResp.data.metadatas[i].name); -// expect(item.size).toEqual(tc.listResp.data.metadatas[i].size); -// expect(item.modTime).toEqual(tc.listResp.data.metadatas[i].modTime); -// expect(item.isDir).toEqual(tc.listResp.data.metadatas[i].isDir); -// }); -// } -// }); - -// test("Browser: deleteUpload: tell uploader to deleteUpload and refreshUploadings", async () => { -// let coreState = initState(); -// addMockUpdate(coreState.panel.browser); -// const component = new Browser(coreState.panel.browser); -// const UpdaterClass = mock(Updater); -// const mockUpdater = instance(UpdaterClass); -// setUpdater(mockUpdater); -// when(UpdaterClass.setItems(anything())).thenResolve(); -// when(UpdaterClass.deleteUpload(anyString())).thenResolve(true); -// when(UpdaterClass.refreshUploadings()).thenResolve(true); - -// const filePath = "filePath"; -// await component.deleteUpload(filePath); - -// verify(UpdaterClass.deleteUpload(filePath)).once(); -// verify(UpdaterClass.refreshUploadings()).once(); -// }); - -// test("Browser: stopUploading: tell updater to stopUploading", async () => { -// let coreState = initState(); -// addMockUpdate(coreState.panel.browser); -// const component = new Browser(coreState.panel.browser); -// const UpdaterClass = mock(Updater); -// const mockUpdater = instance(UpdaterClass); -// setUpdater(mockUpdater); -// when(UpdaterClass.stopUploading(anyString())).thenReturn(); - -// const filePath = "filePath"; -// component.stopUploading(filePath); - -// verify(UpdaterClass.stopUploading(filePath)).once(); -// }); -// }); + expect(updater().props.browser.dirPath).toEqual(newCwd); + expect(updater().props.browser.isSharing).toEqual(true); + expect(updater().props.browser.sharings).toEqual( + List(filesResps.listSharingsMockResp.data.sharingDirs) + ); + expect(updater().props.browser.items).toEqual( + List(filesResps.listHomeMockResp.data.metadatas) + ); + }); +}); diff --git a/src/client/web/src/components/__test__/pane_login.test.tsx b/src/client/web/src/components/__test__/pane_login.test.tsx index 964774f..c88cc72 100644 --- a/src/client/web/src/components/__test__/pane_login.test.tsx +++ b/src/client/web/src/components/__test__/pane_login.test.tsx @@ -1,64 +1,62 @@ +import { List, Set, Map } from "immutable"; import { mock, instance } from "ts-mockito"; -import { newWithWorker } from "../core_state"; +import { User } from "../../client"; +import { AuthPane } from "../pane_login"; +import { ICoreState, newWithWorker } from "../core_state"; import { updater } from "../state_updater"; -import { MockUsersClient } from "../../client/users_mock"; -import { FilesClient } from "../../client/files_mock"; -import { Response } from "../../client"; import { MockWorker } from "../../worker/interface"; +import { MockUsersClient, resps as usersResps } from "../../client/users_mock"; +import { MockFilesClient, resps as filesResps } from "../../client/files_mock"; -describe("AuthPane", () => { - const mockWorkerClass = mock(MockWorker); - const mockWorker = instance(mockWorkerClass); +describe("Login", () => { + test("login", async () => { + const mockWorkerClass = mock(MockWorker); + const mockWorker = instance(mockWorkerClass); - const makePromise = (ret: any): Promise => { - return new Promise((resolve) => { - resolve(ret); + const coreState = newWithWorker(mockWorker); + const pane = new AuthPane({ + authed: coreState.login.authed, + captchaID: coreState.login.captchaID, + update: (updater: (prevState: ICoreState) => ICoreState) => {}, }); - }; - const makeNumberResponse = (status: number): Promise => { - return makePromise({ - status: status, - statusText: "", - data: {}, + + const usersCl = new MockUsersClient(""); + const filesCl = new MockFilesClient(""); + updater().init(coreState); + updater().setClients(usersCl, filesCl); + + await pane.login(); + + // TODO: state is not checked + + // login + expect(updater().props.login).toEqual({ + authed: true, + captchaID: "", }); - }; - test("Updater-initIsAuthed", async () => { - const tests = [ - { - loginStatus: 200, - logoutStatus: 200, - isAuthedStatus: 200, - setPwdStatus: 200, - isAuthed: true, - }, - { - loginStatus: 200, - logoutStatus: 200, - isAuthedStatus: 500, - setPwdStatus: 200, - isAuthed: false, - }, - ]; + // panes + expect(updater().props.panes).toEqual({ + userRole: "admin", + displaying: "", + paneNames: Set(["settings", "login", "admin"]), + }); - const usersClient = new MockUsersClient(""); - const filesClient = new FilesClient(""); - for (let i = 0; i < tests.length; i++) { - const tc = tests[i]; - - usersClient.loginMock(makeNumberResponse(tc.loginStatus)); - usersClient.logoutMock(makeNumberResponse(tc.logoutStatus)); - usersClient.isAuthedMock(makeNumberResponse(tc.isAuthedStatus)); - usersClient.setPwdMock(makeNumberResponse(tc.setPwdStatus)); - - const coreState = newWithWorker(mockWorker); - updater().setClients(usersClient, filesClient); - updater().init(coreState); - await updater().initIsAuthed(); - const newState = updater().updateLogin(coreState); - - expect(newState.login.authed).toEqual(tc.isAuthed); - } + // admin + let usersMap = Map({}); + usersResps.listUsersMockResp.data.users.forEach((user: User) => { + usersMap = usersMap.set(user.name, user); + }); + let roles = Set(); + Object.keys(usersResps.listRolesMockResp.data.roles).forEach( + (role: string) => { + roles = roles.add(role); + } + ); + expect(updater().props.admin).toEqual({ + users: usersMap, + roles: roles, + }); }); }); diff --git a/src/client/web/src/components/__test__/panes.test.tsx b/src/client/web/src/components/__test__/panes.test.tsx index 5797ce4..3d01916 100644 --- a/src/client/web/src/components/__test__/panes.test.tsx +++ b/src/client/web/src/components/__test__/panes.test.tsx @@ -1,55 +1,31 @@ -import { Set } from "immutable"; +import { mock, instance } from "ts-mockito"; -import { ICoreState, initState } from "../core_state"; import { Panes } from "../panes"; -import { mockUpdate } from "../../test/helpers"; +import { ICoreState, newWithWorker } from "../core_state"; import { updater } from "../state_updater"; +import { MockWorker } from "../../worker/interface"; describe("Panes", () => { - test("Panes: closePane", async () => { - interface TestCase { - preState: ICoreState; - postState: ICoreState; - } + test("closePane", async () => { + const mockWorkerClass = mock(MockWorker); + const mockWorker = instance(mockWorkerClass); - const tcs: any = [ - { - preState: { - panes: { - displaying: "settings", - paneNames: Set(["settings", "login"]), - update: mockUpdate, - }, - }, - postState: { - panes: { - displaying: "", - paneNames: Set(["settings", "login"]), - update: mockUpdate, - }, - }, - }, - ]; + const coreState = newWithWorker(mockWorker); + const panes = new Panes({ + panes: coreState.panes, + admin: coreState.admin, + login: coreState.login, + update: (updater: (prevState: ICoreState) => ICoreState) => {}, + }); - const setState = (patch: any, state: ICoreState): ICoreState => { - state.panes = patch.panes; - return state; - }; + updater().init(coreState); - tcs.forEach((tc: TestCase) => { - const preState = setState(tc.preState, initState()); - const postState = setState(tc.postState, initState()); + panes.closePane(); - const component = new Panes({ - panes: preState.panes, - admin: preState.admin, - login: preState.login, - update: mockUpdate, - }); - updater().init(preState); - - component.closePane(); - expect(updater().props).toEqual(postState); + expect(updater().props.panes).toEqual({ + userRole: coreState.panes.userRole, + displaying: "", + paneNames: coreState.panes.paneNames, }); }); }); diff --git a/src/client/web/src/components/__test__/state_mgr.test.tsx b/src/client/web/src/components/__test__/state_mgr.test.tsx new file mode 100644 index 0000000..83fb6af --- /dev/null +++ b/src/client/web/src/components/__test__/state_mgr.test.tsx @@ -0,0 +1,73 @@ +import { List, Set, Map } from "immutable"; +import { mock, instance } from "ts-mockito"; + +import { StateMgr } from "../state_mgr"; +import { User } from "../../client"; +import { MockFilesClient, resps as filesResps } from "../../client/files_mock"; +import { MockUsersClient, resps as usersResps } from "../../client/users_mock"; +import { ICoreState, newWithWorker } from "../core_state"; +import { MockWorker } from "../../worker/interface"; + +describe("State Manager", () => { + test("initUpdater", async () => { + const usersCl = new MockUsersClient(""); + const filesCl = new MockFilesClient(""); + + const mockWorkerClass = mock(MockWorker); + const mockWorker = instance(mockWorkerClass); + + const mgr = new StateMgr({}); // it will call initUpdater + mgr.setUsersClient(usersCl); + mgr.setFilesClient(filesCl); + + // TODO: depress warning + mgr.update = (apply: (prevState: ICoreState) => ICoreState): void => { + // no op + }; + + const coreState = newWithWorker(mockWorker); + await mgr.initUpdater(coreState); + + // browser + expect(coreState.browser.dirPath.join("/")).toEqual("mock_home/files"); + expect(coreState.browser.isSharing).toEqual(true); + expect(coreState.browser.sharings).toEqual( + List(filesResps.listSharingsMockResp.data.sharingDirs) + ); + expect(coreState.browser.uploadings).toEqual( + List(filesResps.listUploadingsMockResp.data.uploadInfos) + ); + expect(coreState.browser.items).toEqual( + List(filesResps.listHomeMockResp.data.metadatas) + ); + + // panes + expect(coreState.panes).toEqual({ + userRole: "admin", + displaying: "browser", + paneNames: Set(["settings", "login", "admin"]), + }); + + // login + expect(coreState.login).toEqual({ + authed: false, + captchaID: "mockCaptchaID", + }); + + // admin + let usersMap = Map({}); + usersResps.listUsersMockResp.data.users.forEach((user: User) => { + usersMap = usersMap.set(user.name, user); + }); + let roles = Set(); + Object.keys(usersResps.listRolesMockResp.data.roles).forEach( + (role: string) => { + roles = roles.add(role); + } + ); + expect(coreState.admin).toEqual({ + users: usersMap, + roles: roles, + }); + }); +}); diff --git a/src/client/web/src/components/browser.tsx b/src/client/web/src/components/browser.tsx index e1e0cfa..aba5309 100644 --- a/src/client/web/src/components/browser.tsx +++ b/src/client/web/src/components/browser.tsx @@ -6,10 +6,7 @@ import FileSize from "filesize"; import { alertMsg, comfirmMsg } from "../common/env"; import { updater } from "./state_updater"; import { ICoreState } from "./core_state"; -import { - MetadataResp, - UploadInfo, -} from "../client"; +import { MetadataResp, UploadInfo } from "../client"; import { Up } from "../worker/upload_mgr"; import { UploadEntry } from "../worker/interface"; @@ -181,12 +178,12 @@ export class Browser extends React.Component { this.chdir(this.props.dirPath.push(childDirName)); }; - chdir = (dirPath: List) => { + chdir = async (dirPath: List) => { if (dirPath === this.props.dirPath) { return; } - updater() + return updater() .setItems(dirPath) .then(() => { return updater().listSharings(); @@ -238,8 +235,8 @@ export class Browser extends React.Component { }); }; - addSharing = () => { - updater() + addSharing = async () => { + return updater() .addSharing() .then((ok) => { if (!ok) { @@ -254,8 +251,8 @@ export class Browser extends React.Component { }); }; - deleteSharing = (dirPath: string) => { - updater() + deleteSharing = async (dirPath: string) => { + return updater() .deleteSharing(dirPath) .then((ok) => { if (!ok) { @@ -270,8 +267,8 @@ export class Browser extends React.Component { }); }; - listSharings = () => { - updater() + listSharings = async () => { + return updater() .listSharings() .then((ok) => { if (ok) { diff --git a/src/client/web/src/components/core_state.ts b/src/client/web/src/components/core_state.ts index 0b2e853..55a0dd7 100644 --- a/src/client/web/src/components/core_state.ts +++ b/src/client/web/src/components/core_state.ts @@ -30,7 +30,7 @@ export function newWithWorker(worker: IWorker): ICoreState { } export function newState(): ICoreState { - const worker = Worker == null ? new FgWorker() : new BgWorker(); + const worker = window.Worker == null ? new FgWorker() : new BgWorker(); initUploadMgr(worker); return initState(); diff --git a/src/client/web/src/components/pane_login.tsx b/src/client/web/src/components/pane_login.tsx index 52acaa5..23a39b7 100644 --- a/src/client/web/src/components/pane_login.tsx +++ b/src/client/web/src/components/pane_login.tsx @@ -50,8 +50,8 @@ export class AuthPane extends React.Component { }); }; - login = () => { - updater() + login = async () => { + return updater() .login( this.state.user, this.state.pwd, @@ -72,6 +72,7 @@ export class AuthPane extends React.Component { this.setState({ user: "", pwd: "" }); alert("Failed to login."); } + }) .then(() => { return updater().refreshUploadings(); @@ -84,6 +85,23 @@ export class AuthPane extends React.Component { .then(() => { return updater().listSharings(); }) + .then(() => { + return updater().self(); + }) + .then(() => { + // TODO: should rely on props to get userRole + if (updater().props.panes.userRole === "admin") { + // TODO: remove hardcode + return updater().listRoles(); + } + }) + .then(() => { + // TODO: should rely on props to get userRole + if (updater().props.panes.userRole === "admin") { + // TODO: remove hardcode + return updater().listUsers(); + } + }) .then((_: boolean) => { this.update(updater().updateBrowser); }); diff --git a/src/client/web/src/components/state_mgr.tsx b/src/client/web/src/components/state_mgr.tsx index c69eef7..628bd77 100644 --- a/src/client/web/src/components/state_mgr.tsx +++ b/src/client/web/src/components/state_mgr.tsx @@ -18,7 +18,6 @@ export class StateMgr extends React.Component { constructor(p: Props) { super(p); this.state = newState(); - this.initUpdater(this.state); } setUsersClient = (client: IUsersClient) => { @@ -29,12 +28,16 @@ export class StateMgr extends React.Component { this.filesClient = client; }; - initUpdater = (state: ICoreState) => { + initUpdater = (state: ICoreState): Promise => { updater().init(state); - updater().setClients(new UsersClient(""), new FilesClient("")); + if (this.usersClient == null || this.filesClient == null) { + console.error("updater's clients are not inited"); + return; + } + updater().setClients(this.usersClient, this.filesClient); const params = new URLSearchParams(document.location.search.substring(1)); - updater() + return updater() .getCaptchaID() .then((ok: boolean) => { if (!ok) { @@ -42,10 +45,10 @@ export class StateMgr extends React.Component { } else { this.update(updater().updateLogin); } - }); - - updater() - .refreshUploadings() + }) + .then(() => { + return updater().refreshUploadings(); + }) .then(() => { const dir = params.get("dir"); if (dir != null && dir !== "") { @@ -84,6 +87,7 @@ export class StateMgr extends React.Component { }) .then(() => { this.update(updater().updatePanes); + this.update(updater().updateAdmin); }); }; diff --git a/src/client/web/src/worker/__test__/uploader.test.tsx b/src/client/web/src/worker/__test__/uploader.test.tsx deleted file mode 100644 index b122fd1..0000000 --- a/src/client/web/src/worker/__test__/uploader.test.tsx +++ /dev/null @@ -1,152 +0,0 @@ -import { FileUploader } from "../uploader"; -import { FilesClient } from "../../client/files_mock"; -import { makePromise } from "../../test/helpers"; - -describe("Uploader", () => { - const content = ["123456"]; - const filePath = "mock/file"; - const blob = new Blob(content); - const fileSize = blob.size; - const file = new File(content, filePath); - - const makeCreateResp = (status: number): Promise => { - return makePromise({ - status: status, - statusText: "", - data: { - path: filePath, - fileSize: fileSize, - }, - }); - }; - - const makeStatusResp = (status: number, uploaded: number): Promise => { - return makePromise({ - status: status, - statusText: "", - data: { - path: filePath, - isDir: false, - fileSize: fileSize, - uploaded: uploaded, - }, - }); - }; - - interface statusResp { - status: number; - uploaded: number; - } - - interface TestCase { - createResps: Array; - uploadChunkResps: Array; - uploadStatusResps: Array; - result: boolean; - } - - test("test start and upload method", async () => { - const testCases: Array = [ - { - // fail to create file 4 times - createResps: [500, 500, 500, 500], - uploadChunkResps: [], - uploadStatusResps: [], - result: false, - }, - { - // fail to get status - createResps: [200], - uploadChunkResps: [{ status: 500, uploaded: 0 }], - uploadStatusResps: [{ status: 600, uploaded: 0 }], - result: false, - }, - { - // upload ok - createResps: [200], - uploadChunkResps: [ - { status: 200, uploaded: 0 }, - { status: 200, uploaded: 1 }, - { status: 200, uploaded: fileSize }, - ], - uploadStatusResps: [], - result: true, - }, - { - // fail once - createResps: [200], - uploadChunkResps: [ - { status: 200, uploaded: 0 }, - { status: 500, uploaded: 1 }, - { status: 200, uploaded: fileSize }, - ], - uploadStatusResps: [{ status: 200, uploaded: 1 }], - result: true, - }, - { - // fail twice - createResps: [500, 500], - uploadChunkResps: [], - uploadStatusResps: [], - result: false, - }, - { - // fail twice - createResps: [500, 200], - uploadChunkResps: [ - { status: 200, uploaded: 0 }, - { status: 500, uploaded: 1 }, - { status: 500, uploaded: 1 }, - { status: 200, uploaded: fileSize }, - ], - uploadStatusResps: [ - { status: 500, uploaded: 1 }, - { status: 500, uploaded: 1 }, - ], - result: true, - }, - { - // other errors - createResps: [500, 200], - uploadChunkResps: [ - { status: 601, uploaded: 0 }, - { status: 408, uploaded: fileSize }, - { status: 200, uploaded: 1 }, - { status: 200, uploaded: fileSize }, - ], - uploadStatusResps: [ - { status: 500, uploaded: 1 }, - { status: 500, uploaded: 1 }, - ], - result: true, - }, - ]; - - for (let i = 0; i < testCases.length; i++) { - const tc = testCases[i]; - const mockCb = ( - filePath: string, - uploaded: number, - done: boolean, - err: string - ):void => {}; - const uploader = new FileUploader(file, filePath, mockCb); - const mockClient = new FilesClient(""); - - const createResps = tc.createResps.map((resp) => makeCreateResp(resp)); - mockClient.createMock(createResps); - const uploadChunkResps = tc.uploadChunkResps.map((resp) => - makeStatusResp(resp.status, resp.uploaded) - ); - mockClient.uploadChunkMock(uploadChunkResps); - const uploadStatusResps = tc.uploadStatusResps.map((resp) => - makeStatusResp(resp.status, resp.uploaded) - ); - mockClient.uploadStatusMock(uploadStatusResps); - uploader.setClient(mockClient); - - const ret = await uploader.start(); - expect(ret).toEqual(tc.result); - } - }); -}); diff --git a/src/userstore/user_store.go b/src/userstore/user_store.go index 31909e9..bb62b19 100644 --- a/src/userstore/user_store.go +++ b/src/userstore/user_store.go @@ -34,7 +34,7 @@ func IsReachedLimitErr(err error) bool { } type Quota struct { - SpaceLimit int64 `json:"spaceLimit,space"` + SpaceLimit int64 `json:"spaceLimit,string"` UploadSpeedLimit int `json:"uploadSpeedLimit"` DownloadSpeedLimit int `json:"downloadSpeedLimit"` }