Skip to content
This repository was archived by the owner on Jul 25, 2022. It is now read-only.

Commit 95a7b51

Browse files
committed
feat: Add tests
1 parent 3930838 commit 95a7b51

File tree

6 files changed

+234
-26
lines changed

6 files changed

+234
-26
lines changed

__mocks__/electron.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
export const app = {
2-
getPath: jest.fn(),
2+
getPath: jest.fn().mockReturnValue(process.env.HOME),
33
};

common/plugins/plugins.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import { SquidPlugin } from '@common/plugins/package/src';
22
import { isDev, isMainProcess } from '../utils/utils';
33
import { TriggerParams } from '@common/plugins/hooks';
44
import electron from 'electron';
5-
import path from 'path';
6-
import fs from 'fs';
5+
import * as path from 'path';
6+
import * as fs from 'fs';
77
import Config from '@common/config/Config';
88

99
const PLUGINS_FOLDER = isDev ?
+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import { baseUrl, makeAuthRequest, makeRequest, MethodType } from '@common/utils/request';
2+
3+
describe('Make a request', () => {
4+
5+
const getCallParams = (method: MethodType, body?: any) => {
6+
7+
return expect.objectContaining({
8+
method,
9+
body: body ? JSON.stringify(body) : body,
10+
headers: {
11+
'Accept': 'application/json',
12+
'Content-Type': 'application/json',
13+
},
14+
});
15+
};
16+
17+
it('should make a request without body', async() => {
18+
19+
const url = 'test';
20+
const method: MethodType = 'GET';
21+
22+
const expected = { success: true };
23+
const expectedUrl = `${baseUrl}/${url}`;
24+
25+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
26+
// @ts-ignore
27+
global.fetch = jest.fn().mockResolvedValueOnce({
28+
json: async() => expected,
29+
});
30+
31+
const resultRequest = await makeRequest(url, method);
32+
33+
expect(global.fetch).toHaveBeenCalledWith(
34+
expectedUrl,
35+
getCallParams(method),
36+
);
37+
expect(resultRequest).toEqual(expected);
38+
});
39+
40+
it('should make a request with body', async() => {
41+
42+
const url = 'test';
43+
const method: MethodType = 'POST';
44+
const body = { hello: 'world' };
45+
46+
const expected = { success: true };
47+
const expectedUrl = `${baseUrl}/${url}`;
48+
49+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
50+
// @ts-ignore
51+
global.fetch = jest.fn().mockResolvedValueOnce({
52+
json: async() => expected,
53+
});
54+
55+
const resultRequest = await makeRequest(url, method, body);
56+
57+
expect(global.fetch).toHaveBeenCalledWith(
58+
expectedUrl,
59+
getCallParams(method, body),
60+
);
61+
expect(resultRequest).toEqual(expected);
62+
});
63+
});
64+
65+
describe('Make an auth request', () => {
66+
67+
const fakeToken = 'faketoken';
68+
69+
const getCallParams = (method: MethodType, body?: any) => {
70+
71+
return expect.objectContaining({
72+
method,
73+
body: body ? JSON.stringify(body) : body,
74+
headers: {
75+
'Accept': 'application/json',
76+
'Content-Type': 'application/json',
77+
'Authorization': `Bearer ${fakeToken}`,
78+
},
79+
});
80+
};
81+
82+
it('should make an auth request without body', async() => {
83+
84+
const url = 'test';
85+
const method: MethodType = 'GET';
86+
87+
const expected = { success: true };
88+
const expectedUrl = `${baseUrl}/${url}`;
89+
90+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
91+
// @ts-ignore
92+
global.fetch = jest.fn().mockResolvedValueOnce({
93+
json: async() => expected,
94+
});
95+
96+
const resultRequest = await makeAuthRequest(url, method, fakeToken);
97+
98+
expect(global.fetch).toHaveBeenCalledWith(
99+
expectedUrl,
100+
getCallParams(method),
101+
);
102+
expect(resultRequest).toEqual(expected);
103+
});
104+
105+
it('should make an auth request with body', async() => {
106+
107+
const url = 'test';
108+
const method: MethodType = 'POST';
109+
const body = { hello: 'world' };
110+
111+
const expected = { success: true };
112+
const expectedUrl = `${baseUrl}/${url}`;
113+
114+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
115+
// @ts-ignore
116+
global.fetch = jest.fn().mockResolvedValueOnce({
117+
json: async() => expected,
118+
});
119+
120+
const resultRequest = await makeAuthRequest(url, method, fakeToken, body);
121+
122+
expect(global.fetch).toHaveBeenCalledWith(
123+
expectedUrl,
124+
getCallParams(method, body),
125+
);
126+
expect(resultRequest).toEqual(expected);
127+
});
128+
});
129+

common/utils/__tests__/utils.test.ts

+67-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { IWindow } from '@app/Terminal';
2-
import { addQuotes, resolveToWSLPath } from '@common/utils/utils';
2+
import { addQuotes, hash, isBlank, resolveToWSLPath } from '@common/utils/utils';
3+
import * as crypto from 'crypto';
34

45
describe('Resolve WSL paths', () => {
56

@@ -80,4 +81,69 @@ describe('Add quotes', () => {
8081

8182
expect(addQuotes(paths)).toEqual(expected);
8283
});
84+
85+
it('should throw error with empty path', () => {
86+
87+
expect(() => addQuotes('')).toThrowError(new Error('Path can not be empty'));
88+
expect(() => addQuotes(' ')).toThrowError(new Error('Path can not be empty'));
89+
});
90+
});
91+
92+
describe('Hash password', () => {
93+
94+
it('should hash password', async() => {
95+
96+
const password = 'hello';
97+
const expected = 'helloencryptedhelloencryptedhelloencryptedhelloencrypted'.substr(0, 32);
98+
99+
const hashMock = {
100+
update: jest.fn().mockReturnThis(),
101+
digest: jest.fn().mockReturnValueOnce(expected),
102+
};
103+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
104+
// @ts-ignore
105+
const createHashMock = jest.spyOn(crypto, 'createHash').mockImplementationOnce(() => hashMock);
106+
const resultHash = await hash(password);
107+
108+
expect(createHashMock).toBeCalledWith('sha256');
109+
expect(hashMock.update).toBeCalledWith(password);
110+
expect(hashMock.digest).toBeCalledWith('base64');
111+
expect(resultHash).toEqual(expected);
112+
113+
createHashMock.mockRestore();
114+
});
115+
116+
it('should throw error with empty password', async() => {
117+
118+
const createHashMock = jest.spyOn(crypto, 'createHash');
119+
120+
expect(hash('')).rejects.toEqual(new Error('Password can not be empty'));
121+
expect(hash(' ')).rejects.toEqual(new Error('Password can not be empty'));
122+
expect(createHashMock).not.toBeCalled();
123+
124+
createHashMock.mockRestore();
125+
});
126+
})
127+
128+
describe('String is blank', () => {
129+
130+
it('should return false if string is not empty', () => {
131+
132+
expect(isBlank('test')).toEqual(false);
133+
});
134+
135+
it('should return false if string is not empty and has white space', () => {
136+
137+
expect(isBlank(' test ')).toEqual(false);
138+
});
139+
140+
it('should return true if string is empty', () => {
141+
142+
expect(isBlank('')).toEqual(true);
143+
});
144+
145+
it('should return true if string only has whitespace', () => {
146+
147+
expect(isBlank(' ')).toEqual(true);
148+
});
83149
});

common/utils/request.ts

+10-14
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { isDev } from '@common/utils/utils';
33

44
const { cloudUrl } = Config.getInstance().loadConfig();
55

6-
const baseUrl = isDev ?
6+
export const baseUrl = isDev ?
77
'http://localhost:3333' :
88
cloudUrl;
99

@@ -22,15 +22,13 @@ export type MethodType = 'GET' | 'POST';
2222
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2323
export const makeRequest = async(url: string, method: MethodType, body?: any): Promise<any> => {
2424

25-
// We only make json requests and responses
26-
const headers = new Headers();
27-
headers.append('Content-Type', 'application/json');
28-
headers.append('Accept', 'application/json');
29-
3025
const response = await fetch(`${baseUrl}/${url}`, {
3126

3227
method,
33-
headers,
28+
headers: {
29+
'Accept': 'application/json',
30+
'Content-Type': 'application/json',
31+
},
3432
body: JSON.stringify(body),
3533
});
3634

@@ -51,16 +49,14 @@ export const makeRequest = async(url: string, method: MethodType, body?: any): P
5149
// eslint-disable-next-line @typescript-eslint/no-explicit-any
5250
export const makeAuthRequest = async(url: string, method: MethodType, token: string, body?: any): Promise<any> => {
5351

54-
// We only make json requests and responses
55-
const headers = new Headers();
56-
headers.append('Content-Type', 'application/json');
57-
headers.append('Accept', 'application/json');
58-
headers.append('Authorization', `Bearer ${token}`);
59-
6052
const response = await fetch(`${baseUrl}/${url}`, {
6153

6254
method,
63-
headers,
55+
headers: {
56+
'Accept': 'application/json',
57+
'Content-Type': 'application/json',
58+
'Authorization': `Bearer ${token}`,
59+
},
6460
body: JSON.stringify(body),
6561
});
6662

common/utils/utils.ts

+25-8
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const wslBasePath = '/mnt/';
2323
* @param path - The path to resolve
2424
* @returns The path which work with wsl
2525
*/
26-
export function resolveToWSLPath(window: IWindow, path: string): string {
26+
export const resolveToWSLPath = (window: IWindow, path: string): string => {
2727

2828
if(!isTerminalSSH(window.terminalType) && !(window.terminalType as IShell).path.includes('wsl.exe'))
2929
return path;
@@ -43,7 +43,10 @@ export function resolveToWSLPath(window: IWindow, path: string): string {
4343
* @param path - The path to add quotes to
4444
* @returns The quoted path
4545
*/
46-
export function addQuotes(path: string): string {
46+
export const addQuotes = (path: string): string => {
47+
48+
if(isBlank(path))
49+
throw new Error('Path can not be empty');
4750

4851
if(!path.includes(' '))
4952
return path;
@@ -56,7 +59,7 @@ export function addQuotes(path: string): string {
5659
*
5760
* @param windows - The list of current windows
5861
*/
59-
export function nextWindowId(windows: IWindow[]): number {
62+
export const nextWindowId = (windows: IWindow[]): number => {
6063

6164
let id = 0;
6265

@@ -72,7 +75,7 @@ export function nextWindowId(windows: IWindow[]): number {
7275
* @param terminalType - The type of the terminal to check
7376
* @returns If the terminal type is ssh or shell
7477
*/
75-
export function isTerminalSSH(terminalType: TerminalType): boolean {
78+
export const isTerminalSSH = (terminalType: TerminalType): boolean => {
7679

7780
// To define the type of terminal we check if there
7881
// is a username property. If yes, we assume that
@@ -88,7 +91,7 @@ export function isTerminalSSH(terminalType: TerminalType): boolean {
8891
* @param window - The window to check
8992
* @returns True if window is a settings window
9093
*/
91-
export function isSettingsWindow(window: IWindow): boolean {
94+
export const isSettingsWindow = (window: IWindow): boolean => {
9295

9396
return window.terminalType === null;
9497
}
@@ -100,7 +103,10 @@ export function isSettingsWindow(window: IWindow): boolean {
100103
* @param password - The password to hash
101104
* @returns A promise of the hashed password
102105
*/
103-
export async function hash(password: string): Promise<string> {
106+
export const hash = async(password: string): Promise<string> => {
107+
108+
if(isBlank(password))
109+
throw new Error('Password can not be empty');
104110

105111
return crypto.createHash('sha256').update(String(password)).digest('base64').substr(0, 32);
106112
}
@@ -126,7 +132,7 @@ export interface IEncrypted {
126132
* @param encryptToken - The token used to encrypt
127133
* @returns The IEncrypted object
128134
*/
129-
export function encrypt(text: string, encryptToken: string): IEncrypted {
135+
export const encrypt = (text: string, encryptToken: string): IEncrypted => {
130136

131137
const cipher = crypto.createCipheriv(algorithm, encryptToken, iv);
132138
const encrypted = Buffer.concat([cipher.update(text), cipher.final()]);
@@ -147,10 +153,21 @@ export function encrypt(text: string, encryptToken: string): IEncrypted {
147153
* @returns The decrypted object
148154
*/
149155

150-
export function decrypt(encrypted: IEncrypted, encryptToken: string): string {
156+
export const decrypt = (encrypted: IEncrypted, encryptToken: string): string => {
151157

152158
const decipher = crypto.createDecipheriv(algorithm, encryptToken, Buffer.from(encrypted.iv, 'hex'));
153159
const decrypted = Buffer.concat([decipher.update(Buffer.from(encrypted.content, 'hex')), decipher.final()]);
154160

155161
return decrypted.toString();
156162
}
163+
164+
/**
165+
* Check if a string is null or blank.
166+
*
167+
* @param value - The value to check
168+
* @returns True if the value is null or blank
169+
*/
170+
export const isBlank = (value: string): boolean => {
171+
172+
return (!value || /^\s*$/.test(value));
173+
}

0 commit comments

Comments
 (0)