From e40878f7be107b7379875031b0f39d94d841361b Mon Sep 17 00:00:00 2001 From: Hexxa Date: Sun, 20 Dec 2020 12:03:33 +0800 Subject: [PATCH] refine uploader, components and their tests (#23) * feat(uploader, auth_pane): refine uploader and add tests * chore(package.json): remove unused deps * test(uploader, components): add tests for uploader and components --- public/index.html | 2 +- src/client/web/jest.setup.js | 1 + src/client/web/package.json | 3 + .../web/src/client/__test__/uploader.test.tsx | 137 +++++++++++++ src/client/web/src/client/files_mock.ts | 120 +++++++----- src/client/web/src/client/index.ts | 9 +- src/client/web/src/client/uploader.ts | 73 ++++--- src/client/web/src/client/users_mock.ts | 51 +++-- .../components/__test__/auth_pane.test.tsx | 52 +++-- .../src/components/__test__/browser.test.tsx | 182 +++++++++++++++++- src/client/web/src/components/auth_pane.tsx | 4 +- src/client/web/src/components/browser.tsx | 4 +- src/client/web/src/test/helpers.ts | 15 ++ 13 files changed, 526 insertions(+), 127 deletions(-) create mode 100644 src/client/web/jest.setup.js create mode 100644 src/client/web/src/client/__test__/uploader.test.tsx create mode 100644 src/client/web/src/test/helpers.ts diff --git a/public/index.html b/public/index.html index dec5e3d..076dc63 100644 --- a/public/index.html +++ b/public/index.html @@ -90,5 +90,5 @@
- + diff --git a/src/client/web/jest.setup.js b/src/client/web/jest.setup.js new file mode 100644 index 0000000..c73b59b --- /dev/null +++ b/src/client/web/jest.setup.js @@ -0,0 +1 @@ +jest.setTimeout(15000); diff --git a/src/client/web/package.json b/src/client/web/package.json index 0b78e32..b6f967e 100644 --- a/src/client/web/package.json +++ b/src/client/web/package.json @@ -77,6 +77,9 @@ "ts", "tsx", "js" + ], + "setupFilesAfterEnv": [ + "./jest.setup.js" ] }, "autoBump": {} diff --git a/src/client/web/src/client/__test__/uploader.test.tsx b/src/client/web/src/client/__test__/uploader.test.tsx new file mode 100644 index 0000000..70d01d9 --- /dev/null +++ b/src/client/web/src/client/__test__/uploader.test.tsx @@ -0,0 +1,137 @@ +import { FileUploader } from "../uploader"; +import { FilesClient } from "../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, 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 uploader = new FileUploader(file, filePath); + 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/client/web/src/client/files_mock.ts b/src/client/web/src/client/files_mock.ts index 853e23a..c717762 100644 --- a/src/client/web/src/client/files_mock.ts +++ b/src/client/web/src/client/files_mock.ts @@ -1,82 +1,102 @@ -import axios, { AxiosRequestConfig, AxiosResponse } from "axios"; - -import {MetadataResp, UploadStatusResp, ListResp} from "./"; +import { + Response, + UploadStatusResp, + ListResp, +} from "./"; export class FilesClient { private url: string; - private createMockResp: number; - private deleteMockResp: number; - private metadataMockResp: MetadataResp | null; - private mkdirMockResp: number | null; - private moveMockResp: number; - private uploadChunkMockResp: UploadStatusResp | null; - private uploadStatusMockResp: UploadStatusResp | null; - private listMockResp: ListResp | null; + + 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>; constructor(url: string) { this.url = url; } - createMock = (resp: number) => { - this.createMockResp = resp; - } - deleteMock = (resp: number) => { + createMock = (resps: Array>) => { + this.createMockResps = resps; + }; + + deleteMock = (resp: Promise) => { this.deleteMockResp = resp; - } - metadataMock = (resp: MetadataResp | null) => { + }; + + metadataMock = (resp: Promise) => { this.metadataMockResp = resp; - } - mkdirMock = (resp: number | null) => { + }; + + mkdirMock = (resp: Promise) => { this.mkdirMockResp = resp; - } - moveMock = (resp: number) => { + }; + + moveMock = (resp: Promise) => { this.moveMockResp = resp; - } - uploadChunkMock = (resp: UploadStatusResp | null) => { - this.uploadChunkMockResp = resp; - } + }; - uploadStatusMock = (resp: UploadStatusResp | null) => { - this.uploadStatusMockResp = resp; - } + uploadChunkMock = (resps: Array>>) => { + this.uploadChunkMockResps = resps; + }; - listMock = (resp: ListResp | null) => { + uploadStatusMock = (resps: Array>>) => { + this.uploadStatusMockResps = resps; + }; + + listMock = (resp: Promise>) => { this.listMockResp = resp; - } + }; - async create(filePath: string, fileSize: number): Promise { - return this.createMockResp; - } + 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}`); + }; - async delete(filePath: string): Promise { + delete = (filePath: string): Promise => { return this.deleteMockResp; - } + }; - async metadata(filePath: string): Promise { + metadata = (filePath: string): Promise => { return this.metadataMockResp; - } + }; - async mkdir(dirpath: string): Promise { + mkdir = (dirpath: string): Promise => { return this.mkdirMockResp; - } + }; - async move(oldPath: string, newPath: string): Promise { + move = (oldPath: string, newPath: string): Promise => { return this.moveMockResp; - } + }; - async uploadChunk( + uploadChunk = ( filePath: string, content: string | ArrayBuffer, offset: number - ): Promise { - return this.uploadChunkMockResp; - } + ): 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}`); + }; - async uploadStatus(filePath: string): Promise { - return this.uploadStatusMockResp; - } + 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}`); + }; - async list(dirPath: string): Promise { + list = (dirPath: string): Promise> => { return this.listMockResp; - } + }; } diff --git a/src/client/web/src/client/index.ts b/src/client/web/src/client/index.ts index ca0a8cc..7637cfd 100644 --- a/src/client/web/src/client/index.ts +++ b/src/client/web/src/client/index.ts @@ -84,7 +84,7 @@ export class BaseClient { setTimeout(() => { if (!returned) { src.cancel("request timeout"); - // resolve(TimeoutResp); + resolve(TimeoutResp); } }, defaultTimeout); @@ -95,11 +95,8 @@ export class BaseClient { }) .catch((e) => { const errMsg = e.toString(); - console.log(e); - - if (errMsg.includes("i/o timeput")) { - resolve(TimeoutResp); - } else if (errMsg.includes("ERR_EMPTY")) { + if (errMsg.includes("ERR_EMPTY")) { + // this means connection is eliminated by server because of timeout. resolve(EmptyBodyResp); } else { resolve(UnknownErrResp(errMsg)); diff --git a/src/client/web/src/client/uploader.ts b/src/client/web/src/client/uploader.ts index f0aa87d..8e125fb 100644 --- a/src/client/web/src/client/uploader.ts +++ b/src/client/web/src/client/uploader.ts @@ -1,26 +1,27 @@ -import { List } from "immutable"; - -import { Response, UnknownErrResp, UploadStatusResp } from "./"; +import { IFilesClient } from "../client"; import { FilesClient } from "../client/files"; +import { Response, UnknownErrResp, UploadStatusResp } from "./"; +// TODO: get settings from server const defaultChunkLen = 1024 * 1024 * 30; // 15MB/s const speedDownRatio = 0.5; const speedUpRatio = 1.1; +const retryLimit = 4; export class FileUploader { private reader = new FileReader(); - private client = new FilesClient(""); - private file: File; - private filePath: string; - private offset: number; + private client: IFilesClient = new FilesClient(""); private chunkLen: number = defaultChunkLen; - private progressCb: (filePath: string, progress: number) => void; + private file: File; + private offset: number; + private filePath: string; private errMsg: string | null; + private progressCb: (filePath: string, progress: number) => void; constructor( file: File, filePath: string, - progressCb: (filePath: string, progress: number) => void + progressCb?: (filePath: string, progress: number) => void ) { this.file = file; this.filePath = filePath; @@ -30,17 +31,41 @@ export class FileUploader { err = (): string | null => { return this.errMsg; - } + }; + + setClient = (client: IFilesClient) => { + this.client = client; + }; + + create = async (filePath: string, fileSize: number): Promise => { + return this.client.create(filePath, fileSize); + }; + + uploadChunk = async ( + filePath: string, + base64Chunk: string, + offset: number + ): Promise> => { + return this.client.uploadChunk(filePath, base64Chunk, offset); + }; + + uploadStatus = async ( + filePath: string + ): Promise> => { + return this.client.uploadStatus(filePath); + }; start = async (): Promise => { - const resp = await this.client.create(this.filePath, this.file.size); - switch (resp.status) { - case 200: + let resp: Response; + for (let i = 0; i < retryLimit; i++) { + resp = await this.create(this.filePath, this.file.size); + if (resp.status === 200) { return await this.upload(); - default: - this.errMsg = `failed to create ${this.filePath}: status=${resp.statusText}`; - return false; + } } + + this.errMsg = `failed to create ${this.filePath}: status=${resp.statusText}`; + return false; }; upload = async (): Promise => { @@ -49,7 +74,7 @@ export class FileUploader { this.offset >= 0 && this.offset < this.file.size ) { - let uploadPromise = new Promise>( + const uploadPromise = new Promise>( (resolve: (resp: Response) => void) => { this.reader.onerror = (ev: ProgressEvent) => { resolve(UnknownErrResp(this.reader.error.toString())); @@ -58,8 +83,7 @@ export class FileUploader { this.reader.onloadend = (ev: ProgressEvent) => { const dataURL = ev.target.result as string; // readAsDataURL const base64Chunk = dataURL.slice(dataURL.indexOf(",") + 1); - this.client - .uploadChunk(this.filePath, base64Chunk, this.offset) + this.uploadChunk(this.filePath, base64Chunk, this.offset) .then((resp: Response) => { resolve(resp); }) @@ -78,27 +102,28 @@ export class FileUploader { this.reader.readAsDataURL(blob); const uploadResp = await uploadPromise; + if (uploadResp.status === 200 && uploadResp.data != null) { this.offset = uploadResp.data.uploaded; this.chunkLen = Math.ceil(this.chunkLen * speedUpRatio); } else { this.errMsg = uploadResp.statusText; this.chunkLen = Math.ceil(this.chunkLen * speedDownRatio); - const uploadStatusResp = await this.client.uploadStatus(this.filePath); - - console.log(uploadStatusResp.status); + const uploadStatusResp = await this.uploadStatus(this.filePath); if (uploadStatusResp.status === 200) { this.offset = uploadStatusResp.data.uploaded; } else if (uploadStatusResp.status === 600) { this.errMsg = "unknown error"; - break + break; } else { // do nothing and retry } } - this.progressCb(this.filePath, Math.ceil(this.offset / this.file.size)); + if (this.progressCb != null) { + this.progressCb(this.filePath, Math.ceil(this.offset / this.file.size)); + } } if (this.chunkLen === 0) { diff --git a/src/client/web/src/client/users_mock.ts b/src/client/web/src/client/users_mock.ts index e475438..279ca49 100644 --- a/src/client/web/src/client/users_mock.ts +++ b/src/client/web/src/client/users_mock.ts @@ -1,45 +1,44 @@ -// import axios, { AxiosRequestConfig, AxiosResponse } from "axios"; - // TODO: replace this with jest mocks +import { Response } from "./"; + export class MockUsersClient { private url: string; - private loginResp: number; - private logoutResp: number; - private isAuthedResp: number; - private setPwdResp: number; + private loginMockResp: Promise; + private logoutMockResp: Promise; + private isAuthedMockResp: Promise; + private setPwdMockResp: Promise; constructor(url: string) { this.url = url; } - mockLoginResp(status: number) { - this.loginResp = status; + loginMock = (resp: Promise) => { + this.loginMockResp = resp; } - mocklogoutResp(status: number) { - this.logoutResp = status; + logoutMock = (resp: Promise) => { + this.logoutMockResp = resp; } - mockisAuthedResp(status: number) { - this.isAuthedResp = status; + isAuthedMock = (resp: Promise) => { + this.isAuthedMockResp = resp; } - mocksetPwdResp(status: number) { - this.setPwdResp = status; - } - - async login(user: string, pwd: string): Promise { - return this.loginResp + setPwdMock = (resp: Promise) => { + this.setPwdMockResp = resp; } - // token cookie is set by browser - async logout(): Promise { - return this.logoutResp + login = (user: string, pwd: string): Promise => { + return this.loginMockResp; } - async isAuthed(): Promise { - return this.isAuthedResp + logout = (): Promise => { + return this.logoutMockResp; } - // token cookie is set by browser - async setPwd(oldPwd: string, newPwd: string): Promise { - return this.setPwdResp + isAuthed = (): Promise => { + return this.isAuthedMockResp; } + + setPwd = (oldPwd: string, newPwd: string): Promise => { + return this.setPwdMockResp; + } + } diff --git a/src/client/web/src/components/__test__/auth_pane.test.tsx b/src/client/web/src/components/__test__/auth_pane.test.tsx index 02ffae0..8f94b1c 100644 --- a/src/client/web/src/components/__test__/auth_pane.test.tsx +++ b/src/client/web/src/components/__test__/auth_pane.test.tsx @@ -1,32 +1,56 @@ -import * as React from "react"; - import { init } from "../core_state"; import { Updater } from "../auth_pane"; import { MockUsersClient } from "../../client/users_mock"; +import { Response } from "../../client"; describe("AuthPane", () => { - test("Updater: initIsAuthed", () => { + const makePromise = (ret: any): Promise => { + return new Promise((resolve) => { + resolve(ret); + }); + }; + const makeNumberResponse = (status: number): Promise => { + return makePromise({ + status: status, + statusText: "", + data: {}, + }); + }; + + test("Updater-initIsAuthed", async () => { const tests = [ { loginStatus: 200, + logoutStatus: 200, + isAuthedStatus: 200, + setPwdStatus: 200, isAuthed: true, }, { - loginStatus: 500, + loginStatus: 200, + logoutStatus: 200, + isAuthedStatus: 500, + setPwdStatus: 200, isAuthed: false, }, ]; - const client = new MockUsersClient("foobarurl"); - Updater.setClient(client); - const coreState = init(); + const client = new MockUsersClient(""); + for (let i = 0; i < tests.length; i++) { + const tc = tests[i]; - tests.forEach(async (tc) => { - client.mockisAuthedResp(tc.loginStatus); - await Updater.initIsAuthed().then(() => { - const newState = Updater.setAuthPane(coreState); - expect(newState.panel.authPane.authed).toEqual(tc.isAuthed); - }); - }); + client.loginMock(makeNumberResponse(tc.loginStatus)); + client.logoutMock(makeNumberResponse(tc.logoutStatus)); + client.isAuthedMock(makeNumberResponse(tc.isAuthedStatus)); + client.setPwdMock(makeNumberResponse(tc.setPwdStatus)); + + const coreState = init(); + Updater.setClient(client); + Updater.init(coreState.panel.authPane); + await Updater.initIsAuthed(); + const newState = Updater.setAuthPane(coreState); + + expect(newState.panel.authPane.authed).toEqual(tc.isAuthed); + } }); }); diff --git a/src/client/web/src/components/__test__/browser.test.tsx b/src/client/web/src/components/__test__/browser.test.tsx index 0b360ba..5789bfa 100644 --- a/src/client/web/src/components/__test__/browser.test.tsx +++ b/src/client/web/src/components/__test__/browser.test.tsx @@ -1,10 +1,188 @@ -import * as React from "react"; +import { List, Map } from "immutable"; import { init } from "../core_state"; +import { makePromise, makeNumberResponse } from "../../test/helpers"; import { Updater } from "../browser"; import { MockUsersClient } from "../../client/users_mock"; +import { FilesClient } from "../../client/files_mock"; +import { MetadataResp } from "../../client"; describe("Browser", () => { - test("Updater: ", () => { + test("Updater: setPwd", 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", + }, + ]; + + const usersClient = new MockUsersClient(""); + const filesClient = new FilesClient(""); + for (let i = 0; i < tests.length; i++) { + const tc = tests[i]; + + filesClient.listMock(makePromise(tc.listResp)); + Updater.setClients(usersClient, filesClient); + + const coreState = init(); + Updater.init(coreState.panel.browser); + await Updater.setItems(List(tc.filePath.split("/"))); + const newState = Updater.setBrowser(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: 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", + }, + ]; + + const usersClient = new MockUsersClient(""); + const filesClient = new FilesClient(""); + for (let i = 0; i < tests.length; i++) { + const tc = tests[i]; + + filesClient.listMock(makePromise(tc.listResp)); + filesClient.deleteMock(makeNumberResponse(200)); + Updater.setClients(usersClient, filesClient); + + const coreState = init(); + Updater.init(coreState.panel.browser); + await Updater.delete( + List(tc.dirPath.split("/")), + List(tc.items), + Map(tc.selected) + ); + const newState = Updater.setBrowser(coreState); + + // 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); + }); + } + }); + + 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 FilesClient(""); + 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 = init(); + Updater.init(coreState.panel.browser); + await Updater.moveHere( + tc.dirPath1, + tc.dirPath2, + Map(tc.selected) + ); + + // TODO: check inputs of move + + const newState = Updater.setBrowser(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); + }); + } }); }); diff --git a/src/client/web/src/components/auth_pane.tsx b/src/client/web/src/components/auth_pane.tsx index 33a19e7..a8f12c4 100644 --- a/src/client/web/src/components/auth_pane.tsx +++ b/src/client/web/src/components/auth_pane.tsx @@ -95,6 +95,7 @@ export class AuthPane extends React.Component { this.update(Updater.setAuthPane); this.setState({ user: "", pwd: "" }); } else { + this.setState({ user: "", pwd: "" }); alert("Failed to login."); } }); @@ -104,9 +105,8 @@ export class AuthPane extends React.Component { Updater.logout().then((ok: boolean) => { if (ok) { this.update(Updater.setAuthPane); - this.setState({ user: "", pwd: "" }); } else { - alert("Failed to login."); + alert("Failed to logout."); } }); }; diff --git a/src/client/web/src/components/browser.tsx b/src/client/web/src/components/browser.tsx index 892e6bf..ba9e83b 100644 --- a/src/client/web/src/components/browser.tsx +++ b/src/client/web/src/components/browser.tsx @@ -18,6 +18,7 @@ export interface Item { isDir: boolean; selected: boolean; } + export interface Props { dirPath: List; items: List; @@ -51,7 +52,7 @@ export class Updater { Updater.props.dirPath = dirParts; Updater.props.items = - listResp != null + listResp.status === 200 ? List(listResp.data.metadatas) : Updater.props.items; }; @@ -98,7 +99,6 @@ export class Updater { async (itemName: string): Promise => { const oldPath = getItemPath(srcDir, itemName); const newPath = getItemPath(dstDir, itemName); - const resp = await Updater.filesClient.move(oldPath, newPath); return resp.status === 200 ? "" : itemName; } diff --git a/src/client/web/src/test/helpers.ts b/src/client/web/src/test/helpers.ts new file mode 100644 index 0000000..555eb97 --- /dev/null +++ b/src/client/web/src/test/helpers.ts @@ -0,0 +1,15 @@ +import { Response } from "../client"; + +export const makePromise = (ret: any): Promise => { + return new Promise((resolve) => { + resolve(ret); + }); +}; + +export const makeNumberResponse = (status: number): Promise => { + return makePromise({ + status: status, + statusText: "", + data: {}, + }); +};