test(单元测试): 初始化测试用例

This commit is contained in:
ivinwu
2021-01-05 10:39:53 +08:00
parent 003ac7d322
commit d8fd05a871
24 changed files with 705 additions and 31 deletions

1
.gitignore vendored
View File

@@ -2,3 +2,4 @@
.DS_Store .DS_Store
node_modules/ node_modules/
package-lock.json package-lock.json
coverage

3
babel.config.js Normal file
View File

@@ -0,0 +1,3 @@
module.exports = {
presets: [["@babel/preset-env", { targets: { node: "current" } }],'@babel/preset-typescript',],
};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -27,12 +27,19 @@
"scripts": { "scripts": {
"build": "webpack --display-error-details", "build": "webpack --display-error-details",
"commit": "git cz", "commit": "git cz",
"pub": "npm publish" "pub": "npm publish",
"test": "jest test"
}, },
"dependencies": {}, "dependencies": {},
"devDependencies": { "devDependencies": {
"@babel/core": "^7.12.9",
"@babel/preset-env": "^7.12.7",
"@babel/preset-typescript": "^7.12.7",
"@types/jest": "^26.0.16",
"babel-jest": "^26.6.3",
"commitizen": "^3.0.5", "commitizen": "^3.0.5",
"cz-conventional-changelog": "^2.1.0", "cz-conventional-changelog": "^2.1.0",
"jest": "^26.6.3",
"prettier": "^1.15.3", "prettier": "^1.15.3",
"pretty-quick": "^1.8.0", "pretty-quick": "^1.8.0",
"ts-loader": "^5.3.1", "ts-loader": "^5.3.1",
@@ -40,7 +47,7 @@
"tslint-config-prettier": "^1.17.0", "tslint-config-prettier": "^1.17.0",
"typescript": "^3.7.4", "typescript": "^3.7.4",
"webpack": "^4.28.0", "webpack": "^4.28.0",
"webpack-cli": "^3.3.10" "webpack-cli": "^3.3.12"
}, },
"husky": { "husky": {
"hooks": { "hooks": {
@@ -51,5 +58,16 @@
"commitizen": { "commitizen": {
"path": "./node_modules/cz-conventional-changelog" "path": "./node_modules/cz-conventional-changelog"
} }
},
"jest": {
"setupFiles": [
"./test/__mock__/wx.ts"
],
"collectCoverage": true,
"collectCoverageFrom": [
"src/**/*.ts",
"!src/*.ts",
"!src/store/**/*.ts"
]
} }
} }

View File

@@ -4,14 +4,11 @@ import { IInitOption } from '../interface'
export default (params: IInitOption) => { export default (params: IInitOption) => {
Object.assign(config, params); Object.assign(config, params);
console.log(config.errorTitle);
try { try {
status.session = wx.getStorageSync(config.sessionName!) || ''; status.session = wx.getStorageSync(config.sessionName!) || '';
} catch (e) { } catch (e) {}
console.error('wx.getStorageSync:fail, can not get session.')
}
try { try {
status.sessionExpire = wx.getStorageSync(config.sessionExpireKey || "sessionExpireKey") || Infinity; status.sessionExpire = wx.getStorageSync(config.sessionExpireKey || "sessionExpireKey") || Infinity;
} catch (e) { } catch (e) {}
console.error('wx.getStorageSync:fail, can not get sessionExpire.')
}
} }

View File

@@ -24,8 +24,6 @@ function logicError(obj: IRequestOption | IUploadFileOption, res: wx.RequestSucc
if (typeof config.errorCallback === "function") { if (typeof config.errorCallback === "function") {
config.errorCallback(obj, res); config.errorCallback(obj, res);
} }
console.error(res);
} }
function getErrorMsg(res: wx.RequestSuccessCallbackResult | wx.UploadFileSuccessCallbackResult) { function getErrorMsg(res: wx.RequestSuccessCallbackResult | wx.UploadFileSuccessCallbackResult) {

View File

@@ -6,7 +6,6 @@ function get(obj: IRequestOption | IUploadFileOption): any {
if(!(config.mockJson[obj.url] || (obj.originUrl && config.mockJson[obj.originUrl]))) { if(!(config.mockJson[obj.url] || (obj.originUrl && config.mockJson[obj.originUrl]))) {
// mock 没有对应接口的数据 // mock 没有对应接口的数据
console.error('mock 没有对应接口的数据');
return false; return false;
} }

View File

@@ -43,9 +43,7 @@ function responseForRequest(
} else { } else {
realData = res.data; realData = res.data;
} }
} catch (e) { } catch (e) {}
console.error("Function successData occur error: " + e);
}
if (!obj.noCacheFlash) { if (!obj.noCacheFlash) {
// 如果为了保证页面不闪烁,则不回调,只是缓存最新数据,待下次进入再用 // 如果为了保证页面不闪烁,则不回调,只是缓存最新数据,待下次进入再用
if (typeof obj.success === "function") { if (typeof obj.success === "function") {
@@ -102,9 +100,7 @@ function responseForUploadFile(
} else { } else {
realData = res.data; realData = res.data;
} }
} catch (e) { } catch (e) {}
console.error("Function successData occur error: " + e);
}
if (typeof obj.success === "function") { if (typeof obj.success === "function") {
obj.success(realData); obj.success(realData);

View File

@@ -13,7 +13,6 @@ let checkSessionPromise: any = null;
function checkSession() { function checkSession() {
if (!checkSessionPromise) { if (!checkSessionPromise) {
checkSessionPromise = new Promise((resolve, reject) => { checkSessionPromise = new Promise((resolve, reject) => {
console.log("wx.checkSession()");
const start = new Date().getTime(); const start = new Date().getTime();
wx.checkSession({ wx.checkSession({
success() { success() {
@@ -91,7 +90,6 @@ function doLogin() {
function login() { function login() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
console.log('wx.login');
const start = new Date().getTime(); const start = new Date().getTime();
wx.login({ wx.login({
success(res) { success(res) {

167
test/__mock__/wx.ts Normal file
View File

@@ -0,0 +1,167 @@
declare namespace NodeJS {
interface Global {
wx: Object;
}
}
const wx = {
// mock 状态
__mock__: {
// 当前toast是否展示
toastIsShow: false,
// 当前modal是否展示
modalIsShow: false,
// storage 内容
storage: {},
},
setStorage: jest.fn((options) => {
if (options.key && options.data) {
wx.__mock__.storage[options.key] = options.data;
}
}),
getStorageSync: jest.fn((options) => wx.__mock__.storage[options]),
getStorage: jest.fn((options) => {
if (options.key && options.success && wx.__mock__.storage[options.key]) {
return options.success({ data: wx.__mock__.storage[options.key] });
}
}),
removeStorage: jest.fn((options) => {
if (options.key) {
delete wx.__mock__.storage[options.key];
}
}),
showModal: jest.fn(() => {
wx.__mock__.modalIsShow = true;
}),
showToast: jest.fn(() => {
wx.__mock__.toastIsShow = true;
}),
hideToast: jest.fn(() => {
wx.__mock__.toastIsShow = false;
}),
login: jest.fn((obj) => {
if (typeof obj.success === "function") {
obj.success({ code: "js_code_xxxxxxxx" });
}
if (typeof obj.complete === "function") {
obj.complete();
}
}),
checkSession: jest.fn((obj) => {
if (typeof obj.fail === "function") {
obj.fail();
}
}),
request: jest.fn((obj) => {
return new Promise((resolve, reject) => {
process.nextTick(() => {
const url = new URL(obj.url);
const response = httpResponse[url.pathname];
if (response && response.hasOwnProperty("statusCode")) {
if (typeof obj.success === "function") {
obj.success(response);
}
resolve(response);
} else {
if (typeof obj.fail === "function") {
obj.fail(response);
}
reject(response);
}
if (typeof obj.complete === "function") {
obj.complete();
}
});
});
}),
uploadFile: jest.fn((obj) => {
return new Promise((resolve, reject) => {
process.nextTick(() => {
const url = new URL(obj.url);
const response = httpResponse[url.pathname];
if (response.hasOwnProperty("statusCode")) {
if (typeof obj.success === "function") {
obj.success(response);
}
resolve(response);
} else {
if (typeof obj.fail === "function") {
obj.fail(response);
}
reject(response);
}
if (typeof obj.complete === "function") {
obj.complete();
}
});
});
}),
};
/** 根据不同的请求pathnamemock返回的内容 */
const httpResponse = {
"/success": {
statusCode: 200,
errMsg: "request:ok",
header: {},
data: {
errcode: 0,
data: {
isSuccess: true,
},
},
},
"/successButNotJson": {
statusCode: 200,
errMsg: "request:ok",
header: {},
data: `{
"errcode": 0,
"data": {
"isSuccess": true
}
}`,
},
"/sessionExpired": {
statusCode: 200,
errMsg: "request:ok",
header: {},
data: {
errcode: -1,
data: {
},
},
},
"/cgiError": {
statusCode: 200,
errMsg: "request:ok",
header: {},
data: {
errcode: -100,
msg: "业务错误提示",
data: {},
},
},
"/code2Session": {
statusCode: 200,
errMsg: "request:ok",
header: {},
data: {
errcode: 0,
data: {
sid: "sid",
},
},
},
"/httpError": {
statusCode: 500,
errMsg: "request:ok",
header: {},
data: "",
},
"/networkError": {
errMsg: "request:fail Failed to execute 'send' on 'XMLHttpRequest'",
},
};
global.wx = wx;

View File

@@ -0,0 +1,13 @@
import getConfig from "../../src/api/getConfig";
import config from "../../src/store/config";
import status from "../../src/store/status";
describe("getConfig", () => {
test("return the correct config", () => {
const defaultConfig = getConfig();
expect(defaultConfig.urlPerfix).toBe(config.urlPerfix);
expect(defaultConfig.sessionExpireKey).toBe(config.sessionExpireKey);
expect(defaultConfig.sessionExpireTime).toBe(config.sessionExpireTime);
expect(defaultConfig.sessionExpire).toBe(status.sessionExpire);
});
});

View File

@@ -0,0 +1,8 @@
import getSession from "../../src/api/getSession";
describe("get Session", () => {
test("session default value is empty", () => {
const session = getSession();
expect(session).toBe("");
});
});

83
test/api/init.ts Normal file
View File

@@ -0,0 +1,83 @@
import init from "../../src/api/init";
const initOption = {
codeToSession: {
url: 'https://sample.com/code2Session',
codeName: 'js_code',
report: "codeToSession",
success: () =>{
return 'xxxx'
}
},
errorTitle: (res) => {
const { msg } = res
return `${msg || '服务可能存在异常,请稍后重试'}`
},
errorContent: (res) => {
const { msg } = res
return `${msg || '服务可能存在异常,请稍后重试'}`
},
sessionName: 'sid',
loginTrigger: (res) => {
return res.errcode === -1
},
successTrigger: res => res.errcode === 0,
successData: res => res.data,
setHeader: () => {return {header: 1}},
sessionExpireTime: 3000,
mockJson: {
"https://sample.com/mock": {
errcode: 0,
data: {
mock: true
}
}
},
globalData: {
version: '0.0.1'
},
errorCallback(){},
reportCGI(){}
}
const initOptionWithSpecificErrorConfig = {
codeToSession: {
url: 'https://sample.com/code2Session',
codeName: 'js_code',
report: "codeToSession",
success: () =>{
return 'xxxx'
}
},
errorTitle: "服务可能存在异常,请稍后重试",
errorContent: "服务可能存在异常,请稍后重试",
sessionName: 'sid',
loginTrigger: (res) => {
return res.errcode === -1
},
successTrigger: res => res.errcode === 0,
successData: res => res.data,
setHeader: () => {return {header: 1}},
sessionExpireTime: 3000,
mockJson: {
"https://sample.com/mock": {
errcode: 0,
data: {
mock: true
}
}
},
globalData: {
version: '0.0.1'
},
errorCallback(){},
reportCGI(){}
}
export function initail() {
init(initOption)
}
export function initailWithSpecificErrorTitle() {
init(initOptionWithSpecificErrorConfig)
}

14
test/api/login.test.ts Normal file
View File

@@ -0,0 +1,14 @@
import login from "../../src/api/login";
import { initail } from "./init";
beforeAll(()=>{
// 以下所有测试用例执行前,需提前执行初始化
initail()
})
describe("login", () =>{
test("call login", async () => {
await login();
expect(wx.__mock__.storage['sid']).toBeTruthy();
})
})

180
test/api/request.test.ts Normal file
View File

@@ -0,0 +1,180 @@
import request from "../../src/api/request";
import config from "../../src/store/config";
import { initail } from "./init";
beforeAll(() => {
// 以下所有测试用例执行前,需提前执行初始化
initail();
});
describe("normal request", () => {
test("send a success request in callback way with duration report", (done) => {
expect.assertions(1);
const normalRequest = {
url: "https://sample.com/success",
showLoading: true,
report: "reportkey",
success: (res) => {
expect(res.isSuccess).toBe(true);
done();
},
};
request(normalRequest);
});
test("send a cgi error request in callback way", (done) => {
expect.assertions(1);
const cgiErrorRequest = {
url: "https://sample.com/cgiError",
fail: (res) => {
expect(res.data.errcode).toBe(-100);
done();
},
};
request(cgiErrorRequest);
});
test("send a http error request in callback way", (done) => {
expect.assertions(1);
const httpErrorRequest = {
url: "https://sample.com/httpError",
fail: (res) => {
expect(res.statusCode).toBe(500);
done();
},
};
request(httpErrorRequest);
});
test("send a success request and response is not json", (done) => {
expect.assertions(1);
const notJsonRequest = {
url: "https://sample.com/successButNotJson",
success: (res) => {
expect(res.isSuccess).toBe(true);
done();
},
};
request(notJsonRequest);
});
test("send a success request with checkSession", (done) => {
config.doNotCheckSession = false;
expect.assertions(1);
const normalRequest = {
url: "https://sample.com/success",
success: (res) => {
expect(res.isSuccess).toBe(true);
done();
},
};
request(normalRequest);
})
// test("send a request when session is expired", (done) => {
// expect.assertions(1);
// const expiredRequest = {
// url: "https://sample.com/sessionExpired",
// success: (res) => {
// expect(res.isSuccess).toBe(true);
// done();
// },
// };
// request(expiredRequest);
// })
// test("send a network error request in callback way without fail definition", async () => {
// expect.assertions(1);
// wx.__mock__.modalIsShow = false;
// const cgiErrorRequest = {
// url: "https://sample.com/cgiError",
// success(){}
// };
// await request(cgiErrorRequest);
// expect(wx.__mock__.modalIsShow).toBe(true);
// done();
// });
test("send a network error request in callback way", (done) => {
expect.assertions(1);
const networkErrorRequest = {
url: "https://sample.com/networkError",
fail: (res) => {
expect(res.errMsg).toContain("request:fail");
done();
},
};
request(networkErrorRequest)
});
});
describe("mock request", () => {
test("try to get mock data", (done) => {
expect.assertions(1);
const mockRequest = {
url: "https://sample.com/mock",
success: (res) => {
expect(res.mock).toBe(true);
done();
},
};
request(mockRequest);
});
});
describe("cache request", () => {
test("send a success request in cache way", (done) => {
expect.assertions(1);
const mockRequest = {
url: "https://sample.com/success",
cache: true,
success: (res) => {
expect(res.isSuccess).toBe(true);
done();
},
};
request(mockRequest);
});
test("check response set in storage successfully", () => {
expect(wx.__mock__.storage["https://sample.com/success"]).not.toBeFalsy();
});
test("send a success request while already has cached", async (done) => {
expect.assertions(2);
const mockRequest = {
url: "https://sample.com/success",
cache: true,
success: (res, cache) => {
if (cache) {
expect(res.isSuccess).toBe(true);
expect(cache.isCache).toBe(true);
done();
}
},
};
request(mockRequest);
});
});
describe("request with promise way", () => {
test("send a http error request in promise way", async () => {
expect.assertions(1);
const httpErrorRequest = {
url: "https://sample.com/httpError",
catchError: true,
};
await request(httpErrorRequest).catch((error) => {
expect(error).toEqual(new Error("500"));
});
});
test("send a cgi error request in promise way", async () => {
expect.assertions(1);
const cgiErrorRequest = {
url: "https://sample.com/cgiError",
catchError: true,
};
await expect(request(cgiErrorRequest)).rejects.toThrow();
});
});

View File

@@ -0,0 +1,11 @@
import setSession from "../../src/api/setSession";
import getSession from "../../src/api/getSession";
describe("set Session", () => {
test("session value is correct", () => {
const someSession = 'sample';
setSession(someSession);
const session = getSession();
expect(session).toBe(someSession);
});
});

View File

@@ -0,0 +1,88 @@
import uploadFile from "../../src/api/uploadFile";
import { initailWithSpecificErrorTitle } from "./init";
beforeAll(()=>{
// 以下所有测试用例执行前,需提前执行初始化
initailWithSpecificErrorTitle();
})
describe("uploadFile", () =>{
test("send a normal uploadFile in callback way", (done) => {
expect.assertions(1);
const normalUploadFile = {
url: 'https://sample.com/success',
filePath: 'test.png',
name: 'test',
success: (res) => {
expect(res.isSuccess).toBe(true);
done();
}
}
uploadFile(normalUploadFile);
})
test("send a cgi error uploadFile in callback way", (done) => {
expect.assertions(1);
const cgiErrorRequest = {
url: "https://sample.com/cgiError",
filePath: 'test.png',
name: 'test',
fail: (res) => {
expect(res.data.errcode).toBe(-100);
done();
},
};
uploadFile(cgiErrorRequest);
});
test("send a success uploadFile and response is not json", (done) => {
expect.assertions(1);
const notJsonRequest = {
url: "https://sample.com/successButNotJson",
success: (res) => {
expect(res.isSuccess).toBe(true);
done();
},
};
uploadFile(notJsonRequest);
});
})
describe("mock uploadFile", () => {
test("try to get mock data", (done) => {
expect.assertions(1);
const mockRequest = {
url: "https://sample.com/mock",
filePath: 'test.png',
name: 'test',
success: (res) => {
expect(res.mock).toBe(true);
done();
},
};
uploadFile(mockRequest);
});
});
describe("uploadFile with promise way", () => {
test("send a http error uploadFile in promise way", async () => {
expect.assertions(1);
const httpErrorRequest = {
url: "https://sample.com/httpError",
catchError: true,
};
await uploadFile(httpErrorRequest).catch((error) => {
expect(error).toEqual(new Error("500"));
});
});
test("send a cgi error uploadFile in promise way", async () => {
expect.assertions(1);
const cgiErrorRequest = {
url: "https://sample.com/cgiError",
catchError: true,
};
await expect(uploadFile(cgiErrorRequest)).rejects.toThrow();
});
});

View File

@@ -0,0 +1,9 @@
import errorHandler from "../../src/module/errorHandler";
describe("show error modal", () => {
test("doError", () => {
wx.__mock__.modalIsShow = false;
errorHandler.doError("标题", "内容");
expect(wx.__mock__.modalIsShow).toBe(true);
})
})

View File

@@ -0,0 +1,10 @@
import mockManager from "../../src/module/mockManager";
describe("mock", () =>{
test("try to get mock data when no config", () => {
const data = mockManager.get({
originUrl: 'success'
})
expect(data).toBe(false)
})
})

View File

@@ -0,0 +1,29 @@
import sessionManager from "../../src/module/sessionManager";
import setSession from "../../src/api/setSession";
import getSession from "../../src/api/getSession";
import config from '../../src/store/config'
describe("delete session", () =>{
test("delete session", () => {
const someValue = "sample";
// 先手动设置一个session
setSession(someValue);
// 执行用例调用删除session接口
sessionManager.delSession();
// 验证session是否被正确删除
const session = getSession();
expect(session).toBeFalsy;
});
})
describe("set session", () => {
test("set session without expire time", () => {
const someValue = "sample";
// 执行用例
sessionManager.setSession(someValue);
// 验证session是否正确
const session = getSession();
expect(session).toBe(wx.__mock__.storage[config.sessionName])
expect(session).toBe(someValue);
})
})

View File

@@ -0,0 +1,14 @@
import replace from "../../src/util/jsonSuperset";
describe.each([
["\u000A"],
["\u000D"],
["\u2028"],
["\u2029"]
])("replace string", (char) => {
test(`return string without ${char}`, () => {
let string = `${char}sam${char}ple`;
string = replace(string)
expect(string).toBe("sample");
});
})

16
test/util/loading.test.ts Normal file
View File

@@ -0,0 +1,16 @@
import loading from "../../src/util/loading";
describe("loading", () => {
test("the toast is show when show is call with txt", () => {
loading.show("加载中");
expect(wx.__mock__.toastIsShow).toBe(true);
});
test("the toast is show when show is call with bool", () => {
loading.show(true);
expect(wx.__mock__.toastIsShow).toBe(true);
});
test("the toast is hide when hide is call", () => {
loading.hide();
expect(wx.__mock__.toastIsShow).toBe(false);
});
});

15
test/util/url.test.ts Normal file
View File

@@ -0,0 +1,15 @@
import url from "../../src/util/url";
describe("setParams", () => {
test("return url with correct querystring when the input has no querystring", () => {
let sample = "https://www.example.com";
sample = url.setParams(sample, { test: 1 });
expect(sample).toBe("https://www.example.com?test=1");
});
test("return url with correct querystring when the input already has querystring", () => {
let sample = "https://www.example.com?test=1";
sample = url.setParams(sample, { test2: 2 });
expect(sample).toBe("https://www.example.com?test=1&test2=2");
});
});