diff --git a/.circleci/config.yml b/.circleci/config.yml index 9710162..317616a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,7 +4,7 @@ jobs: test-job: docker: - - image: "circleci/node:lts" + - image: "cimg/node:lts" working_directory: ~/repo @@ -33,7 +33,7 @@ jobs: command: yarn test environment: TZ: UTC - REACT_APP_COMMIT_SHA: some_sha + VITE_COMMIT_SHA: some_sha - store_artifacts: # For coverage report path: client/coverage diff --git a/build.sh b/build.sh index 5a4972d..8992a82 100755 --- a/build.sh +++ b/build.sh @@ -10,10 +10,10 @@ fi echo "building client..." cd client yarn --production=false -REACT_APP_COMMIT_SHA=$SOURCE_VERSION \ -REACT_APP_API_HOST=$api_host \ -REACT_APP_API_PROTOCOL=$API_PROTOCOL \ -REACT_APP_API_PORT=$API_PORT \ +VITE_COMMIT_SHA=$SOURCE_VERSION \ +VITE_API_HOST=$api_host \ +VITE_API_PROTOCOL=$API_PROTOCOL \ +VITE_API_PORT=$API_PORT \ yarn build cd ../ diff --git a/client/.env.dist b/client/.env.dist index f577e32..311bb67 100644 --- a/client/.env.dist +++ b/client/.env.dist @@ -1,14 +1,14 @@ # Api settings TZ=UTC -REACT_APP_API_HOST=localhost -REACT_APP_API_PROTOCOL=http -REACT_APP_API_PORT=3001 -REACT_APP_COMMIT_SHA=some_sha +VITE_API_HOST=localhost +VITE_API_PROTOCOL=http +VITE_API_PORT=3001 +VITE_COMMIT_SHA=some_sha # To display darkwire version -REACT_APP_COMMIT_SHA=some_sha +VITE_COMMIT_SHA=some_sha # Set max transferable file size in MB -REACT_APP_MAX_FILE_SIZE=4 +VITE_MAX_FILE_SIZE=4 diff --git a/client/.eslintrc b/client/.eslintrc deleted file mode 100644 index e96799e..0000000 --- a/client/.eslintrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": ["plugin:prettier/recommended"], - "parser": "babel-eslint", - "rules": { - "prettier/prettier": "error" - } -} diff --git a/client/.eslintrc.cjs b/client/.eslintrc.cjs new file mode 100644 index 0000000..6815699 --- /dev/null +++ b/client/.eslintrc.cjs @@ -0,0 +1,10 @@ +module.exports = { + extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint'], + root: true, + env: { + browser: true, + node: true, + }, +}; diff --git a/client/.gitignore b/client/.gitignore index e2178dd..c7f41f7 100644 --- a/client/.gitignore +++ b/client/.gitignore @@ -1,9 +1,25 @@ -node_modules -.DS_Store -dist -coverage +# Logs +logs *.log -.env* -!.env.example -build/ -*sublime* \ No newline at end of file +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist/ +dist-ssr +*.local +coverage + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/client/__mocks__/styles.js b/client/__mocks__/styles.js deleted file mode 100644 index f053ebf..0000000 --- a/client/__mocks__/styles.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = {}; diff --git a/client/index.html b/client/index.html new file mode 100644 index 0000000..712990b --- /dev/null +++ b/client/index.html @@ -0,0 +1,17 @@ + + + + + + + + + + + Darkwire.io - instant encrypted web chat + + +
+ + + diff --git a/client/jsconfig.json b/client/jsconfig.json index 1f6b598..0ecf7b3 100644 --- a/client/jsconfig.json +++ b/client/jsconfig.json @@ -6,3 +6,4 @@ } } } + diff --git a/client/package.json b/client/package.json index f9c02ae..c289df5 100644 --- a/client/package.json +++ b/client/package.json @@ -2,6 +2,7 @@ "name": "darkwire-client", "version": "2.0.0-beta.12", "main": "index.js", + "type": "module", "contributors": [ { "name": "Daniel Seripap" @@ -12,44 +13,55 @@ ], "license": "MIT", "dependencies": { - "autosize": "^4.0.2", - "bootstrap": "^4.3.1", - "clipboard": "^2.0.4", - "feather-icons": "^4.21.0", - "jquery": "^3.5.0", - "js-cookie": "^2.2.0", - "lodash": "^4.17.20", - "moment": "^2.24.0", - "node-sass": "^4.13.1", - "popper.js": "^1.15.0", - "randomcolor": "^0.5.4", - "react": "^16.8.6", - "react-dom": "^16.8.6", - "react-feather": "^1.1.6", - "react-linkify": "^0.2.2", - "react-modal": "^3.8.1", - "react-redux": "^7.0.3", - "react-router-dom": "^5.0.0", - "react-scripts": "3.0.0", + "bootstrap": "^4.6.2", + "classnames": "^2.3.2", + "clipboard": "^2.0.11", + "jquery": "3", + "js-cookie": "^3.0.1", + "moment": "^2.29.4", + "nanoid": "^4.0.0", + "randomcolor": "^0.6.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-feather": "^2.0.10", + "react-linkify": "^1.0.0-alpha", + "react-modal": "^3.16.1", + "react-redux": "^8.0.5", + "react-router": "^6.4.4", + "react-router-dom": "^6.4.4", "react-simple-dropdown": "^3.2.3", - "redux": "^4.0.1", - "redux-thunk": "^2.3.0", - "sanitize-html": "^1.20.1", - "shortid": "^2.2.14", - "socket.io-client": "^2.2.0", - "tinycon": "^0.6.8", - "webcrypto-shim": "^0.1.4" + "redux": "^4.2.0", + "redux-thunk": "^2.4.2", + "sanitize-html": "^2.7.3", + "socket.io-client": "^4.5.4", + "tinycon": "^0.6.8" }, "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test --env=jest-environment-jsdom-sixteen", - "coverage": "react-scripts test --env=jest-environment-jsdom-sixteen --coverage --watchAll=false", - "eject": "react-scripts eject", - "lint": "eslint src" + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "test": "TZ=UTC vitest", + "lint": "eslint src", + "coverage": "TZ=UTC vitest --coverage --watch=false" }, - "eslintConfig": { - "extends": "react-app" + "devDependencies": { + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^14.4.3", + "@typescript-eslint/eslint-plugin": "^5.46.0", + "@typescript-eslint/parser": "^5.46.0", + "@vitejs/plugin-react": "^3.0.0", + "@vitest/coverage-istanbul": "^0.25.7", + "eslint": "^8.29.0", + "eslint-config-prettier": "^6.11.0", + "eslint-plugin-prettier": "^3.1.3", + "jest-environment-jsdom-sixteen": "^1.0.3", + "jest-fetch-mock": "^3.0.3", + "prettier": "^2.0.5", + "sass": "^1.56.2", + "typescript": "^4.9.4", + "vitest": "^0.25.7", + "vitest-fetch-mock": "^0.2.1" }, "browserslist": { "production": [ @@ -62,18 +74,5 @@ "last 1 firefox version", "last 1 safari version" ] - }, - "devDependencies": { - "@peculiar/webcrypto": "^1.1.1", - "@testing-library/jest-dom": "^5.5.0", - "@testing-library/react": "^10.0.4", - "enzyme": "^3.9.0", - "enzyme-adapter-react-16": "^1.12.1", - "enzyme-to-json": "^3.3.5", - "eslint-config-prettier": "^6.11.0", - "eslint-plugin-prettier": "^3.1.3", - "jest-environment-jsdom-sixteen": "^1.0.3", - "jest-fetch-mock": "^3.0.3", - "prettier": "^2.0.5" } } diff --git a/client/public/index.html b/client/public/index.html deleted file mode 100644 index f15050b..0000000 --- a/client/public/index.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - darkwire.io - instant encrypted web chat - - - -
- - - diff --git a/client/src/actions/app.test.js b/client/src/actions/app.test.js index 5589671..9ba2a35 100644 --- a/client/src/actions/app.test.js +++ b/client/src/actions/app.test.js @@ -1,5 +1,7 @@ import * as actions from './app'; +import { describe, it, expect, vi } from 'vitest'; + describe('App actions', () => { it('should create an action to scroll to bottom', () => { expect(actions.setScrolledToBottom('test')).toEqual({ @@ -22,7 +24,7 @@ describe('App actions', () => { }); it('should create an action to clear activities', () => { - const mockDispatch = jest.fn(); + const mockDispatch = vi.fn(); actions.clearActivities()(mockDispatch); @@ -31,7 +33,7 @@ describe('App actions', () => { }); }); it('should create all actions', () => { - const mockDispatch = jest.fn(); + const mockDispatch = vi.fn(); const actionsResults = [ [actions.toggleWindowFocus('test'), 'TOGGLE_WINDOW_FOCUS'], diff --git a/client/src/actions/encrypted_messages.js b/client/src/actions/encrypted_messages.js index 2cefd40..9500042 100644 --- a/client/src/actions/encrypted_messages.js +++ b/client/src/actions/encrypted_messages.js @@ -1,5 +1,5 @@ -import { getSocket } from 'utils/socket'; -import { prepare as prepareMessage, process as processMessage } from 'utils/message'; +import { getSocket } from '@/utils/socket'; +import { prepare as prepareMessage, process as processMessage } from '@/utils/message'; export const sendEncryptedMessage = payload => async (dispatch, getState) => { const state = getState(); diff --git a/client/src/actions/encrypted_messages.test.js b/client/src/actions/encrypted_messages.test.js index 9687322..10fc3fe 100644 --- a/client/src/actions/encrypted_messages.test.js +++ b/client/src/actions/encrypted_messages.test.js @@ -1,21 +1,23 @@ import * as actions from './encrypted_messages'; -import { getSocket } from 'utils/socket'; -import { prepare as prepareMessage, process as processMessage } from 'utils/message'; +import { getSocket } from '@/utils/socket'; +import { prepare as prepareMessage, process as processMessage } from '@/utils/message'; -jest.mock('utils/message', () => { +import { describe, it, expect, vi } from 'vitest'; + +vi.mock('@/utils/message', () => { return { - prepare: jest + prepare: vi .fn() .mockResolvedValue({ original: { type: 'messageType', payload: 'test' }, toSend: 'encryptedpayload' }), - process: jest.fn().mockResolvedValue({ type: 'messageType', payload: 'test' }), + process: vi.fn().mockResolvedValue({ type: 'messageType', payload: 'test' }), }; }); -const mockEmit = jest.fn(); +const mockEmit = vi.fn(); -jest.mock('utils/socket', () => { +vi.mock('@/utils/socket', () => { return { - getSocket: jest.fn().mockImplementation(() => ({ + getSocket: vi.fn().mockImplementation(() => ({ emit: mockEmit, })), }; @@ -23,9 +25,9 @@ jest.mock('utils/socket', () => { describe('Encrypted messages actions', () => { it('should create an action to send message', async () => { - const mockDispatch = jest.fn(); + const mockDispatch = vi.fn(); - await actions.sendEncryptedMessage({ payload: 'payload' })(mockDispatch, jest.fn().mockReturnValue({ state: {} })); + await actions.sendEncryptedMessage({ payload: 'payload' })(mockDispatch, vi.fn().mockReturnValue({ state: {} })); expect(prepareMessage).toHaveBeenLastCalledWith({ payload: 'payload' }, { state: {} }); expect(mockDispatch).toHaveBeenLastCalledWith({ payload: 'test', type: 'SEND_ENCRYPTED_MESSAGE_messageType' }); @@ -33,11 +35,11 @@ describe('Encrypted messages actions', () => { }); it('should create an action to receive message', async () => { - const mockDispatch = jest.fn(); + const mockDispatch = vi.fn(); await actions.receiveEncryptedMessage({ payload: 'encrypted' })( mockDispatch, - jest.fn().mockReturnValue({ state: {} }), + vi.fn().mockReturnValue({ state: {} }), ); expect(processMessage).toHaveBeenLastCalledWith({ payload: 'encrypted' }, { state: {} }); diff --git a/client/src/actions/unencrypted_messages.js b/client/src/actions/unencrypted_messages.js index 115d54b..d87cc02 100644 --- a/client/src/actions/unencrypted_messages.js +++ b/client/src/actions/unencrypted_messages.js @@ -1,4 +1,4 @@ -import { getSocket } from 'utils/socket'; +import { getSocket } from '@/utils/socket'; const receiveUserEnter = (payload, dispatch) => { dispatch({ type: 'USER_ENTER', payload }); @@ -70,7 +70,7 @@ const sendToggleLockRoom = (dispatch, getState) => { }); }; -export const sendUnencryptedMessage = (type, payload) => async (dispatch, getState) => { +export const sendUnencryptedMessage = type => async (dispatch, getState) => { switch (type) { case 'TOGGLE_LOCK_ROOM': return sendToggleLockRoom(dispatch, getState); diff --git a/client/src/actions/unencrypted_messages.test.js b/client/src/actions/unencrypted_messages.test.js index dc52cd5..f28c150 100644 --- a/client/src/actions/unencrypted_messages.test.js +++ b/client/src/actions/unencrypted_messages.test.js @@ -1,33 +1,34 @@ import * as actions from './unencrypted_messages'; -import { getSocket } from 'utils/socket'; -const mockEmit = jest.fn((_type, _null, callback) => { +import { describe, it, expect, vi } from 'vitest'; + +const mockEmit = vi.fn((_type, _null, callback) => { callback({ isLocked: true }); }); -jest.mock('utils/socket', () => { +vi.mock('@/utils/socket', () => { return { - getSocket: jest.fn().mockImplementation(() => ({ + getSocket: vi.fn().mockImplementation(() => ({ emit: mockEmit, })), }; }); -describe('Reveice unencrypted message actions', () => { +describe('Receive unencrypted message actions', () => { it('should create no action', () => { - const mockDispatch = jest.fn(); - actions.receiveUnencryptedMessage('FAKE')(mockDispatch, jest.fn().mockReturnValue({})); + const mockDispatch = vi.fn(); + actions.receiveUnencryptedMessage('FAKE')(mockDispatch, vi.fn().mockReturnValue({})); expect(mockDispatch).not.toHaveBeenCalled(); }); it('should create user enter action', () => { - const mockDispatch = jest.fn(); - actions.receiveUnencryptedMessage('USER_ENTER', 'test')(mockDispatch, jest.fn().mockReturnValue({ state: {} })); + const mockDispatch = vi.fn(); + actions.receiveUnencryptedMessage('USER_ENTER', 'test')(mockDispatch, vi.fn().mockReturnValue({ state: {} })); expect(mockDispatch).toHaveBeenLastCalledWith({ type: 'USER_ENTER', payload: 'test' }); }); it('should create user exit action', () => { - const mockDispatch = jest.fn(); + const mockDispatch = vi.fn(); const state = { room: { members: [ @@ -37,7 +38,7 @@ describe('Reveice unencrypted message actions', () => { ], }, }; - const mockGetState = jest.fn().mockReturnValue(state); + const mockGetState = vi.fn().mockReturnValue(state); const payload1 = [ { publicKey: { n: 'alankey' } }, { publicKey: { n: 'dankey' } }, @@ -62,7 +63,7 @@ describe('Reveice unencrypted message actions', () => { }); it('should create receive toggle lock room action', () => { - const mockDispatch = jest.fn(); + const mockDispatch = vi.fn(); const state = { room: { members: [ @@ -71,7 +72,7 @@ describe('Reveice unencrypted message actions', () => { ], }, }; - const mockGetState = jest.fn().mockReturnValue(state); + const mockGetState = vi.fn().mockReturnValue(state); const payload = { publicKey: { n: 'alankey' } }; actions.receiveUnencryptedMessage('TOGGLE_LOCK_ROOM', payload)(mockDispatch, mockGetState); @@ -82,14 +83,14 @@ describe('Reveice unencrypted message actions', () => { }); it('should create receive toggle lock room action', () => { - const mockDispatch = jest.fn(); + const mockDispatch = vi.fn(); const state = { user: { username: 'alan', id: 'idalan', }, }; - const mockGetState = jest.fn().mockReturnValue(state); + const mockGetState = vi.fn().mockReturnValue(state); actions.sendUnencryptedMessage('TOGGLE_LOCK_ROOM')(mockDispatch, mockGetState); expect(mockDispatch).toHaveBeenLastCalledWith({ @@ -101,20 +102,20 @@ describe('Reveice unencrypted message actions', () => { describe('Send unencrypted message actions', () => { it('should create no action', () => { - const mockDispatch = jest.fn(); - actions.sendUnencryptedMessage('FAKE')(mockDispatch, jest.fn().mockReturnValue({})); + const mockDispatch = vi.fn(); + actions.sendUnencryptedMessage('FAKE')(mockDispatch, vi.fn().mockReturnValue({})); expect(mockDispatch).not.toHaveBeenCalled(); }); it('should create toggle lock room action', () => { - const mockDispatch = jest.fn(); + const mockDispatch = vi.fn(); const state = { user: { username: 'alan', id: 'idalan', }, }; - const mockGetState = jest.fn().mockReturnValue(state); + const mockGetState = vi.fn().mockReturnValue(state); actions.sendUnencryptedMessage('TOGGLE_LOCK_ROOM')(mockDispatch, mockGetState); expect(mockDispatch).toHaveBeenLastCalledWith({ diff --git a/client/src/api/config.js b/client/src/api/config.js index adff730..c0c127d 100644 --- a/client/src/api/config.js +++ b/client/src/api/config.js @@ -3,21 +3,21 @@ let host; let protocol; let port; -switch (process.env.NODE_ENV) { +switch (import.meta.env.NODE_ENV) { case 'staging': - host = process.env.REACT_APP_API_HOST; - protocol = process.env.REACT_APP_API_PROTOCOL || 'https'; - port = process.env.REACT_APP_API_PORT || 443; + host = import.meta.env.VITE_API_HOST; + protocol = import.meta.env.VITE_API_PROTOCOL || 'https'; + port = import.meta.env.VITE_API_PORT || 443; break; case 'production': - host = process.env.REACT_APP_API_HOST; - protocol = process.env.REACT_APP_API_PROTOCOL || 'https'; - port = process.env.REACT_APP_API_PORT || 443; + host = import.meta.env.VITE_API_HOST; + protocol = import.meta.env.VITE_API_PROTOCOL || 'https'; + port = import.meta.env.VITE_API_PORT || 443; break; default: - host = process.env.REACT_APP_API_HOST || 'localhost'; - protocol = process.env.REACT_APP_API_PROTOCOL || 'http'; - port = process.env.REACT_APP_API_PORT || 3001; + host = import.meta.env.VITE_API_HOST || 'localhost'; + protocol = import.meta.env.VITE_API_PROTOCOL || 'http'; + port = import.meta.env.VITE_API_PORT || 3001; } export default { diff --git a/client/src/components/About/About.test.js b/client/src/components/About/About.test.jsx similarity index 80% rename from client/src/components/About/About.test.js rename to client/src/components/About/About.test.jsx index 74846e3..21ad331 100644 --- a/client/src/components/About/About.test.js +++ b/client/src/components/About/About.test.jsx @@ -1,15 +1,17 @@ import React from 'react'; -import { render, fireEvent, waitFor } from '@testing-library/react'; +import { render, fireEvent } from '@testing-library/react'; import About from '.'; -import fetchMock from 'jest-fetch-mock'; +import { describe, it, expect, vi, afterEach } from 'vitest'; -jest.useFakeTimers(); +vi.useFakeTimers(); // Mock Api generator -jest.mock('../../api/generator', () => { - return path => { - return `http://fakedomain/${path}`; +vi.mock('@/api/generator', () => { + return { + default: path => { + return `http://fakedomain/${path}`; + }, }; }); @@ -43,7 +45,7 @@ describe('About component', () => { fireEvent.change(getByPlaceholderText('Room ID'), { target: { value: 'newRoomName' } }); - jest.runAllTimers(); + vi.runAllTimers(); fireEvent.click(getByText('Submit')); diff --git a/client/src/components/About/__snapshots__/About.test.js.snap b/client/src/components/About/__snapshots__/About.test.jsx.snap similarity index 99% rename from client/src/components/About/__snapshots__/About.test.js.snap rename to client/src/components/About/__snapshots__/About.test.jsx.snap index 5d3841a..eae0680 100644 --- a/client/src/components/About/__snapshots__/About.test.js.snap +++ b/client/src/components/About/__snapshots__/About.test.jsx.snap @@ -1,12 +1,12 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1 -exports[`About component should display 1`] = ` +exports[`About component > should display 1`] = `