diff --git a/frontend/app/jest.config.json b/frontend/app/jest.config.json index f5b1e0bf0..e84086b01 100644 --- a/frontend/app/jest.config.json +++ b/frontend/app/jest.config.json @@ -26,11 +26,18 @@ "node" ], "transform": { - "^.+\\.tsx?$": "ts-jest" + "^.+\\.tsx?$": "ts-jest", + "^.+\\.svg$": "/svgTransform.js" }, + "transformIgnorePatterns": [ + "/node_modules/(?!core)" + ], + "modulePaths": [ + "" + ], + "moduleDirectories": ["node_modules", ""], "globals": { - "window": { "location": {"host": "localhost"}}, -"testEnvironment": "jsdom" - - } + "window": { "location": {"host": "localhost"}}, + "testEnvironment": "jsdom" + } } diff --git a/frontend/app/src/__test__/__mockData__/localStorage.ts b/frontend/app/src/__test__/__mockData__/localStorage.ts new file mode 100644 index 000000000..60283956d --- /dev/null +++ b/frontend/app/src/__test__/__mockData__/localStorage.ts @@ -0,0 +1,27 @@ +export const localStorageMock = (function () { + let store = {}; + + return { + getItem(key: string | number) { + return store[key]; + }, + + setItem(key: string | number, value: any) { + store[key] = value; + }, + + clear() { + store = {}; + }, + + removeItem(key: string | number) { + delete store[key]; + }, + + getAll() { + return store; + } + }; +})(); + +Object.defineProperty(window, 'localStorage', { value: localStorageMock }); diff --git a/frontend/app/src/__test__/__mockData__/user.ts b/frontend/app/src/__test__/__mockData__/user.ts index 18e4086b6..41db9f329 100644 --- a/frontend/app/src/__test__/__mockData__/user.ts +++ b/frontend/app/src/__test__/__mockData__/user.ts @@ -1,11 +1,15 @@ +import { v4 as uuidv4 } from 'uuid'; import { MeInfo } from '../../store/ui'; export const user: MeInfo = { id: 1, pubkey: 'test_pub_key', + uuid: uuidv4(), contact_key: 'test_owner_contact_key', + owner_route_hint: 'test_owner_route_hint', alias: 'Vladimir', photo_url: '', + github_issues: [], route_hint: 'test_hint:1099567661057', price_to_meet: 0, jwt: 'test_jwt', @@ -20,7 +24,8 @@ export const user: MeInfo = { }, owner_alias: 'Vladimir', owner_pubkey: 'test_pub_key', - img: '', + img: '/static/avatarPlaceholders/placeholder_34.jpg', twitter_confirmed: false, - isSuperAdmin: false + isSuperAdmin: false, + websocketToken: 'test_websocketToken' }; diff --git a/frontend/app/src/__test__/localStorage.spec.ts b/frontend/app/src/__test__/localStorage.spec.ts new file mode 100644 index 000000000..a66a27d2c --- /dev/null +++ b/frontend/app/src/__test__/localStorage.spec.ts @@ -0,0 +1,43 @@ +import { localStorageMock } from '../__test__/__mockData__/localStorage'; +describe('Local Storage', () => { + beforeEach(() => { + Object.defineProperty(window, 'localStorage', { value: localStorageMock }); + }); + + afterEach(() => { + localStorageMock.clear(); + }); + + it('should set and get an item from local storage', () => { + const key = 'testKey'; + const value = 'testValue'; + + localStorageMock.setItem(key, value); + + expect(localStorageMock.getItem(key)).toEqual(value); + }); + + it('should remove an item from local storage', () => { + const key = 'testKey'; + const value = 'testValue'; + + localStorageMock.setItem(key, value); + localStorageMock.removeItem(key); + + expect(localStorageMock.getItem(key)).toBeUndefined(); + }); + + it('should clear all items from local storage', () => { + const key1 = 'testKey1'; + const value1 = 'testValue1'; + const key2 = 'testKey2'; + const value2 = 'testValue2'; + + localStorageMock.setItem(key1, value1); + localStorageMock.setItem(key2, value2); + localStorageMock.clear(); + + expect(localStorageMock.getItem(key1)).toBeUndefined(); + expect(localStorageMock.getItem(key2)).toBeUndefined(); + }); +}); diff --git a/frontend/app/src/helpers/index.ts b/frontend/app/src/helpers/index.ts index 622b9e385..f080f6989 100644 --- a/frontend/app/src/helpers/index.ts +++ b/frontend/app/src/helpers/index.ts @@ -1 +1 @@ -export * from './helpers'; +export * from './helpers.tsx'; diff --git a/frontend/app/src/store/__test__/main.spec.ts b/frontend/app/src/store/__test__/main.spec.ts new file mode 100644 index 000000000..a0b8568a3 --- /dev/null +++ b/frontend/app/src/store/__test__/main.spec.ts @@ -0,0 +1,74 @@ +import { toJS } from 'mobx'; +import { user } from '../../__test__/__mockData__/user'; +import { uiStore } from '../ui'; +import { MainStore } from '../main'; +import { localStorageMock } from '../../__test__/__mockData__/localStorage'; + +const mockFetch = jest.fn(); +const mockHeaders = jest.fn(); + +const origFetch = global.fetch; + +beforeAll(() => { + global.fetch = mockFetch; + global.Headers = mockHeaders; +}); + +afterAll(() => { + global.fetch = origFetch; + jest.clearAllMocks(); +}); + +describe('Main store', () => { + beforeEach(async () => { + uiStore.setMeInfo(user); + localStorageMock.setItem('ui', JSON.stringify(uiStore)); + }); + + it('should call endpoint on saveBounty', () => { + const mainStore = new MainStore(); + mainStore.saveBounty = jest + .fn() + .mockReturnValueOnce(Promise.resolve({ status: 200, message: 'success' })); + const bounty = { + body: { + title: 'title', + description: 'description', + amount: 100, + owner_pubkey: user.owner_pubkey, + owner_alias: user.alias, + owner_contact_key: user.contact_key, + owner_route_hint: user.route_hint ?? '', + extras: user.extras, + price_to_meet: user.price_to_meet, + img: user.img, + tags: [], + route_hint: user.route_hint + } + }; + mainStore.saveBounty(bounty); + expect(mainStore.saveBounty).toBeCalledWith({ + body: bounty.body + }); + }); + + it('should save user profile', async () => { + const mainStore = new MainStore(); + const person = { + owner_pubkey: user.owner_pubkey, + owner_alias: user.alias, + owner_contact_key: user.contact_key, + owner_route_hint: user.route_hint ?? '', + description: user.description, + extras: user.extras, + price_to_meet: user.price_to_meet, + img: user.img, + tags: [], + route_hint: user.route_hint + }; + mainStore.saveProfile(person); + + expect(toJS(uiStore.meInfo)).toEqual(user); + expect(localStorageMock.getItem('ui')).toEqual(JSON.stringify(uiStore)); + }); +}); diff --git a/frontend/app/src/store/ui.ts b/frontend/app/src/store/ui.ts index 12c7bfd86..1c20675f8 100644 --- a/frontend/app/src/store/ui.ts +++ b/frontend/app/src/store/ui.ts @@ -22,7 +22,7 @@ export interface MeInfo { owner_route_hint?: string; photo_url: string; alias: string; - img?: string; + img: string; owner_alias?: string; github_issues?: any[]; route_hint: string; @@ -211,6 +211,7 @@ export const emptyMeData: MeData = { contact_key: '', price_to_meet: 0, photo_url: '', + img: '', url: '', jwt: '', tribe_jwt: '', @@ -228,6 +229,7 @@ export const emptyMeInfo: MeInfo = { price_to_meet: 0, photo_url: '', url: '', + img: '', jwt: '', tribe_jwt: '', description: '', diff --git a/frontend/app/svgTransform.js b/frontend/app/svgTransform.js new file mode 100644 index 000000000..996dbeed1 --- /dev/null +++ b/frontend/app/svgTransform.js @@ -0,0 +1,9 @@ +module.exports = { + process() { + return { code: 'module.exports = {};' }; + }, + getCacheKey() { + // The output is always the same. + return 'svgTransform'; + }, +}; \ No newline at end of file diff --git a/frontend/app/tsconfig.json b/frontend/app/tsconfig.json index 5d572a7b2..daf21c31c 100644 --- a/frontend/app/tsconfig.json +++ b/frontend/app/tsconfig.json @@ -18,6 +18,7 @@ "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, + "allowImportingTsExtensions": true, "jsx": "react", "noImplicitAny": false, "baseUrl": "./src",