diff --git a/app/components/inputs/ReceiveAccountsSelect.jsx b/app/components/inputs/ReceiveAccountsSelect.jsx index b56b8f8399..f090b8ebbe 100644 --- a/app/components/inputs/ReceiveAccountsSelect.jsx +++ b/app/components/inputs/ReceiveAccountsSelect.jsx @@ -3,6 +3,7 @@ import AccountsSelect from "./AccountsSelect"; import { useSelector, useDispatch } from "react-redux"; import * as ca from "actions/ControlActions"; import * as sel from "selectors"; +import { compose, get, eq } from "lodash/fp"; function ReceiveAccountsSelect({ onChange, @@ -12,7 +13,16 @@ function ReceiveAccountsSelect({ }) { const dispatch = useDispatch(); const mixedAccount = useSelector(sel.getMixedAccount); - const nextAddressAccount = useSelector(sel.nextAddressAccount); + const getNextAddressResponse = useSelector(sel.getNextAddressResponse); + const visibleAccounts = useSelector(sel.visibleAccounts); + + const nextAddressAccountNumber = getNextAddressResponse + ? getNextAddressResponse.accountNumber + : null; + + const nextAddressAccount = visibleAccounts.find( + compose(eq(nextAddressAccountNumber), get("value")) + ); const getNextAddressAttempt = useCallback( (value) => dispatch(ca.getNextAddressAttempt(value)), diff --git a/app/components/shared/SendTransaction/hooks.js b/app/components/shared/SendTransaction/hooks.js index faf3c996de..b847521f33 100644 --- a/app/components/shared/SendTransaction/hooks.js +++ b/app/components/shared/SendTransaction/hooks.js @@ -4,6 +4,7 @@ import * as ca from "actions/ControlActions"; import { baseOutput } from "./helpers"; import { useSelector, useDispatch, shallowEqual } from "react-redux"; import { usePrevious } from "hooks"; +import { compose, get, eq } from "lodash/fp"; export function useSendTransaction() { const defaultSpendingAccount = useSelector( @@ -16,7 +17,16 @@ export function useSendTransaction() { const estimatedSignedSize = useSelector(sel.estimatedSignedSize); const totalSpent = useSelector(sel.totalSpent); const nextAddress = useSelector(sel.nextAddress); - const nextAddressAccount = useSelector(sel.nextAddressAccount, shallowEqual); + const getNextAddressResponse = useSelector(sel.getNextAddressResponse); + const visibleAccounts = useSelector(sel.visibleAccounts); + + const nextAddressAccountNumber = getNextAddressResponse + ? getNextAddressResponse.accountNumber + : null; + + const nextAddressAccount = visibleAccounts.find( + compose(eq(nextAddressAccountNumber), get("value")) + ); const constructTxLowBalance = useSelector(sel.constructTxLowBalance); const publishTxResponse = useSelector(sel.publishTxResponse); const notMixedAccounts = useSelector( diff --git a/app/components/views/ProposalDetailsPage/helpers/ProposalCard/ProposalCard.jsx b/app/components/views/ProposalDetailsPage/helpers/ProposalCard/ProposalCard.jsx index fd34280d1b..cd350391b5 100644 --- a/app/components/views/ProposalDetailsPage/helpers/ProposalCard/ProposalCard.jsx +++ b/app/components/views/ProposalDetailsPage/helpers/ProposalCard/ProposalCard.jsx @@ -3,12 +3,13 @@ import { classNames, StatusBar, Tooltip, Text, StatusTag } from "pi-ui"; import { FormattedMessage as T } from "react-intl"; import { PoliteiaLink } from "shared"; import { - Event, VOTE_ENDS_EVENT, VOTE_ENDED_EVENT, - PROPOSAL_UPDATED_EVENT, - Join -} from "../"; + PROPOSAL_UPDATED_EVENT +} from "../Event"; +import Join from "../Join"; +import Event from "../Event"; + import { getStatusBarData, getProposalStatusTagProps } from "../../utils"; const ProposalCard = ({ diff --git a/app/containers/App/App.jsx b/app/containers/App/App.jsx index c862784613..a91606d96f 100644 --- a/app/containers/App/App.jsx +++ b/app/containers/App/App.jsx @@ -8,7 +8,6 @@ import ShutdownPage from "components/views/ShutdownPage/ShutdownPage"; import FatalErrorPage from "components/views/FatalErrorPage/FatalErrorPage"; import Snackbar from "components/Snackbar"; import TrezorModals from "components/modals/TrezorModals/TrezorModals"; -import { hot } from "react-hot-loader/root"; import { CantCloseModals, AboutModal, ConfirmationDialogModal } from "modals"; import { useApp } from "../hooks"; import styles from "./App.module.css"; @@ -52,4 +51,4 @@ const App = () => { ); }; -export default hot(App); +export default App; diff --git a/app/index.js b/app/index.js index 20493999a4..35dd3f0dcb 100644 --- a/app/index.js +++ b/app/index.js @@ -1,4 +1,4 @@ -import ReactDOM from "react-dom"; +import { createRoot } from "react-dom/client"; import { Provider } from "react-redux"; import { ConnectedRouter } from "connected-react-router"; import { Switch, Route } from "react-router-dom"; @@ -18,7 +18,6 @@ import { } from "constants"; import * as cfgConstants from "constants/config"; import { wallet } from "wallet-preload-shim"; -import { AppContainer } from "react-hot-loader"; import { defaultLightTheme, ThemeProvider, @@ -560,23 +559,19 @@ const defaultThemeName = currentTheme.includes("dark") ? DEFAULT_DARK_THEME_NAME : DEFAULT_LIGHT_THEME_NAME; -const render = () => - ReactDOM.render( - - - - - - - - - - - , - document.getElementById("root") - ); - -render(); +const container = document.getElementById("root"); +const root = createRoot(container); +root.render( + + + + + + + + + +); diff --git a/app/selectors.js b/app/selectors.js index 6c1b1e0aca..357b5e3428 100644 --- a/app/selectors.js +++ b/app/selectors.js @@ -717,7 +717,10 @@ export const buyerAccount = createSelector( ); export const buyerMaxFeePercentage = get(["vsp", "maxFeePercentage"]); -const getNextAddressResponse = get(["control", "getNextAddressResponse"]); +export const getNextAddressResponse = get([ + "control", + "getNextAddressResponse" +]); const nextAddressAccountNumber = compose( (res) => (res ? res.accountNumber : null), getNextAddressResponse diff --git a/babel.config.js b/babel.config.js index 87bf9e69bb..107b7ce051 100644 --- a/babel.config.js +++ b/babel.config.js @@ -27,7 +27,6 @@ module.exports = function (api) { "@babel/plugin-proposal-optional-chaining", "@babel/plugin-proposal-throw-expressions", "./scripts/aliasDefaultMessage.js", - "react-hot-loader/babel", [ "react-intl", { "messagesDir": "app/i18n/extracted" } ], [ "module-resolver", { "root": [ "./app" ], diff --git a/package.json b/package.json index c704ad2949..9f98cc20eb 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "description": "Decrediton based on React, React Router, Webpack, React Hot Loader for rapid application development", "main": "main.js", "scripts": { - "test": "TZ=UTC cross-env NODE_ENV=test jest", + "test": "TZ=UTC cross-env NODE_ENV=test jest -i", "test-watch": "TZ=UTC ./node_modules/.bin/jest --watch", "test-debug": "node --inspect-brk ./node_modules/jest/bin/jest.js --runInBand --watch", "test-e2e": "cross-env NODE_ENV=test BABEL_DISABLE_CACHE=1 mocha --retries 2 --compilers js:@babel/register --require ./test/setup.js ./test/e2e.js", @@ -225,10 +225,11 @@ "@babel/preset-react": "7.18.6", "@babel/register": "7.18.9", "@electron/rebuild": "3.3.0", - "@testing-library/dom": "^7.21.3", - "@testing-library/jest-dom": "^4.2.4", - "@testing-library/react": "^9.4.0", - "@testing-library/user-event": "12.8.3", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.10", + "@testing-library/dom": "^8.20.0", + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^14.4.3", "babel-core": "^7.0.0-bridge.0", "babel-jest": "^26.6.0", "babel-loader": "8.3.0", @@ -258,6 +259,7 @@ "node-loader": "1.0.3", "prettier": "2.7.1", "react-intl-translations-manager": "^5.0.0", + "react-refresh": "^0.14.0", "redux-logger": "^2.7.4", "source-map-explorer": "^2.5.3", "style-loader": "^2.0.0", @@ -305,11 +307,10 @@ "qr-image": "^3.2.0", "qrcode": "1.5.3", "raw-loader": "^0.5.1", - "react": "16.14.0", - "react-dom": "16.14.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", "react-draggable": "4.4.5", "react-event-listener": "^0.6.6", - "react-hot-loader": "4.13.1", "react-infinite-scroller": "1.2.6", "react-intl": "^3.11.0", "react-intl-po": "^2.2.2", diff --git a/test/test-utils.js b/test/test-utils.js index eb5c14d515..7cb6f7ac30 100644 --- a/test/test-utils.js +++ b/test/test-utils.js @@ -1,4 +1,5 @@ import { render as rtlRender } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; import "@testing-library/jest-dom/extend-expect"; import { createMemoryHistory } from "history"; import configureStore from "store/configureStore"; @@ -18,6 +19,8 @@ import { DEFAULT_LIGHT_THEME_NAME } from "pi-ui"; +globalThis.IS_REACT_ACT_ENVIRONMENT = true; + beforeAll(() => { jest.spyOn(console, "groupCollapsed").mockImplementation(() => {}); jest.spyOn(console, "info").mockImplementation(() => {}); @@ -43,6 +46,7 @@ const themes = { function render(ui, renderOptions) { const history = createMemoryHistory(); + const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime }); let currentSettings = { locale: locale.key, theme: DEFAULT_LIGHT_THEME_NAME, @@ -112,7 +116,8 @@ function render(ui, renderOptions) { return { ...rtlRender(ui, { wrapper: Wrapper, ...renderOptions }), history, - store + store, + user }; } diff --git a/test/unit/actions/AccountMixerActions.spec.js b/test/unit/actions/AccountMixerActions.spec.js index 774e1f9303..6e285d54f2 100644 --- a/test/unit/actions/AccountMixerActions.spec.js +++ b/test/unit/actions/AccountMixerActions.spec.js @@ -1,4 +1,4 @@ -import { wait } from "@testing-library/dom"; +import { waitFor } from "@testing-library/dom"; import * as ama from "actions/AccountMixerActions"; import * as cla from "actions/ClientActions"; import * as cta from "actions/ControlActions"; @@ -299,7 +299,7 @@ test("test runAccountMixer start and stop", async () => { }) ); - await wait(() => + await waitFor(() => expect(mockUnlockAccount).toHaveBeenCalledWith( testWalletService, testPassphrase, @@ -307,7 +307,7 @@ test("test runAccountMixer start and stop", async () => { ) ); - await wait(() => + await waitFor(() => expect(mockRunAccountMixerRequest).toHaveBeenCalledWith( testAccountMixerService, { @@ -318,7 +318,7 @@ test("test runAccountMixer start and stop", async () => { } ) ); - await wait(() => + await waitFor(() => expect(store.getState().grpc.accountMixerRunning).toBeTruthy() ); mockOnEvent("data"); @@ -335,7 +335,7 @@ test("test runAccountMixer start and stop", async () => { mockOnEvent("end"); expect(store.getState().grpc.mixerStreamerError).toBe(testError); - await wait(() => + await waitFor(() => expect(mockLockAccount).toHaveBeenCalledWith( testWalletService, changeAccountNumber @@ -344,7 +344,7 @@ test("test runAccountMixer start and stop", async () => { expect(mockCleanPrivacyLogs).toHaveBeenCalled(); expect(mockMixerStreamerCancel).toHaveBeenCalled(); - await wait(() => + await waitFor(() => expect(store.getState().grpc.accountMixerRunning).toBeFalsy() ); expect(store.getState().grpc.mixerStreamer).toBeNull(); @@ -376,7 +376,7 @@ test("test runAccountMixer - unlockAcctAndExecFn failed", async () => { }) ); - await wait(() => + await waitFor(() => expect(mockUnlockAccount).toHaveBeenCalledWith( testWalletService, testPassphrase, @@ -384,7 +384,7 @@ test("test runAccountMixer - unlockAcctAndExecFn failed", async () => { ) ); - await wait(() => + await waitFor(() => expect(store.getState().grpc.mixerStreamerError).toBe(testError) ); }); diff --git a/test/unit/actions/ClientActions.spec.js b/test/unit/actions/ClientActions.spec.js index 3708c6ffed..1009f5bea7 100644 --- a/test/unit/actions/ClientActions.spec.js +++ b/test/unit/actions/ClientActions.spec.js @@ -1,7 +1,7 @@ import * as wal from "wallet"; import * as cla from "actions/ClientActions"; import { createStore } from "test-utils.js"; -import { wait } from "@testing-library/react"; +import { waitFor } from "@testing-library/react"; import { defaultMockAvailableMainnetVsps, defaultMockAvailableTestnetVsps, @@ -42,7 +42,7 @@ test("test getTreasuryPolicies", async () => { const store = createStore({}); await store.dispatch(clientActions.getTreasuryPolicies()); - await wait(() => + await waitFor(() => expect(store.getState().grpc.getTreasuryPoliciesRequestAttempt).toBeFalsy() ); expect(store.getState().grpc.getTreasuryPoliciesResponse).toBe( @@ -56,7 +56,7 @@ test("test getTreasuryPolicies (failed request)", async () => { const store = createStore({}); await store.dispatch(clientActions.getTreasuryPolicies()); - await wait(() => + await waitFor(() => expect(store.getState().grpc.getTreasuryPoliciesRequestAttempt).toBeFalsy() ); expect(store.getState().grpc.getTreasuryPoliciesResponse).toBe(undefined); @@ -67,7 +67,7 @@ test("test setTreasuryPolicy", async () => { const store = createStore({}); await store.dispatch(clientActions.setTreasuryPolicy()); - await wait(() => + await waitFor(() => expect(store.getState().grpc.setTreasuryPolicyRequestAttempt).toBeFalsy() ); expect(store.getState().grpc.setTreasuryPolicyError).toBe(null); @@ -85,7 +85,7 @@ test("test setTreasuryPolicy (failed request)", async () => { const store = createStore({}); await store.dispatch(clientActions.setTreasuryPolicy()); - await wait(() => + await waitFor(() => expect(store.getState().grpc.setTreasuryPolicyRequestAttempt).toBeFalsy() ); expect(store.getState().grpc.setTreasuryPolicyError).toBe(testErrorMsg); @@ -100,7 +100,7 @@ test("test getTSpendPolicies", async () => { const store = createStore({}); await store.dispatch(clientActions.getTSpendPolicies()); - await wait(() => + await waitFor(() => expect(store.getState().grpc.getTSpendPoliciesRequestAttempt).toBeFalsy() ); expect(store.getState().grpc.getTSpendPoliciesResponse).toBe( @@ -114,7 +114,7 @@ test("test getTSpendPolicies (failed request)", async () => { const store = createStore({}); await store.dispatch(clientActions.getTSpendPolicies()); - await wait(() => + await waitFor(() => expect(store.getState().grpc.getTSpendPoliciesRequestAttempt).toBeFalsy() ); expect(store.getState().grpc.getTSpendPoliciesResponse).toBe(undefined); @@ -125,7 +125,7 @@ test("test setTSpendPolicy", async () => { const store = createStore({}); await store.dispatch(clientActions.setTSpendPolicy()); - await wait(() => + await waitFor(() => expect(store.getState().grpc.setTSpendPolicyRequestAttempt).toBeFalsy() ); expect(store.getState().grpc.setTSpendPolicyError).toBe(null); @@ -143,7 +143,7 @@ test("test setTSpendPolicy (failed request)", async () => { const store = createStore({}); await store.dispatch(clientActions.setTSpendPolicy()); - await wait(() => + await waitFor(() => expect(store.getState().grpc.setTSpendPolicyRequestAttempt).toBeFalsy() ); expect(store.getState().grpc.setTSpendPolicyError).toBe(testErrorMsg); diff --git a/test/unit/actions/ControlActions.spec.js b/test/unit/actions/ControlActions.spec.js index 073026eb27..8bbfcde327 100644 --- a/test/unit/actions/ControlActions.spec.js +++ b/test/unit/actions/ControlActions.spec.js @@ -31,7 +31,7 @@ import { ERR_INVALID_MASTERPUB_CHECKSUM, ERR_INVALID_MASTER_PUB_KEY } from "../../../app/helpers/addresses"; -import { wait } from "@testing-library/react"; +import { waitFor } from "@testing-library/react"; import { advanceBy, clear } from "jest-date-mock"; import { act } from "react-dom/test-utils"; @@ -666,7 +666,7 @@ test("test monitorLockableAccounts", async () => { expect(mockGetVSPTrackedTickets).toHaveBeenCalled(); expect(mockSetNeedsVSPdProcessTickets).not.toHaveBeenCalled(); addUnlockedAccount(commitmentAccountNumber); // mock that commitmentAccountNumber has been unlocked - await wait(() => + await waitFor(() => expect(mockLockAccount).toHaveBeenCalledWith( testWalletService, commitmentAccountNumber @@ -686,7 +686,9 @@ test("test monitorLockableAccounts", async () => { advanceBy(31 * 1000); jest.advanceTimersByTime(31 * 1000); }); - await wait(() => expect(mockSetNeedsVSPdProcessTickets).toHaveBeenCalled()); + await waitFor(() => + expect(mockSetNeedsVSPdProcessTickets).toHaveBeenCalled() + ); expect(mockGetVSPTrackedTickets).toHaveBeenCalled(); expect(mockLockAccount).not.toHaveBeenCalled(); expect(store.getState().control.lockAccountError).toBeNull(); @@ -733,7 +735,7 @@ test("test monitorLockableAccounts - lockAccount failed", async () => { addUnlockedAccount(commitmentAccountNumber); // mock that commitmentAccountNumber has been unlocked - await wait(() => + await waitFor(() => expect(mockLockAccount).toHaveBeenCalledWith( testWalletService, commitmentAccountNumber @@ -761,7 +763,7 @@ test("test unlockAcctAndExecFn", async () => { testPassphrase, changeAccountNumber ); - await wait(() => + await waitFor(() => expect(store.getState().control.unlockAndExecFnRunning).toBeFalsy() ); expect(mockCallbackFunction).toHaveBeenCalledTimes(1); @@ -887,7 +889,7 @@ test("test unlockAcctAndExecFn - callback function failed, lock after, multi acc testWalletService, mixedAccountNumber ); - await wait(() => + await waitFor(() => expect(store.getState().control.unlockAndExecFnRunning).toBeFalsy() ); @@ -1050,7 +1052,7 @@ test("test unlockAcctAndExecFn - does not unlock change account if account mixer testPassphrase, changeAccountNumber ); - await wait(() => + await waitFor(() => expect(store.getState().control.unlockAndExecFnRunning).toBeFalsy() ); expect(mockCallbackFunction).toHaveBeenCalledTimes(1); @@ -1147,7 +1149,7 @@ test("test purchase tickets - in a private wallet, mixer is not running", async testWalletService, defaultAccountNumber ); - await wait(() => + await waitFor(() => expect(mockLockAccount).toHaveBeenNthCalledWith( 2, testWalletService, @@ -1200,7 +1202,7 @@ test("test purchase tickets - failed to unlock account", async () => { ) ); - await wait(() => + await waitFor(() => expect(mockLockAccount).toHaveBeenNthCalledWith( 1, testWalletService, @@ -1264,7 +1266,7 @@ test("test purchase tickets - in a private wallet, mixer is running", async () = // mixer has been stoped expect(mockMixerStreamerCancel).toHaveBeenCalled(); - await wait(() => + await waitFor(() => expect(mockLockAccount).toHaveBeenNthCalledWith( 1, testWalletService, @@ -1296,14 +1298,14 @@ test("test purchase tickets - in a private wallet, mixer is running", async () = expect(mockUpdateUsedVSPs).toHaveBeenCalledWith(testVSP); expect(mockGetVSPTrackedTickets).toHaveBeenCalled(); - await wait(() => + await waitFor(() => expect(mockLockAccount).toHaveBeenNthCalledWith( 2, testWalletService, defaultAccountNumber ) ); - await wait(() => + await waitFor(() => expect(mockLockAccount).toHaveBeenNthCalledWith( 3, testWalletService, @@ -1333,7 +1335,7 @@ test("test purchase tickets - in a private wallet, mixer is running", async () = testPassphrase, changeAccountNumber ); - await wait(() => + await waitFor(() => expect(store.getState().grpc.accountMixerRunning).toBeTruthy() ); }); @@ -1364,7 +1366,7 @@ test("test purchase tickets - in a non private wallet", async () => { defaultAccountNumber ); - await wait(() => + await waitFor(() => expect(mockLockAccount).toHaveBeenCalledWith( testWalletService, defaultAccountNumber @@ -1429,7 +1431,7 @@ test("test purchase tickets - in a watching wallet", async () => { // Since we are currently using the default account for the ticket's // voting address we also need to unlock that account so communication // with the VSP may occur. - await wait(() => + await waitFor(() => expect(mockLockAccount).toHaveBeenNthCalledWith( 1, testWalletService, @@ -1521,7 +1523,7 @@ test("test purchase tickets - failed ", async () => { // mixer has been stoped expect(mockMixerStreamerCancel).toHaveBeenCalled(); - await wait(() => + await waitFor(() => expect(mockLockAccount).toHaveBeenNthCalledWith( 1, testWalletService, @@ -1557,7 +1559,7 @@ test("test purchase tickets - failed ", async () => { testPassphrase, changeAccountNumber ); - await wait(() => + await waitFor(() => expect(store.getState().grpc.accountMixerRunning).toBeTruthy() ); }); @@ -1601,7 +1603,7 @@ test("test purchase tickets - failed with insufficient balance", async () => { // mixer has been stoped expect(mockMixerStreamerCancel).toHaveBeenCalled(); - await wait(() => + await waitFor(() => expect(mockLockAccount).toHaveBeenNthCalledWith( 1, testWalletService, @@ -1639,7 +1641,7 @@ test("test purchase tickets - failed with insufficient balance", async () => { // Since we are currently using the default account for the ticket's // voting address we also need to unlock that account so communication // with the VSP may occur. - await wait(() => + await waitFor(() => expect(mockLockAccount).toHaveBeenNthCalledWith( 2, testWalletService, @@ -1667,7 +1669,7 @@ test("test purchase tickets - failed with insufficient balance", async () => { testPassphrase, changeAccountNumber ); - await wait(() => + await waitFor(() => expect(store.getState().grpc.accountMixerRunning).toBeTruthy() ); }); @@ -1695,7 +1697,7 @@ test("test start and stop ticket autobuyer start", async () => { testVSP ) ); - await wait(() => expect(mockUpdateUsedVSPs).toHaveBeenCalledWith(testVSP)); + await waitFor(() => expect(mockUpdateUsedVSPs).toHaveBeenCalledWith(testVSP)); expect(mockStartTicketAutoBuyer).toHaveBeenCalledWith( testTicketBuyerService, { @@ -1721,11 +1723,11 @@ test("test start and stop ticket autobuyer start", async () => { store.dispatch(controlActions.ticketBuyerCancel()); cbs.error("Cancelled"); cbs.end(); - await wait(() => + await waitFor(() => expect(store.getState().vsp.ticketAutoBuyerRunning).toBeFalsy() ); - await wait(() => + await waitFor(() => expect(mockLockAccount).toHaveBeenNthCalledWith( 1, testWalletService, @@ -1757,11 +1759,11 @@ test("test start ticket autobuyer - failed", async () => { expect(mockUpdateUsedVSPs).not.toHaveBeenCalled(); expect(mockSetNeedsVSPdProcessTickets).not.toHaveBeenCalled(); - await wait(() => + await waitFor(() => expect(store.getState().vsp.ticketAutoBuyerRunning).toBeFalsy() ); - await wait(() => + await waitFor(() => expect(mockLockAccount).toHaveBeenNthCalledWith( 1, testWalletService, @@ -1813,7 +1815,7 @@ test("test start ticket autobuyer in a non-private wallet - receive unknown erro ) ); - await wait(() => + await waitFor(() => expect(mockStartTicketAutoBuyer).toHaveBeenCalledWith( testTicketBuyerService, { @@ -1833,11 +1835,11 @@ test("test start ticket autobuyer in a non-private wallet - receive unknown erro store.dispatch(controlActions.ticketBuyerCancel()); cbs.error(testError); - await wait(() => + await waitFor(() => expect(store.getState().vsp.ticketAutoBuyerRunning).toBeFalsy() ); - await wait(() => + await waitFor(() => expect(mockLockAccount).toHaveBeenCalledWith( testWalletService, defaultAccountNumber @@ -1875,7 +1877,7 @@ test("test signMessageAttempt", async () => { "IBkHEpGrzmwUIgl4BVSxgxMbRR2ksKUg1rqTEBOnv8fIMacjUMSg0uRQd8qgp1xLsvR21212W8lgadA7O8h++FY=" ); expect(store.getState().grpc.getSignMessageError).toBeNull(); - await wait(() => + await waitFor(() => expect(mockLockAccount).toHaveBeenCalledWith( testWalletService, defaultAccountNumber @@ -1942,7 +1944,7 @@ test("test signMessageAttempt - signMessage failed", async () => { expect(store.getState().grpc.getSignMessageSignature).toBeNull(); expect(store.getState().grpc.getSignMessageError).toBe(testError); - await wait(() => + await waitFor(() => expect(mockLockAccount).toHaveBeenCalledWith( testWalletService, defaultAccountNumber @@ -2041,6 +2043,7 @@ test("test getNextChangeAddressAttempt - failed", async () => { }); test("test renameAccountAttempt", async () => { + jest.useFakeTimers(); const store = createStore(cloneDeep(initialState)); await store.dispatch( controlActions.renameAccountAttempt( @@ -2049,16 +2052,20 @@ test("test renameAccountAttempt", async () => { ) ); - expect(mockRenameAccount).toHaveBeenCalledWith( - testWalletService, - ticketBuyerAccountNumber, - testNewAccountName - ); + act(() => { + jest.advanceTimersByTime(2001); + }); - await wait(() => - expect(store.getState().control.renameAccountResponse).toBe( - testRenameAccountResponse - ) + await waitFor(() => { + expect(mockRenameAccount).toHaveBeenCalledWith( + testWalletService, + ticketBuyerAccountNumber, + testNewAccountName + ); + }); + + expect(store.getState().control.renameAccountResponse).toBe( + testRenameAccountResponse ); expect(store.getState().control.renameAccountError).toBeNull(); }); @@ -2081,7 +2088,7 @@ test("test renameAccountAttempt - failed", async () => { testNewAccountName ); - await wait(() => + await waitFor(() => expect(store.getState().control.renameAccountError).toBe(testError) ); expect(store.getState().control.renameAccountResponse).toBe(undefined); @@ -2218,7 +2225,7 @@ test("test rescanAttempt - failed", async () => { cbs.error(testError); - await wait(() => expect(catchedError).toBe(testError)); + await waitFor(() => expect(catchedError).toBe(testError)); expect(store.getState().control.rescanCall).toBeNull(); expect(store.getState().control.rescanError).toBe(testError); expect(store.getState().control.rescanRequest).toStrictEqual({ @@ -2326,7 +2333,7 @@ test("test getNextAccountAttempt - getNexsetAccountPassphrase failed", async () expect(store.getState().control.getNextAccountRequestAttempt).toBeFalsy(); expect(store.getState().control.getNextAccountResponse).toBe(undefined); - await wait(() => + await waitFor(() => expect(store.getState().snackbar.messages).not.toBe(undefined) ); expect( @@ -2583,7 +2590,7 @@ test("test signTransactionAttempt publish transaction failed", async () => { testSignedSignature ); - await wait(() => + await waitFor(() => expect( store.getState().control.publishTransactionRequestAttempt ).toBeFalsy() @@ -2613,7 +2620,7 @@ test("test discoverUsageAttempt", async () => { ); expect(mockLoadActiveDataFilters).toHaveBeenCalledWith(testWalletService); - await wait(() => + await waitFor(() => expect(mockRescan).toHaveBeenCalledWith(testWalletService, 0, undefined) ); expect(store.getState().control.discoverUsageAttempt).toBeFalsy(); @@ -2768,7 +2775,7 @@ test("test verifyMessageAttempt - failed", async () => { testMessage, testSignature ); - await wait(() => + await waitFor(() => expect(store.getState().grpc.getVerifyMessageRequestAttempt).toBeFalsy() ); expect(store.getState().grpc.getVerifyMessageError).toBe(testError); @@ -2790,7 +2797,7 @@ test("test publishUnminedTransactionsAttempt", async () => { expect(mockPublishUnminedTransactions).toHaveBeenCalledWith( testWalletService ); - await wait(() => + await waitFor(() => expect(store.getState().snackbar.messages).not.toBe(undefined) ); expect( @@ -2818,7 +2825,7 @@ test("test publishUnminedTransactionsAttempt - failed", async () => { expect(mockPublishUnminedTransactions).toHaveBeenCalledWith( testWalletService ); - await wait(() => + await waitFor(() => expect(store.getState().snackbar.messages).not.toBe(undefined) ); expect(store.getState().snackbar.messages[0].message.defaultMessage).toBe( diff --git a/test/unit/actions/DaemonActions.spec.js b/test/unit/actions/DaemonActions.spec.js index 5b416fb893..0dac84e1c5 100644 --- a/test/unit/actions/DaemonActions.spec.js +++ b/test/unit/actions/DaemonActions.spec.js @@ -56,7 +56,7 @@ import { createStore } from "test-utils.js"; import * as wal from "wallet"; import { act } from "react-dom/test-utils"; import { advanceBy, clear } from "jest-date-mock"; -import { wait } from "@testing-library/react"; +import { waitFor } from "@testing-library/react"; import { preDefinedGradients } from "helpers"; const wallet = wal; @@ -1603,7 +1603,7 @@ test("test syncDaemon", async () => { advanceBy(1000); jest.advanceTimersByTime(1000); }); - await wait(() => expect(mockGetBlockCount).toHaveBeenCalled()); + await waitFor(() => expect(mockGetBlockCount).toHaveBeenCalled()); expect(store.getState().daemon.daemonSynced).toBeFalsy(); expect(store.getState().daemon.currentBlockCount).toBe( testBlockChainInfo.blockCount @@ -1622,7 +1622,7 @@ test("test syncDaemon", async () => { jest.advanceTimersByTime(1000); }); testDateNow += 1000; - await wait(() => expect(mockGetBlockCount).toHaveBeenCalled()); + await waitFor(() => expect(mockGetBlockCount).toHaveBeenCalled()); expect(store.getState().daemon.currentBlockCount).toBe( testBlockChainInfo.blockCount ); @@ -1636,7 +1636,7 @@ test("test syncDaemon", async () => { }); testDateNow += 2000; - await wait(() => expect(mockSetHeightSynced).toHaveBeenCalled()); + await waitFor(() => expect(mockSetHeightSynced).toHaveBeenCalled()); expect(store.getState().daemon.daemonSynced).toBeTruthy(); }); diff --git a/test/unit/components/Balance.spec.js b/test/unit/components/Balance.spec.js index c5a76bf0db..d4573266a1 100644 --- a/test/unit/components/Balance.spec.js +++ b/test/unit/components/Balance.spec.js @@ -36,19 +36,19 @@ test.each([ [DCR, 420000001323, "4,200.00001323"] ])("(%s) %s display '%s'", testDisplay); -test("test click on atom balance", () => { +test("test click on atom balance", async () => { const mockClick = jest.fn(() => {}); mockCurrencyDisplay = ATOMS; render(); - user.click(screen.getByText("42").parentElement); + await user.click(screen.getByText("42").parentElement); expect(mockClick).toHaveBeenCalled(); }); -test("test click on DCR balance", () => { +test("test click on DCR balance", async () => { const mockClick = jest.fn(() => {}); mockCurrencyDisplay = DCR; render(); - user.click(screen.getByText("4.20").parentElement); + await user.click(screen.getByText("4.20").parentElement); expect(mockClick).toHaveBeenCalled(); }); diff --git a/test/unit/components/SideBar/LastBlockTime.spec.js b/test/unit/components/SideBar/LastBlockTime.spec.js index 53d31496de..949a7000f4 100644 --- a/test/unit/components/SideBar/LastBlockTime.spec.js +++ b/test/unit/components/SideBar/LastBlockTime.spec.js @@ -1,10 +1,7 @@ import { LastBlockTime } from "SideBar/MenuBottom/LastBlockTime/LastBlockTime"; -import { mount } from "enzyme"; import { advanceBy, clear } from "jest-date-mock"; -import { IntlProvider, FormattedMessage } from "react-intl"; -import { FormattedRelative } from "shared"; -import { en as enLocale, defaultFormats } from "../../../../app/i18n/locales"; -import { render, wait } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; +import { render } from "test-utils.js"; import "@testing-library/jest-dom/extend-expect"; import { act } from "react-dom/test-utils"; @@ -13,55 +10,30 @@ afterEach(() => { jest.useRealTimers(); }); -// en -const locale = enLocale; - -const Wrapper = ({ lastBlockTimestamp, setTimeout, clearTimeout }) => { - return ( - - - - ); -}; - test("Recent mined block time displays correctly", () => { const now = new Date().getTime() / 1000 - 1; - const lbt = mount( - {}} clearTimeout={() => {}} /> ); - expect( - lbt.find(LastBlockTime).find(FormattedMessage).prop("defaultMessage") - ).toBe("seconds ago"); + expect(screen.getByText("seconds ago")).toBeInTheDocument(); }); test("Old mined block time displays correctly", () => { const now = new Date().getTime() / 1000 - 86400; - const lbt = mount( - {}} clearTimeout={() => {}} /> ); - const targetDate = new Date(now * 1000); - expect(lbt.find(LastBlockTime).find(FormattedRelative).prop("value")).toEqual( - targetDate - ); + expect(screen.getByText("yesterday")).toBeInTheDocument(); }); test("Block time updates after a minute", async () => { @@ -69,7 +41,7 @@ test("Block time updates after a minute", async () => { jest.useFakeTimers(); const { getByText, queryByText } = render( - {}} @@ -84,18 +56,20 @@ test("Block time updates after a minute", async () => { jest.advanceTimersByTime(61 * 1000); }); - await wait(() => expect(queryByText("seconds ago")).not.toBeInTheDocument()); - await wait(() => expect(queryByText("1 minute ago")).toBeInTheDocument()); + await waitFor(() => + expect(queryByText("seconds ago")).not.toBeInTheDocument() + ); + await waitFor(() => expect(queryByText("1 minute ago")).toBeInTheDocument()); }); test("Empty timestamp returns null", () => { - const lbt = mount( - {}} clearTimeout={() => {}} /> ); - expect(lbt.find(LastBlockTime).children().exists()).toEqual(false); + expect(container.textContent).toBe(""); }); diff --git a/test/unit/components/SideBar/Sidebar.spec.js b/test/unit/components/SideBar/Sidebar.spec.js index 8fa654e536..89a2cc428e 100644 --- a/test/unit/components/SideBar/Sidebar.spec.js +++ b/test/unit/components/SideBar/Sidebar.spec.js @@ -1,8 +1,6 @@ import SideBar from "components/SideBar/SideBar"; import { render } from "test-utils.js"; -import "@testing-library/jest-dom/extend-expect"; -import user from "@testing-library/user-event"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor, fireEvent } from "@testing-library/react"; import * as sel from "selectors"; import { rescanAttempt as mockRescanAttempt, @@ -41,7 +39,7 @@ jest.mock("react-router-dom", () => ({ })); const defaultMenuLinkBorderColor = "border-color: rgba(249, 250, 250, 1)"; //sidebar-color -const activeMenuLinkBorderColor = "border-color: rgba(46, 216, 163, 1)"; +const activeMenuLinkBorderColor = "border-color: #2ed8a3"; const testCurrentBlockHeight = 12; const testBalances = [ { @@ -83,13 +81,12 @@ const getMenuContentByTestId = (testId, sidebarOnBottom, expandSideBar) => { menuLinkContent }; }; -const expectToHaveDefaultMenuLinks = async (params) => { +const expectToHaveDefaultMenuLinks = async (user, params) => { const { sidebarOnBottom, isTrezorEnabled, isLnEnabled = true, - expandSideBar, - isSPV = false + expandSideBar } = params || {}; const expectToHaveMenuLink = async ( @@ -115,16 +112,14 @@ const expectToHaveDefaultMenuLinks = async (params) => { expect(menuLinkContent.firstChild).toHaveClass(className); // test clicking expect(menuLink).toHaveStyle(defaultMenuLinkBorderColor); - user.click(menuLink); - if (disabled) { - await wait(() => - expect(menuLink).toHaveStyle(defaultMenuLinkBorderColor) - ); - expect(mockHistoryPush).not.toHaveBeenCalledWith(path); - } else { - await wait(() => expect(menuLink).toHaveStyle(activeMenuLinkBorderColor)); - expect(mockHistoryPush).toHaveBeenCalledWith(path); - } + fireEvent.click(menuLink); + await waitFor(() => { + if (disabled) { + expect(mockHistoryPush).not.toHaveBeenCalledWith(path); + } else { + expect(mockHistoryPush).toHaveBeenCalledWith(path); + } + }); }; await expectToHaveMenuLink( @@ -141,21 +136,7 @@ const expectToHaveDefaultMenuLinks = async (params) => { "/transactions", "On-chain Transactions" ); - await expectToHaveMenuLink( - "menuLinkContent-governance", - "Governance", - "governanceIcon", - "/governance", - "Governance" - ); if (!sidebarOnBottom || expandSideBar) { - await expectToHaveMenuLink( - "menuLinkContent-tickets", - "Staking", - "ticketsIcon", - "/tickets", - "Staking" - ); await expectToHaveMenuLink( "menuLinkContent-accounts", "Accounts", @@ -180,10 +161,21 @@ const expectToHaveDefaultMenuLinks = async (params) => { "DEX", "dexIcon", "/dex", - isSPV - ? "DEX not available while using SPV. Please go to settings and disable SPV to access the DEX." - : "DEX", - isSPV + "DEX" + ); + await expectToHaveMenuLink( + "menuLinkContent-tickets", + "Staking", + "ticketsIcon", + "/tickets", + "Staking" + ); + await expectToHaveMenuLink( + "menuLinkContent-governance", + "Governance", + "governanceIcon", + "/governance", + "Governance" ); } } @@ -200,9 +192,9 @@ const expectToHaveDefaultMenuLinks = async (params) => { } }; -test("renders default sidebar", () => { - render(); - expectToHaveDefaultMenuLinks({ +test("render default sidebar", async () => { + const { user } = render(); + expectToHaveDefaultMenuLinks(user, { sidebarOnBottom: false, expandSideBar: false }); @@ -228,7 +220,7 @@ test("renders default sidebar", () => { expect(screen.queryByText(/total balance/i)).not.toBeInTheDocument(); // expands the sidebar - user.click(screen.queryByRole("button", { name: /logo/i })); + await user.click(screen.queryByRole("button", { name: /logo/i })); expect(screen.queryByRole("button", { name: /reduce sidebar/i })).toHaveClass( "reducedArrow" @@ -240,7 +232,9 @@ test("renders default sidebar", () => { const accountList = screen.getByTestId("account-list"); expect(accountList).toHaveClass("extended"); user.hover(screen.getByText(/total balance/i)); - expect(accountList).toHaveClass("extended showingAccounts"); + await waitFor(() => + expect(accountList).toHaveClass("extended showingAccounts") + ); // checks AccountNames testBalances.map((balance) => { if (!balance["hidden"]) { @@ -254,11 +248,13 @@ test("renders default sidebar", () => { } }); - user.unhover(screen.getByText(/total balance/i)); - expect(accountList).not.toHaveClass("extended showingAccounts"); + await user.unhover(screen.getByText(/total balance/i)); + await waitFor(() => + expect(accountList).not.toHaveClass("extended showingAccounts") + ); // collapses the sidebar - user.click(screen.getByRole("button", { name: /reduce sidebar/i })); + await user.click(screen.getByRole("button", { name: /reduce sidebar/i })); expect( screen.queryByRole("button", { name: /reduce sidebar/i }) @@ -268,11 +264,11 @@ test("renders default sidebar", () => { expect(mockBalances).toHaveBeenCalled(); }); -test("renders sidebar on the bottom", () => { +test("renders sidebar on the bottom", async () => { const mockSidebarOnBottom = (selectors.sidebarOnBottom = jest.fn(() => true)); - render(); + const { user } = render(); - expectToHaveDefaultMenuLinks({ + await expectToHaveDefaultMenuLinks(user, { sidebarOnBottom: true }); expect( @@ -283,7 +279,7 @@ test("renders sidebar on the bottom", () => { expect(screen.queryByText(/total balance/i)).not.toBeInTheDocument(); // expands the sidebar - user.click(screen.queryByRole("button", { name: /logo/i })); + await user.click(screen.queryByRole("button", { name: /logo/i })); expect(screen.getByRole("button", { name: /reduce sidebar/i })).toHaveClass( "reducedArrow" @@ -292,7 +288,7 @@ test("renders sidebar on the bottom", () => { expect(screen.getByText(/total balance/i)).toBeInTheDocument(); // collapses the sidebar - user.click(screen.getByRole("button", { name: /reduce sidebar/i })); + await user.click(screen.getByRole("button", { name: /reduce sidebar/i })); expect( screen.queryByRole("button", { @@ -305,10 +301,12 @@ test("renders sidebar on the bottom", () => { mockSidebarOnBottom.mockRestore(); }); -test("renders sidebar with trezor enabled, should not find trezor menu, it have been moved to a separate tab under settings", () => { +test("renders sidebar with trezor enabled, should not find trezor menu,\ + it have been moved to a separate tab under settings. Governance and Tickets\ + should be hidden.", async () => { const mockIsTrezor = (selectors.isTrezor = jest.fn(() => true)); - render(); - expectToHaveDefaultMenuLinks({ + const { user } = render(); + await expectToHaveDefaultMenuLinks(user, { isTrezorEnabled: true }); @@ -316,11 +314,11 @@ test("renders sidebar with trezor enabled, should not find trezor menu, it have mockIsTrezor.mockRestore(); }); -test("renders sidebar with lightning network not enabled", () => { +test("renders sidebar with lightning network not enabled", async () => { const mockLnEnabled = (selectors.lnEnabled = jest.fn(() => false)); - render(); - expectToHaveDefaultMenuLinks({ + const { user } = render(); + await expectToHaveDefaultMenuLinks(user, { isLnEnabled: false }); @@ -328,7 +326,7 @@ test("renders sidebar with lightning network not enabled", () => { mockLnEnabled.mockRestore(); }); -test("renders expanded sidebar with testnet network enabled", () => { +test("renders expanded sidebar with testnet network enabled", async () => { const mockIsTestNet = (selectors.isTestNet = jest.fn(() => true)); const mockExpandSideBar = (selectors.expandSideBar = jest.fn(() => true)); const mockSidebarOnBottom = (selectors.sidebarOnBottom = jest.fn( @@ -336,8 +334,8 @@ test("renders expanded sidebar with testnet network enabled", () => { )); const mockLnEnabled = (selectors.lnEnabled = jest.fn(() => true)); - render(); - expectToHaveDefaultMenuLinks({ + const { user } = render(); + await expectToHaveDefaultMenuLinks(user, { sidebarOnBottom: false, expandSideBar: true }); @@ -351,22 +349,10 @@ test("renders expanded sidebar with testnet network enabled", () => { mockExpandSideBar.mockRestore(); }); -test("renders sidebar with SPV enabled. DEX should be disabled", () => { - const mockIsSPV = (selectors.isSPV = jest.fn(() => true)); - - render(); - expectToHaveDefaultMenuLinks({ - isSPV: true - }); - - expect(mockIsSPV).toHaveBeenCalled(); - mockIsSPV.mockRestore(); -}); - test("tests rescan on the expanded sidebar", async () => { const mockExpandSideBar = (selectors.expandSideBar = jest.fn(() => true)); - render(, { + const { user } = render(, { initialState: { grpc: { currentBlockHeight: testCurrentBlockHeight, @@ -375,7 +361,7 @@ test("tests rescan on the expanded sidebar", async () => { } }); - await wait(() => + await waitFor(() => expect(screen.getByText(/seconds ago/i)).toBeInTheDocument() ); expect( @@ -393,23 +379,24 @@ test("tests rescan on the expanded sidebar", async () => { name: /^rescan$/i }) ); - expect(mockRescanAttempt).toHaveBeenCalledTimes(1); - - expect( - screen.getByRole("button", { - name: /^rescan$/i - }) - ).toHaveClass("rescan syncing"); + await waitFor(() => { + expect(mockRescanAttempt).toHaveBeenCalledTimes(1); + expect( + screen.getByRole("button", { name: /cancel rescan/i }) + ).toBeInTheDocument(); + expect( + screen.getByRole("button", { + name: /^rescan$/i + }) + ).toHaveClass("rescan", "syncing"); + }); - expect( - screen.getByRole("button", { name: /cancel rescan/i }) - ).toBeInTheDocument(); expect(screen.queryByText(/seconds ago/i)).not.toBeInTheDocument(); expect(screen.getByText(/^Rescanning/)).toBeInTheDocument(); expect(screen.getByText(`0/${testCurrentBlockHeight}`)).toBeInTheDocument(); expect(screen.getByText(/(0%)/i)).toBeInTheDocument(); - user.click(screen.getByRole("button", { name: /cancel rescan/i })); + await user.click(screen.getByRole("button", { name: /cancel rescan/i })); expect(mockRescanCancel).toHaveBeenCalledTimes(1); expect( @@ -425,10 +412,10 @@ test("tests rescan on the expanded sidebar", async () => { mockExpandSideBar.mockRestore(); }); -test("tests rescan on the collapsed sidebar", () => { +test("tests rescan on the collapsed sidebar", async () => { const mockExpandSideBar = (selectors.expandSideBar = jest.fn(() => false)); - render(, { + const { user } = render(, { initialState: { grpc: { currentBlockHeight: testCurrentBlockHeight, @@ -446,7 +433,7 @@ test("tests rescan on the collapsed sidebar", () => { screen.queryByRole("button", { name: /cancel rescan/i }) ).not.toBeInTheDocument(); - user.click(screen.getByRole("button", { name: /^rescan$/i })); + await user.click(screen.getByRole("button", { name: /^rescan$/i })); expect(mockRescanAttempt).toHaveBeenCalledTimes(1); @@ -459,7 +446,7 @@ test("tests rescan on the collapsed sidebar", () => { screen.getByRole("button", { name: /cancel rescan/i }) ).toBeInTheDocument(); - user.click(screen.getByRole("button", { name: /cancel rescan/i })); + await user.click(screen.getByRole("button", { name: /cancel rescan/i })); expect(mockRescanCancel).toHaveBeenCalledTimes(1); expect( @@ -534,15 +521,7 @@ test("tests notification icon on the menu link (newNotYetVotedActiveProposalsCou mockNewNotYetVotedActiveProposalsCount.mockRestore(); }); -test("tests tabbedPage location", async () => { - const { history } = render(); - const { menuLink } = getMenuContentByTestId("menuLinkContent-transactions"); - expect(menuLink).toHaveStyle(defaultMenuLinkBorderColor); - history.push("transactions/send"); - await wait(() => expect(menuLink).toHaveStyle(activeMenuLinkBorderColor)); -}); - -test("none of the menu links should be selected when clicking on the settings button", () => { +test("none of the menu links should be selected when clicking on the settings button", async () => { render(); let menuLinkContents = screen.getAllByTestId(/menuLinkContent-/i); menuLinkContents.map((menuLinkContent) => { @@ -550,26 +529,30 @@ test("none of the menu links should be selected when clicking on the settings bu expect(menuLink).toHaveStyle(defaultMenuLinkBorderColor); }); const { menuLink } = getMenuContentByTestId("menuLinkContent-tickets"); - user.click(menuLink); + fireEvent.click(menuLink); // click on Staking menuLinkContents = screen.getAllByTestId(/menuLinkContent-/i); menuLinkContents.forEach(async (menuLinkContent) => { const menuLink = menuLinkContent.parentNode.parentNode.parentNode; if (menuLink.textContent == "Staking") { - await wait(() => expect(menuLink).toHaveStyle(activeMenuLinkBorderColor)); + await waitFor(() => + expect(menuLink).toHaveStyle(activeMenuLinkBorderColor) + ); } else { - await wait(() => + await waitFor(() => expect(menuLink).toHaveStyle(defaultMenuLinkBorderColor) ); } }); // click on settings - user.click(screen.getByRole("link", { name: "settings" })); + fireEvent.click(screen.getByRole("link", { name: "settings" })); menuLinkContents = screen.getAllByTestId(/menuLinkContent-/i); - menuLinkContents.map((menuLinkContent) => { - const menuLink = menuLinkContent.parentNode.parentNode.parentNode; - expect(menuLink).toHaveStyle(defaultMenuLinkBorderColor); - }); + await waitFor(() => + menuLinkContents.map((menuLinkContent) => { + const menuLink = menuLinkContent.parentNode.parentNode.parentNode; + expect(menuLink).toHaveStyle(defaultMenuLinkBorderColor); + }) + ); }); diff --git a/test/unit/components/SideBar/SidebarTabbedPage.spec.js b/test/unit/components/SideBar/SidebarTabbedPage.spec.js new file mode 100644 index 0000000000..b220eaa756 --- /dev/null +++ b/test/unit/components/SideBar/SidebarTabbedPage.spec.js @@ -0,0 +1,27 @@ +import SideBar from "components/SideBar/SideBar"; +import { render } from "test-utils.js"; +import { screen, waitFor, act } from "@testing-library/react"; + +const defaultMenuLinkBorderColor = "border-color: rgba(249, 250, 250, 1)"; //sidebar-color +const activeMenuLinkBorderColor = "border-color: #2ed8a3"; +const getMenuContentByTestId = (testId, sidebarOnBottom, expandSideBar) => { + const menuLinkContent = screen.getByTestId(testId); + let menuLink = menuLinkContent.parentNode.parentNode.parentNode; + if (!sidebarOnBottom && expandSideBar) { + menuLink = menuLinkContent.parentNode.parentNode; + } + return { + menuLink, + menuLinkContent + }; +}; + +test("tests tabbedPage location", async () => { + const { history } = render(); + const { menuLink } = getMenuContentByTestId("menuLinkContent-transactions"); + expect(menuLink).toHaveStyle(defaultMenuLinkBorderColor); + act(() => { + history.push("transactions/send"); + }); + await waitFor(() => expect(menuLink).toHaveStyle(activeMenuLinkBorderColor)); +}); diff --git a/test/unit/components/Snackbar/Snackbar.spec.js b/test/unit/components/Snackbar/Snackbar.spec.js index 630f328f6e..c9933716a5 100644 --- a/test/unit/components/Snackbar/Snackbar.spec.js +++ b/test/unit/components/Snackbar/Snackbar.spec.js @@ -1,7 +1,7 @@ import Snackbar from "components/Snackbar"; import { render } from "test-utils.js"; import user from "@testing-library/user-event"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; import { useDispatch } from "react-redux"; import { WatchOnlyWarnNotification } from "shared"; import * as sel from "selectors"; @@ -75,7 +75,7 @@ test("test WatchOnlyWarnNotification without animation", async () => { ); user.click(screen.getByText("watch-only-warn-notification")); - await wait(() => + await waitFor(() => expect(screen.getByTestId("snackbar-message").textContent).toMatch( "This functionality is disabled for watch-only Wallets" ) @@ -107,7 +107,7 @@ test("test a newlyMinedTransaction snackbar message", async () => { ); user.click(screen.getByTestId("snackbarSenderButton")); - await wait(() => screen.getByTestId("transaction-notification")); + await waitFor(() => screen.getByTestId("transaction-notification")); expect(screen.getByText(String(testFee))).toBeInTheDocument(); expect(screen.getByText(String(testAmount))).toBeInTheDocument(); expect(screen.getAllByText(testTxHash).length).toBe(2); //one link and one tooltip @@ -141,7 +141,7 @@ test("test a newlyUnminedTransaction(Voted) snackbar message without direction a ); user.click(screen.getByTestId("snackbarSenderButton")); - await wait(() => screen.getByTestId("transaction-notification")); + await waitFor(() => screen.getByTestId("transaction-notification")); expect(screen.getByText(String(testFee))).toBeInTheDocument(); expect(screen.getByText(String(testAmount))).toBeInTheDocument(); expect(screen.getAllByText(testTxHash).length).toBe(2); //one link and one tooltip @@ -159,47 +159,48 @@ test("test multi notification", async () => { ); const snackbarSenderButton = screen.getByTestId("snackbarSenderButton"); user.click(snackbarSenderButton); - await wait(() => + await waitFor(() => expect(screen.getByTestId("snackbar-message")).toBeInTheDocument() ); expect(mockUiAnimations).toHaveBeenCalled(); user.click(snackbarSenderButton); - await wait(() => + await waitFor(() => expect(screen.getAllByTestId("snackbar-message").length).toBe(2) ); //close one of the snackbar user.click(screen.getByRole("button", { name: "Close" })); - await wait(() => + await waitFor(() => expect(screen.getAllByTestId("snackbar-message").length).toBe(1) ); //close the rest user.click(screen.getByRole("button", { name: "Close" })); - await wait(() => + await waitFor(() => expect(screen.queryByTestId("snackbar-message")).not.toBeInTheDocument() ); - //open again two snackbar notification and wait until they're disappearing + //open again two snackbar notification and waitFor until they're disappearing jest.useFakeTimers(); user.click(snackbarSenderButton); user.click(snackbarSenderButton); - await wait(() => - expect(screen.getAllByTestId("snackbar-message").length).toBe(2) + user.click(snackbarSenderButton); + await waitFor(() => + expect(screen.getAllByTestId("snackbar-message").length).toBe(3) ); // simulate that 10 * 500 seconds have passed act(() => { jest.advanceTimersByTime(5000); }); - await wait(() => - expect(screen.getAllByTestId("snackbar-message").length).toBe(1) + await waitFor(() => + expect(screen.getAllByTestId("snackbar-message").length).toBe(2) ); // simulate that 10 * 500 seconds have passed act(() => { jest.advanceTimersByTime(5000); }); - await wait(() => - expect(screen.queryByTestId("snackbar-message")).not.toBeInTheDocument() + await waitFor(() => + expect(screen.getAllByTestId("snackbar-message").length).toBe(1) ); }); @@ -211,7 +212,7 @@ const testMessage = async (sender, expectedMessage) => { ); user.click(screen.getByTestId("snackbarSenderButton")); - await wait(() => + await waitFor(() => expect(screen.getByTestId("snackbar-message").textContent).toMatch( expectedMessage ) diff --git a/test/unit/components/buttons/Buttons.spec.js b/test/unit/components/buttons/Buttons.spec.js index b5dbcaa316..081edd3497 100644 --- a/test/unit/components/buttons/Buttons.spec.js +++ b/test/unit/components/buttons/Buttons.spec.js @@ -7,7 +7,6 @@ import { SlateGrayButton } from "buttons"; import { render } from "test-utils.js"; -import user from "@testing-library/user-event"; import { screen } from "@testing-library/react"; const testClassName = "test-class-name"; @@ -18,8 +17,8 @@ let mockOnClick; beforeEach(() => { mockOnClick = jest.fn(() => {}); }); -test("render default Button", () => { - render( +test("render default Button", async () => { + const { user } = render( @@ -30,19 +29,19 @@ test("render default Button", () => { expect(button.className).toMatch(testClassName); expect(button.style.margin).toMatch(testStyle.margin); expect(button.disabled).toBe(false); - user.click(button); + await user.click(button); expect(mockOnClick).toHaveBeenCalled(); }); -test("render disabled Button", () => { - render( +test("render disabled Button", async () => { + const { user } = render( ); const button = screen.getByRole("button"); - user.click(button); + await user.click(button); expect(button.disabled).toBe(true); expect(mockOnClick).not.toHaveBeenCalled(); }); diff --git a/test/unit/components/buttons/CopyToClipboardButton.spec.js b/test/unit/components/buttons/CopyToClipboardButton.spec.js index 19fe78f432..ab473edf79 100644 --- a/test/unit/components/buttons/CopyToClipboardButton.spec.js +++ b/test/unit/components/buttons/CopyToClipboardButton.spec.js @@ -1,6 +1,5 @@ import { CopyToClipboardButton } from "buttons"; import { render } from "test-utils.js"; -import user from "@testing-library/user-event"; import { screen } from "@testing-library/react"; import copy from "clipboard-copy"; import { fireEvent } from "@testing-library/react"; @@ -15,8 +14,8 @@ beforeEach(() => { mockCopy = copy.mockImplementation(() => true); }); -test("test CopyToClipboardButton", () => { - render( +test("test CopyToClipboardButton", async () => { + const { user } = render( { const copyIcon = screen.getByRole("button", { name: "Copy" }); // success indicator should appear after successful copy - user.click(copyIcon); + await user.click(copyIcon); expect(mockCopy).toHaveBeenCalledWith(testTextToCopy); expect(copied.className).not.toMatch("hidden"); diff --git a/test/unit/components/buttons/SendTransactionButton.spec.js b/test/unit/components/buttons/SendTransactionButton.spec.js index badecc48c7..36f9871007 100644 --- a/test/unit/components/buttons/SendTransactionButton.spec.js +++ b/test/unit/components/buttons/SendTransactionButton.spec.js @@ -1,6 +1,5 @@ import { SendTransactionButton } from "buttons"; -import { render, wait } from "test-utils.js"; -import user from "@testing-library/user-event"; +import { render, waitFor } from "test-utils.js"; import { screen } from "@testing-library/react"; import * as tza from "actions/TrezorActions"; import * as ca from "actions/ControlActions"; @@ -42,23 +41,25 @@ beforeEach(() => { selectors.isTrezor = jest.fn(() => false); }); -test("render default SendTransactionButton ", () => { - render(); +test("render default SendTransactionButton ", async () => { + const { user } = render( + + ); expect(screen.getByText(/send/i)).toBeInTheDocument(); const button = screen.getByRole("button"); - user.click(button); + await user.click(button); expect(screen.getByText(/transaction confirmation/i)).toBeInTheDocument(); // cancel modal first - user.click(screen.getByText(/cancel/i)); + await user.click(screen.getByText(/cancel/i)); expect( screen.queryByText(/transaction confirmation/i) ).not.toBeInTheDocument(); // try again - user.click(button); + await user.click(button); const testPassPhrase = "test-pf"; - user.type(screen.getByLabelText(/private passphrase/i), testPassPhrase); - user.click(screen.getByText(/continue/i)); + await user.type(screen.getByLabelText(/private passphrase/i), testPassPhrase); + await user.click(screen.getByText(/continue/i)); expect(mockSignTransactionAttempt).toHaveBeenCalledWith( testPassPhrase, testUnsignedTransaction, @@ -75,40 +76,40 @@ test("render default SendTransactionButton ", () => { test("render SendTransactionButton when trezor is enabled", async () => { selectors.isTrezor = jest.fn(() => true); - render(); + const { user } = render(); expect(screen.getByText(/send/i)).toBeInTheDocument(); const button = screen.getByRole("button"); - user.click(button); + await user.click(button); expect(mockSignTransactionAttempt).not.toHaveBeenCalled(); expect(mockSignTransactionAttemptTrezor).toHaveBeenCalledWith( testUnsignedTransaction, testConstructTxResponse ); - await wait(() => expect(mockOnSubmit).toHaveBeenCalled()); + await waitFor(() => expect(mockOnSubmit).toHaveBeenCalled()); }); -test("render loading default SendTransactionButton ", () => { +test("render loading default SendTransactionButton ", async () => { mockIsSendingTransaction = selectors.isSendingTransaction = jest.fn( () => true ); - render(); + const { user } = render(); expect(screen.queryByText(/send/i)).not.toBeInTheDocument(); const button = screen.getByRole("button"); - user.click(button); + await user.click(button); expect(button.disabled).toBe(true); expect(mockIsSendingTransaction).toHaveBeenCalled(); expect(mockSignTransactionAttempt).not.toHaveBeenCalled(); expect(mockSignTransactionAttemptTrezor).not.toHaveBeenCalled(); }); -test("render loading SendTransactionButton when trezor is enabled", () => { +test("render loading SendTransactionButton when trezor is enabled", async () => { mockIsSendingTransaction = selectors.isSendingTransaction = jest.fn( () => true ); - render(); + const { user } = render(); expect(screen.queryByText(/send/i)).not.toBeInTheDocument(); const button = screen.getByRole("button"); - user.click(button); + await user.click(button); expect(button.disabled).toBe(true); expect(mockIsSendingTransaction).toHaveBeenCalled(); expect(mockSignTransactionAttempt).not.toHaveBeenCalled(); diff --git a/test/unit/components/buttons/TextToggle.spec.js b/test/unit/components/buttons/TextToggle.spec.js index 4c34f8525b..6d237f86dc 100644 --- a/test/unit/components/buttons/TextToggle.spec.js +++ b/test/unit/components/buttons/TextToggle.spec.js @@ -1,6 +1,5 @@ import { TextToggle } from "buttons"; import { render } from "test-utils.js"; -import user from "@testing-library/user-event"; import { screen } from "@testing-library/react"; const testLeftText = "test-left-text"; @@ -9,8 +8,8 @@ const mockToggleAction = jest.fn(() => {}); const testClassName = "test-class-name"; const testChildClassName = "test-child-class-name"; -test("render TextToggle with left active button", () => { - render( +test("render TextToggle with left active button", async () => { + const { user } = render( { expect(leftText.className).toMatch(testChildClassName); expect(rightText.className).toMatch(testChildClassName); - user.click(rightText); + await user.click(rightText); expect(mockToggleAction).toHaveBeenCalledWith("right"); expect(leftText.className).not.toMatch(/active/i); expect(rightText.className).toMatch(/active/i); - user.click(leftText); + await user.click(leftText); expect(mockToggleAction).toHaveBeenCalledWith("left"); expect(leftText.className).toMatch(/active/i); expect(rightText.className).not.toMatch(/active/i); diff --git a/test/unit/components/indicators/AnimatedLinearProgressFull.spec.js b/test/unit/components/indicators/AnimatedLinearProgressFull.spec.js index 0647ad6ced..a4aba5d8ef 100644 --- a/test/unit/components/indicators/AnimatedLinearProgressFull.spec.js +++ b/test/unit/components/indicators/AnimatedLinearProgressFull.spec.js @@ -4,7 +4,7 @@ import { screen } from "@testing-library/react"; import * as sel from "selectors"; import * as da from "actions/DaemonActions"; import { act } from "react-dom/test-utils"; -import { wait } from "@testing-library/react"; +import { waitFor } from "@testing-library/react"; const testProps = { min: 0, @@ -165,7 +165,7 @@ test("dcrwallet log line is shown or log the error to console", async () => { act(() => { jest.advanceTimersByTime(2001); }); - await wait(() => + await waitFor(() => expect(screen.getByText(testDcrwalletLogLine)).toBeInTheDocument() ); @@ -176,7 +176,7 @@ test("dcrwallet log line is shown or log the error to console", async () => { act(() => { jest.advanceTimersByTime(2001); }); - await wait(() => expect(mockConsoleLog).toHaveBeenCalled()); + await waitFor(() => expect(mockConsoleLog).toHaveBeenCalled()); mockGetDcrdLastLineLogs.mockRestore(); mockConsoleLog.mockRestore(); diff --git a/test/unit/components/indicators/Indicators.spec.js b/test/unit/components/indicators/Indicators.spec.js index 83985d10ad..65488e9990 100644 --- a/test/unit/components/indicators/Indicators.spec.js +++ b/test/unit/components/indicators/Indicators.spec.js @@ -72,7 +72,7 @@ test("render StakeyBounceXs", () => { expect(stakeyBounceXs.firstElementChild.className).toMatch("stakeyBounce"); }); -test("render StepIndicator", () => { +test("render StepIndicator", async () => { const mockOnGotoPage = jest.fn(() => {}); const testCurrentPageIndex = 2; const testPageCount = 4; @@ -99,7 +99,7 @@ test("render StepIndicator", () => { } }); - user.click(uncheckedStepIndicator); + await user.click(uncheckedStepIndicator); expect(mockOnGotoPage).toHaveBeenCalledWith(uncheckedStepIndicatorIndex); }); diff --git a/test/unit/components/indicators/LoadingMoreTickets.spec.js b/test/unit/components/indicators/LoadingMoreTickets.spec.js index ae608e32a9..f4ffa2551c 100644 --- a/test/unit/components/indicators/LoadingMoreTickets.spec.js +++ b/test/unit/components/indicators/LoadingMoreTickets.spec.js @@ -1,7 +1,6 @@ import { LoadingMoreTicketsIndicator } from "indicators"; import { render } from "test-utils.js"; -import user from "@testing-library/user-event"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; import * as sel from "selectors"; let mockStartRequestHeight; @@ -32,7 +31,9 @@ beforeEach(() => { }); test("test default LoadingMoreTickets", async () => { - render(); + const { user } = render( + + ); expect(mockStartRequestHeight).toHaveBeenCalled(); expect(mockTicketsFilter).toHaveBeenCalled(); @@ -48,13 +49,13 @@ test("test default LoadingMoreTickets", async () => { expect(screen.getByText("Cancel listing tickets")).toBeInTheDocument(); /* cancel listing Tickets */ - user.click(screen.getByRole("button")); - await wait(() => screen.getByText(/loading more tickets canceled/i)); + await user.click(screen.getByRole("button")); + await waitFor(() => screen.getByText(/loading more tickets canceled/i)); expect(screen.getByText("Return listing tickets")).toBeInTheDocument(); /* reenable listing Tickets */ - user.click(screen.getByRole("button")); - await wait(() => screen.getByText(/loading more tickets/i)); + await user.click(screen.getByRole("button")); + await waitFor(() => screen.getByText(/loading more tickets/i)); expect(screen.getByText("Cancel listing tickets")).toBeInTheDocument(); }); diff --git a/test/unit/components/inputs/AccountsSelect.spec.js b/test/unit/components/inputs/AccountsSelect.spec.js index 6b0f488a6d..170e475e00 100644 --- a/test/unit/components/inputs/AccountsSelect.spec.js +++ b/test/unit/components/inputs/AccountsSelect.spec.js @@ -1,6 +1,5 @@ import { AccountsSelect } from "inputs"; import { render } from "test-utils.js"; -import user from "@testing-library/user-event"; import { screen } from "@testing-library/react"; import * as sel from "selectors"; import { DCR } from "constants"; @@ -76,47 +75,47 @@ test("render empty AccountsSelect with accounts buttons", () => { expect(screen.getByText("Accounts")).toBeInTheDocument(); }); -test("render AccountsSelect (visible accounts)", () => { - render( +test("render AccountsSelect (visible accounts)", async () => { + const { user } = render( ); expect(screen.queryByText(/select account/i)).not.toBeInTheDocument(); const accountsSelect = screen.getByTestId("accountsSelect"); expect(accountsSelect.className).not.toMatch(testClassName); - user.click(screen.getByText(mockDefaultAccount.name)); + await user.click(screen.getByText(mockDefaultAccount.name)); expect(screen.getByText(mockUnmixedAccount.name)).toBeInTheDocument(); expect(screen.getByText(mockMixedAccount.name)).toBeInTheDocument(); expect(screen.getByText(mockAccount2.name)).toBeInTheDocument(); - user.click(screen.getByText(mockMixedAccount.name)); + await user.click(screen.getByText(mockMixedAccount.name)); expect(mockOnChange).toHaveBeenCalledWith(mockMixedAccount); expect(screen.queryByText(mockUnmixedAccount.name)).not.toBeInTheDocument(); expect(screen.queryByText(mockMixedAccount.name)).not.toBeInTheDocument(); expect(screen.queryByText(mockAccount2.name)).not.toBeInTheDocument(); }); -test("render AccountsSelect (spendable accounts)", () => { +test("render AccountsSelect (spendable accounts)", async () => { mockMixedAccountValue = null; - render( + const { user } = render( ); expect(screen.queryByText(/select account/i)).not.toBeInTheDocument(); const accountsSelect = screen.getByTestId("accountsSelect"); expect(accountsSelect.className).not.toMatch(testClassName); - user.click(screen.getByText(mockDefaultAccount.name)); + await user.click(screen.getByText(mockDefaultAccount.name)); expect(screen.getByText(mockUnmixedAccount.name)).toBeInTheDocument(); expect(screen.queryByText(mockMixedAccount.name)).not.toBeInTheDocument(); expect(screen.getByText(mockAccount2.name)).toBeInTheDocument(); - user.click(screen.getByText(mockAccount2.name)); + await user.click(screen.getByText(mockAccount2.name)); expect(mockOnChange).toHaveBeenCalledWith(mockAccount2); expect(screen.queryByText(mockUnmixedAccount.name)).not.toBeInTheDocument(); expect(screen.queryByText(mockMixedAccount.name)).not.toBeInTheDocument(); expect(screen.queryByText(mockAccount2.name)).not.toBeInTheDocument(); }); -test("render AccountsSelect (filtered accounts)", () => { - render( +test("render AccountsSelect (filtered accounts)", async () => { + const { user } = render( { expect(screen.queryByText(/select account/i)).not.toBeInTheDocument(); const accountsSelect = screen.getByTestId("accountsSelect"); expect(accountsSelect.className).not.toMatch(testClassName); - user.click(screen.getByText(mockDefaultAccount.name)); + await user.click(screen.getByText(mockDefaultAccount.name)); expect(screen.queryByText(mockUnmixedAccount.name)).not.toBeInTheDocument(); expect(screen.queryByText(mockMixedAccount.name)).not.toBeInTheDocument(); expect(screen.getByText(mockAccount2.name)).toBeInTheDocument(); - user.click(screen.getByText(mockAccount2.name)); + await user.click(screen.getByText(mockAccount2.name)); expect(mockOnChange).toHaveBeenCalledWith(mockAccount2); expect(screen.queryByText(mockUnmixedAccount.name)).not.toBeInTheDocument(); expect(screen.queryByText(mockMixedAccount.name)).not.toBeInTheDocument(); diff --git a/test/unit/components/inputs/Inputs.spec.js b/test/unit/components/inputs/Inputs.spec.js index c603e9fea9..8d38f21216 100644 --- a/test/unit/components/inputs/Inputs.spec.js +++ b/test/unit/components/inputs/Inputs.spec.js @@ -16,7 +16,7 @@ import { } from "inputs"; import { render } from "test-utils.js"; import user from "@testing-library/user-event"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; import { fireEvent } from "@testing-library/react"; import { useState } from "react"; import * as wallet from "wallet"; @@ -149,21 +149,21 @@ test("render default NumericInput", () => { expect(input.className).toMatch("numericInput"); }); -test("render default IntegerInput", () => { +test("render default IntegerInput", async () => { const { input, inputTag } = renderWrappedInput(IntegerInput); checkDefaultInput(input, inputTag); expect(input.className).toMatch("numericInput"); - user.type(inputTag, "1.89a&%}"); + await user.type(inputTag, "1.89a&%}"); expect(inputTag.value).toBe("189"); }); -test("render default FloatInput", () => { +test("render default FloatInput", async () => { const { input, inputTag } = renderWrappedInput(FloatInput, { maxFracDigits: 3 }); checkDefaultInput(input, inputTag); expect(input.className).toMatch("numericInput"); - user.type(inputTag, "1.89a&%4234}"); + await user.type(inputTag, "1.89a&%4234}"); expect(inputTag.value).toBe("1.894"); }); @@ -187,7 +187,7 @@ test("render passphraseModalField", () => { expect(screen.getByLabelText(`${testLabel}:`)).toBeInTheDocument(); }); -test("render default PasswordInput", () => { +test("render default PasswordInput", async () => { const { input, inputTag } = renderInput(PasswordInput); checkDefaultInput(input, inputTag, "password"); expect(inputTag.type).toBe("password"); @@ -195,10 +195,10 @@ test("render default PasswordInput", () => { name: "Toggle Passsword Visibility" }); - user.click(togglePasswordVisibilityButton); + await user.click(togglePasswordVisibilityButton); expect(inputTag.type).toBe("text"); - user.click(togglePasswordVisibilityButton); + await user.click(togglePasswordVisibilityButton); expect(inputTag.type).toBe("password"); }); @@ -363,7 +363,7 @@ test("render input with unit in error state", () => { expect(unitElement.className).toMatch("error"); }); -test("render default NumTicketsInput", () => { +test("render default NumTicketsInput", async () => { const testInitValue = "1"; const { input, inputTag } = renderNumTicketsInput({ numTickets: testInitValue @@ -373,17 +373,17 @@ test("render default NumTicketsInput", () => { expect(input.className).toMatch("integerInput"); expect(screen.getByText("ticket")).toBeInTheDocument(); // single ticket //manual type a value - user.type(inputTag, "2.3"); + await user.type(inputTag, "2.3"); expect(inputTag.value).toBe("123"); expect(screen.getByText("tickets")).toBeInTheDocument(); // multiple tickets /* test buttons */ const lessButton = screen.getByRole("button", { name: "less" }); - user.click(lessButton); + await user.click(lessButton); expect(inputTag.value).toBe("122"); const moreButton = screen.getByRole("button", { name: "more" }); - user.click(moreButton); + await user.click(moreButton); expect(inputTag.value).toBe("123"); /* test arrow key */ @@ -415,34 +415,38 @@ test("open file with PathBrowseInput", async () => { filePaths: [testFilePath], filePath: testFilePath }); - user.click(screen.getByRole("button", { name: "Select a path" })); + await user.click(screen.getByRole("button", { name: "Select a path" })); expect(wallet.showOpenDialog).toHaveBeenCalledWith(anyArg()); - await wait(() => expect(mockOnChange).toHaveBeenCalledWith(testFilePath)); + await waitFor(() => expect(mockOnChange).toHaveBeenCalledWith(testFilePath)); /* electron return only filePath */ mockOnChange.mockRestore(); - user.clear(inputTag); + await user.clear(inputTag); wallet.showOpenDialog.mockReturnValueOnce({ filePath: testFilePath }); - user.click(screen.getByRole("button", { name: "Select a path" })); + await user.click(screen.getByRole("button", { name: "Select a path" })); expect(wallet.showOpenDialog).toHaveBeenCalledWith(anyArg()); - await wait(() => expect(mockOnChange).toHaveBeenCalledWith(testFilePath)); + await waitFor(() => expect(mockOnChange).toHaveBeenCalledWith(testFilePath)); /* electron does not return filePaths or filePath */ mockOnChange.mockRestore(); - user.clear(inputTag); + await user.clear(inputTag); wallet.showOpenDialog.mockReturnValueOnce({}); - user.click(screen.getByRole("button", { name: "Select a path" })); + await user.click(screen.getByRole("button", { name: "Select a path" })); expect(wallet.showOpenDialog).toHaveBeenCalledWith(anyArg()); - await wait(() => expect(mockOnChange).not.toHaveBeenCalledWith(testFilePath)); + await waitFor(() => + expect(mockOnChange).not.toHaveBeenCalledWith(testFilePath) + ); /* write path into input directly*/ const filePathManual = "t"; mockOnChange.mockRestore(); wallet.showOpenDialog.mockRestore(); - user.clear(inputTag); - user.type(inputTag, filePathManual); + await user.clear(inputTag); + await user.type(inputTag, filePathManual); expect(wallet.showOpenDialog).not.toHaveBeenCalled(); - await wait(() => expect(mockOnChange).toHaveBeenCalledWith(filePathManual)); + await waitFor(() => + expect(mockOnChange).toHaveBeenCalledWith(filePathManual) + ); }); test("save directory with PathBrowseInput", async () => { @@ -453,7 +457,7 @@ test("save directory with PathBrowseInput", async () => { checkDefaultInput(input, inputTag); expect(screen.getByText("Select a path")).toBeInTheDocument(); wallet.showSaveDialog.mockReturnValueOnce({ filePath: testFilePath }); - user.click(screen.getByRole("button", { name: "Select a path" })); + await user.click(screen.getByRole("button", { name: "Select a path" })); expect(wallet.showSaveDialog).toHaveBeenCalledWith(anyArg()); - await wait(() => expect(mockOnChange).toHaveBeenCalledWith(testFilePath)); + await waitFor(() => expect(mockOnChange).toHaveBeenCalledWith(testFilePath)); }); diff --git a/test/unit/components/layout/TabbedPage.spec.js b/test/unit/components/layout/TabbedPage.spec.js index 93a940b6ee..79c375550d 100644 --- a/test/unit/components/layout/TabbedPage.spec.js +++ b/test/unit/components/layout/TabbedPage.spec.js @@ -60,7 +60,7 @@ beforeEach(() => { selectors.uiAnimations = jest.fn(() => true); }); -test("test tabs with animation", () => { +test("test tabs with animation", async () => { render(} tabs={tabs} />); expect(screen.getByText(testPageHeaderTitle)).toBeInTheDocument(); @@ -70,17 +70,17 @@ test("test tabs with animation", () => { expect(screen.getByText(testDesc1)).toBeInTheDocument(); // move to the second tab - user.click(screen.getByText(testLabel2)); + await user.click(screen.getByText(testLabel2)); expect(screen.getByText(testContent2)).toBeInTheDocument(); expect(screen.getByText(testDesc2)).toBeInTheDocument(); // move to the third tab - user.click(screen.getByText(testLabel3)); + await user.click(screen.getByText(testLabel3)); expect(screen.getByText(testContent3)).toBeInTheDocument(); expect(screen.getByText(testDesc3)).toBeInTheDocument(); }); -test("test tabs without animation", () => { +test("test tabs without animation", async () => { selectors.uiAnimations = jest.fn(() => false); render(} tabs={tabs} />); @@ -91,12 +91,12 @@ test("test tabs without animation", () => { expect(screen.getByText(testDesc1)).toBeInTheDocument(); // move to the second tab - user.click(screen.getByText(testLabel2)); + await user.click(screen.getByText(testLabel2)); expect(screen.getByText(testContent2)).toBeInTheDocument(); expect(screen.getByText(testDesc2)).toBeInTheDocument(); // move to the third tab - user.click(screen.getByText(testLabel3)); + await user.click(screen.getByText(testLabel3)); expect(screen.getByText(testContent3)).toBeInTheDocument(); expect(screen.getByText(testDesc3)).toBeInTheDocument(); }); diff --git a/test/unit/components/shared/DetailsTable.spec.js b/test/unit/components/shared/DetailsTable.spec.js index 76ecfb0460..120cb86be8 100644 --- a/test/unit/components/shared/DetailsTable.spec.js +++ b/test/unit/components/shared/DetailsTable.spec.js @@ -52,7 +52,7 @@ const mockTitle = "mock-title"; const mockClassName = "mock-classname"; const mockHeaderClassName = "mock-header-classname"; -test("check non-expandable table", () => { +test("check non-expandable table", async () => { render( { expect(title.className).toMatch(mockHeaderClassName); expect(title.parentNode.className).toMatch(mockClassName); expect(screen.getByText(`${mockData[0].label}:`)).toBeInTheDocument(); - user.click(title); + await user.click(title); expect(screen.getByText(`${mockData[0].label}:`)).toBeInTheDocument(); }); -test("check expandable table", () => { +test("check expandable table", async () => { render(); const title = screen.getByText(mockTitle); expect(screen.queryByText(`${mockData[0].label}:`)).not.toBeInTheDocument(); //open details - user.click(title); + await user.click(title); expect(screen.getByText(`${mockData[0].label}:`)).toBeInTheDocument(); expect(screen.getByText(`${mockData[1].label}:`)).toBeInTheDocument(); expect(screen.getByText(mockData[0].value)).toBeInTheDocument(); @@ -110,6 +110,6 @@ test("check expandable table", () => { ).toBeInTheDocument(); //close details - user.click(title); + await user.click(title); expect(screen.queryByText(`${mockData[0].label}:`)).not.toBeInTheDocument(); }); diff --git a/test/unit/components/shared/VerticalAccordion.spec.js b/test/unit/components/shared/VerticalAccordion.spec.js index ba7c3625ac..8b867ff4cc 100644 --- a/test/unit/components/shared/VerticalAccordion.spec.js +++ b/test/unit/components/shared/VerticalAccordion.spec.js @@ -1,7 +1,7 @@ import { VerticalAccordion } from "shared"; import { render } from "test-utils.js"; import user from "@testing-library/user-event"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; const testHeader = "test-header"; const testContentText = "test-content-test"; @@ -37,9 +37,9 @@ test("toggle vertical accordion", async () => { expect(header.firstElementChild.className).toMatch(testArrowClassName); expect(header.parentElement.className).toMatch(testClassName); - await wait(() => screen.getByText(testContentText)); + await waitFor(() => screen.getByText(testContentText)); user.click(screen.getByText(testHeader)); - await wait(() => + await waitFor(() => expect(screen.queryByText(testContentText)).not.toBeInTheDocument() ); }); @@ -49,7 +49,7 @@ test("try toggling disabled vertical accordion", async () => { expect(screen.queryByText(testContentText)).not.toBeInTheDocument(); user.click(screen.getByText(testHeader)); - await wait(() => + await waitFor(() => expect(screen.queryByText(testContentText)).not.toBeInTheDocument() ); }); diff --git a/test/unit/components/views/AccountsPage/AccountsPage.spec.js b/test/unit/components/views/AccountsPage/AccountsPage.spec.js index be663dbbbf..e7b55f764b 100644 --- a/test/unit/components/views/AccountsPage/AccountsPage.spec.js +++ b/test/unit/components/views/AccountsPage/AccountsPage.spec.js @@ -1,7 +1,6 @@ import AccountsPage from "components/views/AccountsPage"; import { render } from "test-utils.js"; -import { screen, wait } from "@testing-library/react"; -import user from "@testing-library/user-event"; +import { screen, waitFor } from "@testing-library/react"; import * as sel from "selectors"; import * as ca from "actions/ControlActions"; import * as cla from "actions/ClientActions"; @@ -149,7 +148,7 @@ const queryHideButton = () => screen.queryByText("Hide"); const getShowButton = () => screen.getByText("Show").nextElementSibling; test("test the Primary account", async () => { - render(); + const { user } = render(); // default account const account = screen.getByText("Primary Account"); @@ -158,8 +157,8 @@ test("test the Primary account", async () => { ); // show account details - user.click(account); - await wait(() => expect(getBalances()).toBeInTheDocument()); + await user.click(account); + await waitFor(() => expect(getBalances()).toBeInTheDocument()); expect(getBalancesTextContent()).toMatchInlineSnapshot( '"BalancesTotal95.51454006 DCRSpendable95.51454006 DCRImmature Rewards0.00000 DCRLocked By Tickets0.00000 DCRVoting Authority58.02257025 DCRImmature Staking Rewards0.00000 DCRUnconfirmed0.00000 DCR"' ); @@ -168,7 +167,7 @@ test("test the Primary account", async () => { ); expect(getExtendedPublicKeyValue()).toBe("Hidden"); // reveal pubKey - user.click(getRevealPubkeyButton()); + await user.click(getRevealPubkeyButton()); expect(mockGetAccountExtendedKeyAttempt).toHaveBeenCalledWith( mockBalances[0].accountNumber ); @@ -177,31 +176,31 @@ test("test the Primary account", async () => { ).toBeInTheDocument(); // hide pubKey - user.click(getHidePubkeyButton()); + await user.click(getHidePubkeyButton()); expect(getExtendedPublicKeyValue()).toBe("Hidden"); // rename account, but cancel first - user.click(getRenameAccountButton()); - user.click(screen.getByText("Cancel")); + await user.click(getRenameAccountButton()); + await user.click(screen.getByText("Cancel")); // try rename without giving the name - user.click(getRenameAccountButton()); - user.click(screen.getByText("Rename")); + await user.click(getRenameAccountButton()); + await user.click(screen.getByText("Rename")); expect(screen.getByText("This field is required")).toBeInTheDocument(); const newAccountNameInput = screen.getByPlaceholderText("New Account Name"); // try rename typing a too long name - user.click(getRenameAccountButton()); + await user.click(getRenameAccountButton()); const tooLongAccountName = new Array(100).join("a"); - user.type(newAccountNameInput, tooLongAccountName); + await user.type(newAccountNameInput, tooLongAccountName); expect(newAccountNameInput.value.length).toBe(50); // rename account const testAccountName = "test-account-name"; - user.clear(newAccountNameInput); - user.type(newAccountNameInput, testAccountName); + await user.clear(newAccountNameInput); + await user.type(newAccountNameInput, testAccountName); expect(screen.queryByText("This field is required")).not.toBeInTheDocument(); - user.click(screen.getByText("Rename")); + await user.click(screen.getByText("Rename")); expect(mockRenameAccountAttempt).toHaveBeenCalledWith( mockBalances[0].accountNumber, testAccountName @@ -210,15 +209,15 @@ test("test the Primary account", async () => { expect(mockGetAccountsAttempt).toHaveBeenCalled(); // hide account details - user.click(account); - await wait(() => expect(queryBalances()).not.toBeInTheDocument()); + await user.click(account); + await waitFor(() => expect(queryBalances()).not.toBeInTheDocument()); // default account is not hideable expect(queryHideButton()).not.toBeInTheDocument(); }); test("test a common account", async () => { - render(); + const { user } = render(); const commonAccount = screen.getByText(mockBalances[1].accountName); expect(commonAccount.nextElementSibling.textContent).toMatchInlineSnapshot( @@ -226,8 +225,8 @@ test("test a common account", async () => { ); // show account details - user.click(commonAccount); - await wait(() => expect(getBalances()).toBeInTheDocument()); + await user.click(commonAccount); + await waitFor(() => expect(getBalances()).toBeInTheDocument()); expect(getBalancesTextContent()).toMatchInlineSnapshot( '"BalancesTotal481.25138665 DCRSpendable350.74115317 DCRImmature Rewards0.00000 DCRLocked By Tickets130.51023348 DCRVoting Authority130.51020368 DCRImmature Staking Rewards0.00000 DCRUnconfirmed0.00000 DCR"' ); @@ -239,12 +238,12 @@ test("test a common account", async () => { expect(queryHideButton()).not.toBeInTheDocument(); // hide common account details - user.click(commonAccount); - await wait(() => expect(queryBalances()).not.toBeInTheDocument()); + await user.click(commonAccount); + await waitFor(() => expect(queryBalances()).not.toBeInTheDocument()); }); test("test an empy account", async () => { - render(); + const { user } = render(); const emtpyAccount = screen.getByText(mockBalances[2].accountName); expect(emtpyAccount.nextElementSibling.textContent).toMatchInlineSnapshot( @@ -252,8 +251,8 @@ test("test an empy account", async () => { ); // show account details - user.click(emtpyAccount); - await wait(() => expect(getBalances()).toBeInTheDocument()); + await user.click(emtpyAccount); + await waitFor(() => expect(getBalances()).toBeInTheDocument()); expect(getBalancesTextContent()).toMatchInlineSnapshot( '"BalancesTotal0.00000 DCRSpendable0.00000 DCRImmature Rewards0.00000 DCRLocked By Tickets0.00000 DCRVoting Authority0.00000 DCRImmature Staking Rewards0.00000 DCRUnconfirmed0.00000 DCR"' ); @@ -262,45 +261,48 @@ test("test an empy account", async () => { ); // test hide button - user.click(getHideButton()); + await user.click(getHideButton()); expect(mockHideAccount).toHaveBeenCalledWith(mockBalances[2].accountNumber); // test show button - user.click(getShowButton()); + await user.click(getShowButton()); expect(mockShowAccount).toHaveBeenCalledWith(mockBalances[2].accountNumber); }); -test("test imported account", () => { - render(); +test("test imported account", async () => { + const { user } = render(); // default account const account = screen.getByText("imported"); expect(account.nextElementSibling.textContent).toMatchInlineSnapshot( '"0.00000 DCRSpendable:0.00000 DCR"' ); - user.click(account); + await user.click(account); expect(queryBalances()).not.toBeInTheDocument(); }); -test("test add new account", () => { - render(); +test("test add new account", async () => { + const { user } = render(); const addNewButton = screen.getByText("Add New"); - user.click(addNewButton); + await user.click(addNewButton); expect(getCreateNewAccountModalTitle()).toBeInTheDocument(); // cancel first - user.click(screen.getByText("Cancel")); + await user.click(screen.getByText("Cancel")); expect(queryCreateNewAccountModalTitle()).not.toBeInTheDocument(); - user.click(addNewButton); + await user.click(addNewButton); expect(getCreateNewAccountModalTitle()).toBeInTheDocument(); const testPrivatePassphrase = "test-priv-pass"; const testAccountName = "test-account-name"; - user.type(screen.getByLabelText("Private Passphrase"), testPrivatePassphrase); - user.type(screen.getByLabelText("New Account Name"), testAccountName); - user.click(screen.getByText("Continue")); + await user.type( + screen.getByLabelText("Private Passphrase"), + testPrivatePassphrase + ); + await user.type(screen.getByLabelText("New Account Name"), testAccountName); + await user.click(screen.getByText("Continue")); expect(mockGetNextAccountAttempt).toHaveBeenCalledWith( testPrivatePassphrase, testAccountName diff --git a/test/unit/components/views/AgendaDetailsPage/AgendaDetailsPage.spec.js b/test/unit/components/views/AgendaDetailsPage/AgendaDetailsPage.spec.js index f834ceeeb4..188aa6d785 100644 --- a/test/unit/components/views/AgendaDetailsPage/AgendaDetailsPage.spec.js +++ b/test/unit/components/views/AgendaDetailsPage/AgendaDetailsPage.spec.js @@ -60,7 +60,7 @@ const testAgendaCardElements = ( /* AgendaDetailsPage */ -const testAgendaDetailsPage = ( +const testAgendaDetailsPage = async ( mockChoice, finished, passed, @@ -104,7 +104,7 @@ const testAgendaDetailsPage = ( expect(screen.getByRole("radio", { name: mockChoice }).checked).toBe(true); if (!finished && !isLoading && mockChoice != "yes") { - user.click(yesButton); + await user.click(yesButton); expect(mockSetSelectedChoice).toHaveBeenCalledWith("yes"); } }; diff --git a/test/unit/components/views/DexPage/DexPage.spec.js b/test/unit/components/views/DexPage/DexPage.spec.js index 831848cb84..aa3db901f6 100644 --- a/test/unit/components/views/DexPage/DexPage.spec.js +++ b/test/unit/components/views/DexPage/DexPage.spec.js @@ -1,7 +1,6 @@ import DexPage from "components/views/DexPage"; import { render } from "test-utils.js"; -import user from "@testing-library/user-event"; -import { screen, wait, act } from "@testing-library/react"; +import { screen, waitFor, act } from "@testing-library/react"; import * as sel from "selectors"; import * as da from "actions/DexActions"; import * as dm from "actions/DaemonActions"; @@ -132,19 +131,19 @@ const getConnectDCRWalletButton = () => screen.getByRole("button", { name: "Connect DCR Wallet" }); const getLoginBtn = () => screen.queryByRole("button", { name: "Login" }); -test("enable dex view", () => { +test("enable dex view", async () => { selectors.dexEnabled = jest.fn(() => false); - render(); + const { user } = render(); const enableBtn = getEnableBtn(); - user.click(enableBtn); + await user.click(enableBtn); expect(screen.getByText("Wallet restart required")).toBeInTheDocument(); // cancel first - user.click(getCancelBtn()); + await user.click(getCancelBtn()); - user.click(enableBtn); - user.click(getConfirmBtn()); + await user.click(enableBtn); + await user.click(getConfirmBtn()); expect(mockOnEnableDex).toHaveBeenCalled(); }); @@ -166,41 +165,41 @@ test("dex is enabled, but not running", () => { ); }); -const testPassphraseModal = () => { +const testPassphraseModal = async (user) => { const contineBtn = getContinueBtn(); const confirmInput = screen.getByLabelText("Confirm"); const newPassphraseInput = screen.getByLabelText("New Passphrase"); - user.clear(newPassphraseInput); - user.clear(confirmInput); + await user.clear(newPassphraseInput); + await user.clear(confirmInput); expect(contineBtn.disabled).toBeTruthy(); - user.type(newPassphraseInput, testPassphrase); + await user.type(newPassphraseInput, testPassphrase); expect(contineBtn.disabled).toBeTruthy(); - user.type(confirmInput, testPassphrase + "-2"); + await user.type(confirmInput, testPassphrase + "-2"); expect(contineBtn.disabled).toBeTruthy(); // disabled until Confirm is different - user.clear(confirmInput); - user.type(confirmInput, testPassphrase); + await user.clear(confirmInput); + await user.type(confirmInput, testPassphrase); expect(contineBtn.disabled).toBeFalsy(); - user.click(contineBtn); + await user.click(contineBtn); }; -test("init dex without DEX seed", () => { - render(); +test("init dex without DEX seed", async () => { + const { user } = render(); expect(screen.getByText("Set DEX Password")).toBeInTheDocument(); const setDexPasshpraseBtn = getSetDexPasshpraseBtn(); - user.click(setDexPasshpraseBtn); + await user.click(setDexPasshpraseBtn); //cancel first - user.click(getCancelBtn()); - user.click(setDexPasshpraseBtn); + await user.click(getCancelBtn()); + await user.click(setDexPasshpraseBtn); - testPassphraseModal(); + await testPassphraseModal(user); expect(mockInitDex).toHaveBeenCalledWith(testPassphrase); }); -test("init dex with DEX seed", () => { - render(); +test("init dex with DEX seed", async () => { + const { user } = render(); expect(screen.getByText("Set DEX Password")).toBeInTheDocument(); const hasDexSeedInput = screen.getByLabelText( @@ -209,28 +208,28 @@ test("init dex with DEX seed", () => { expect(queryDexSeedInput()).not.toBeInTheDocument(); expect(hasDexSeedInput.checked).toBeFalsy(); - user.click(hasDexSeedInput); + await user.click(hasDexSeedInput); expect(hasDexSeedInput.checked).toBeTruthy(); // toggle off and back - user.click(hasDexSeedInput); - user.click(hasDexSeedInput); + await user.click(hasDexSeedInput); + await user.click(hasDexSeedInput); // continue without entering seed - user.click(getSetDexPasshpraseBtn()); - testPassphraseModal(); + await user.click(getSetDexPasshpraseBtn()); + await testPassphraseModal(user); expect(mockInitDex).not.toHaveBeenCalled(); expect(screen.getByText("You must enter a seed.")).toBeInTheDocument(); // continue with entering seed - user.type(getDexSeedInput(), testSeed); - user.click(getSetDexPasshpraseBtn()); - testPassphraseModal(); + await user.type(getDexSeedInput(), testSeed); + await user.click(getSetDexPasshpraseBtn()); + await testPassphraseModal(user); expect(mockInitDex).toHaveBeenCalledWith(testPassphrase, testSeed); }); -test("test confirm seed view", () => { +test("test confirm seed view", async () => { selectors.dexInit = jest.fn(() => true); - render(); + const { user } = render(); expect(screen.getByText("Confirm DEX Account Seed")).toBeInTheDocument(); expect( screen.getByText("Please confirm your DEX account seed before proceeding.") @@ -242,32 +241,32 @@ test("test confirm seed view", () => { ); expect(screen.queryByText(testSeed)).not.toBeInTheDocument(); - user.click(getRevealBtn()); + await user.click(getRevealBtn()); expect(screen.getByText(testSeed)).toBeInTheDocument(); - user.click(getCopyButton()); + await user.click(getCopyButton()); expect(mockCopy).toHaveBeenCalledWith(testSeed); - user.click(getConfirmSubmitButton()); + await user.click(getConfirmSubmitButton()); expect(mockConfirmDexSeed).toHaveBeenCalled(); }); -const testCreateDexAccountModal = () => { +const testCreateDexAccountModal = async (user) => { const contineBtn = getContinueBtn(); const newAccountNameInput = screen.getByLabelText("New Account Name"); const privatePassphraseInput = screen.getByLabelText("Private Passphrase"); - user.clear(privatePassphraseInput); - user.clear(newAccountNameInput); + await user.clear(privatePassphraseInput); + await user.clear(newAccountNameInput); expect(contineBtn.disabled).toBeTruthy(); - user.type(privatePassphraseInput, testPassphrase); - user.type(newAccountNameInput, testAccountName); - user.click(contineBtn); + await user.type(privatePassphraseInput, testPassphrase); + await user.type(newAccountNameInput, testAccountName); + await user.click(contineBtn); }; test("test create dex account", async () => { selectors.dexInit = jest.fn(() => true); selectors.confirmDexSeed = jest.fn(() => true); - render(); + const { user } = render(); expect( screen.getByText( "A new account is required to be created to improve security for the wallet overall." @@ -281,12 +280,12 @@ test("test create dex account", async () => { // create a new dex account const createDexAccountButton = getCreateDexAccountButton(); - user.click(createDexAccountButton); + await user.click(createDexAccountButton); //cancel first - user.click(getCancelBtn()); + await user.click(getCancelBtn()); - user.click(createDexAccountButton); - testCreateDexAccountModal(); + await user.click(createDexAccountButton); + await testCreateDexAccountModal(user); expect(mockCreateDexAccount).toHaveBeenCalledWith( testPassphrase, @@ -296,38 +295,38 @@ test("test create dex account", async () => { // select an existing account const selectAnExistingAccountButton = getSelectAnExistingAccountButton(); expect(selectAnExistingAccountButton.disabled).toBeTruthy(); - user.click(screen.getByText("Select account")); + await user.click(screen.getByText("Select account")); // mixed account in not in the list expect(screen.queryByText("mixed")).not.toBeInTheDocument(); - user.click(screen.getByText(mockAccount2.name)); + await user.click(screen.getByText(mockAccount2.name)); - await wait(() => + await waitFor(() => expect(screen.queryByText("Select account")).not.toBeInTheDocument() ); expect(selectAnExistingAccountButton.disabled).toBeFalsy(); - user.click(selectAnExistingAccountButton); + await user.click(selectAnExistingAccountButton); expect(mockSelectDexAccount).toHaveBeenCalledWith(mockAccount2.name); }); -const testConnectDCRWalletModal = () => { +const testConnectDCRWalletModal = async (user) => { const contineBtn = getContinueBtn(); const privatePassphraseInput = screen.getByLabelText("Private Passphrase"); const dexPassphraseInput = screen.getByLabelText("DEX Passphrase"); - user.clear(privatePassphraseInput); - user.clear(dexPassphraseInput); + await user.clear(privatePassphraseInput); + await user.clear(dexPassphraseInput); expect(contineBtn.disabled).toBeTruthy(); - user.type(privatePassphraseInput, testPassphrase); - user.type(dexPassphraseInput, testDexPassphrase); - user.click(contineBtn); + await user.type(privatePassphraseInput, testPassphrase); + await user.type(dexPassphraseInput, testDexPassphrase); + await user.click(contineBtn); }; -test("test connect dex wallet view", () => { +test("test connect dex wallet view", async () => { selectors.dexInit = jest.fn(() => true); selectors.confirmDexSeed = jest.fn(() => true); selectors.dexAccount = jest.fn(() => mockAccount2.name); - render(); + const { user } = render(); expect(screen.getByText("Connect DCR Wallet to DEX")).toBeInTheDocument(); expect( @@ -336,13 +335,13 @@ test("test connect dex wallet view", () => { const connectDCRWalletButton = getConnectDCRWalletButton(); - user.click(connectDCRWalletButton); + await user.click(connectDCRWalletButton); //cancel first - user.click(getCancelBtn()); + await user.click(getCancelBtn()); - user.click(connectDCRWalletButton); + await user.click(connectDCRWalletButton); - testConnectDCRWalletModal(); + await testConnectDCRWalletModal(user); expect(mockCreateWalletDex).toHaveBeenCalledWith( testPassphrase, @@ -360,27 +359,27 @@ test("connecting DCR wallet is in progress", () => { expect(queryEnableBtn()).not.toBeInTheDocument(); }); -test("test launch DEX view", () => { +test("test launch DEX view", async () => { selectors.dexInit = jest.fn(() => true); selectors.confirmDexSeed = jest.fn(() => true); selectors.dexAccount = jest.fn(() => mockAccount2.name); selectors.dexDCRWalletRunning = jest.fn(() => true); - render(); + const { user } = render(); - user.click(screen.getByRole("button", { name: "Launch DEX Window" })); + await user.click(screen.getByRole("button", { name: "Launch DEX Window" })); expect(mockLaunchDexWindow).toHaveBeenCalled(); }); -test("test login view", () => { +test("test login view", async () => { selectors.dexInit = jest.fn(() => true); selectors.confirmDexSeed = jest.fn(() => true); selectors.dexAccount = jest.fn(() => mockAccount2.name); selectors.dexDCRWalletRunning = jest.fn(() => true); selectors.loggedInDex = jest.fn(() => false); - render(); + const { user } = render(); expect(screen.getByText("DEX Login")).toBeInTheDocument(); expect( @@ -389,17 +388,17 @@ test("test login view", () => { const loginButton = getLoginBtn(); - user.click(loginButton); + await user.click(loginButton); //cancel first - user.click(getCancelBtn()); + await user.click(getCancelBtn()); - user.click(loginButton); + await user.click(loginButton); const contineBtn = getContinueBtn(); const dexPassphraseInput = screen.getByLabelText("DEX Passphrase"); expect(contineBtn.disabled).toBeTruthy(); - user.type(dexPassphraseInput, testDexPassphrase); - user.click(contineBtn); + await user.type(dexPassphraseInput, testDexPassphrase); + await user.click(contineBtn); expect(mockLoginDex).toHaveBeenCalledWith(testDexPassphrase); }); @@ -409,19 +408,19 @@ test("test when dex is ready", async () => { selectors.dexReady = jest.fn(() => true); jest.useFakeTimers(); - render(); - user.click(screen.getByRole("button", { name: "Launch DEX Window" })); + const { user } = render(); + await user.click(screen.getByRole("button", { name: "Launch DEX Window" })); - await wait(() => expect(mockGetDexLogs).toHaveBeenCalled()); + await waitFor(() => expect(mockGetDexLogs).toHaveBeenCalled()); expect(screen.queryByText(testLog)).not.toBeInTheDocument(); - user.click(screen.getByText("Logs")); - expect(screen.getByText(testLog)).toBeInTheDocument(); + await user.click(screen.getByText("Logs")); + await waitFor(() => expect(screen.getByText(testLog)).toBeInTheDocument()); // hide log - user.click(screen.getByText("Logs")); + await user.click(screen.getByText("Logs")); expect(screen.queryByText(testLog)).not.toBeInTheDocument(); // show again - user.click(screen.getByText("Logs")); + await user.click(screen.getByText("Logs")); mockGetDexLogs.mockClear(); mockGetDexLogs = daemonActions.getDexLogs = jest.fn( @@ -431,10 +430,12 @@ test("test when dex is ready", async () => { jest.advanceTimersByTime(2001); }); - await wait(() => expect(mockGetDexLogs).toHaveBeenCalled()); - expect(screen.getByText(testLog2)).toBeInTheDocument(); - expect(screen.queryByText(testLog)).not.toBeInTheDocument(); - expect(mockLaunchDexWindow).toHaveBeenCalled(); + await waitFor(() => { + expect(mockGetDexLogs).toHaveBeenCalled(); + expect(screen.getByText(testLog2)).toBeInTheDocument(); + expect(screen.queryByText(testLog)).not.toBeInTheDocument(); + expect(mockLaunchDexWindow).toHaveBeenCalled(); + }); }); test("receive error while getting error", async () => { @@ -445,11 +446,11 @@ test("receive error while getting error", async () => { mockGetDexLogs = daemonActions.getDexLogs = jest.fn( () => () => Promise.reject("error") ); - render(); + const { user } = render(); - await wait(() => expect(mockGetDexLogs).toHaveBeenCalled()); + await waitFor(() => expect(mockGetDexLogs).toHaveBeenCalled()); expect(screen.queryByText(testLog)).not.toBeInTheDocument(); - user.click(screen.getByText("Logs")); + await user.click(screen.getByText("Logs")); expect(screen.queryByText(testLog)).not.toBeInTheDocument(); mockGetDexLogs.mockClear(); @@ -457,6 +458,6 @@ test("receive error while getting error", async () => { jest.advanceTimersByTime(2001); }); - await wait(() => expect(mockGetDexLogs).toHaveBeenCalled()); + await waitFor(() => expect(mockGetDexLogs).toHaveBeenCalled()); expect(screen.queryByText(testLog)).not.toBeInTheDocument(); }); diff --git a/test/unit/components/views/GetStaredPage/AdvancedStartup.spec.js b/test/unit/components/views/GetStaredPage/AdvancedStartup.spec.js index 0a55a28cb8..b90d17cf00 100644 --- a/test/unit/components/views/GetStaredPage/AdvancedStartup.spec.js +++ b/test/unit/components/views/GetStaredPage/AdvancedStartup.spec.js @@ -1,7 +1,7 @@ import GetStartedPage from "components/views/GetStartedPage/GetStartedPage"; import { render } from "test-utils.js"; import user from "@testing-library/user-event"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; import * as sel from "selectors"; import * as wal from "wallet"; import * as da from "actions/DaemonActions"; @@ -55,7 +55,7 @@ beforeEach(() => { test("test remote daemon form", async () => { render(); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); expect(mockIsAdvancedDaemon).toHaveBeenCalled(); expect(mockGetRemoteCredentials).toHaveBeenCalled(); @@ -67,9 +67,9 @@ test("test remote daemon form", async () => { ); //test toggle control - user.click(screen.getByTestId("switch")); + await user.click(screen.getByTestId("switch")); expect(screen.getByText("Daemon Data Directory:")).toBeInTheDocument(); - user.click(screen.getByTestId("switch")); + await user.click(screen.getByTestId("switch")); const rpcUsernameInput = screen.getByPlaceholderText(/rpc username/i); const rpcPasswordInput = screen.getByPlaceholderText(/rpc password/i); @@ -84,26 +84,26 @@ test("test remote daemon form", async () => { expect(rpcHostInput.value).toMatch(""); expect(rpcPortInput.value).toMatch(""); - user.type(rpcUsernameInput, testRemoteCredentials.rpc_user); - user.type(rpcPasswordInput, testRemoteCredentials.rpc_pass); - user.type(rpcCertificatePathInput, testRemoteCredentials.rpc_cert); - user.type(rpcHostInput, testRemoteCredentials.rpc_host); - user.type(rpcPortInput, testRemoteCredentials.rpc_port); + await user.type(rpcUsernameInput, testRemoteCredentials.rpc_user); + await user.type(rpcPasswordInput, testRemoteCredentials.rpc_pass); + await user.type(rpcCertificatePathInput, testRemoteCredentials.rpc_cert); + await user.type(rpcHostInput, testRemoteCredentials.rpc_host); + await user.type(rpcPortInput, testRemoteCredentials.rpc_port); - user.clear(rpcUsernameInput); - user.clear(rpcPasswordInput); - user.clear(rpcCertificatePathInput); - user.clear(rpcHostInput); - user.clear(rpcPortInput); + await user.clear(rpcUsernameInput); + await user.clear(rpcPasswordInput); + await user.clear(rpcCertificatePathInput); + await user.clear(rpcHostInput); + await user.clear(rpcPortInput); expect(screen.getAllByText(/this field is required/i).length).toBe(5); // type rpc credentials again - user.type(rpcUsernameInput, testRemoteCredentials.rpc_user); - user.type(rpcPasswordInput, testRemoteCredentials.rpc_pass); - user.type(rpcCertificatePathInput, testRemoteCredentials.rpc_cert); - user.type(rpcHostInput, testRemoteCredentials.rpc_host); - user.type(rpcPortInput, testRemoteCredentials.rpc_port); + await user.type(rpcUsernameInput, testRemoteCredentials.rpc_user); + await user.type(rpcPasswordInput, testRemoteCredentials.rpc_pass); + await user.type(rpcCertificatePathInput, testRemoteCredentials.rpc_cert); + await user.type(rpcHostInput, testRemoteCredentials.rpc_host); + await user.type(rpcPortInput, testRemoteCredentials.rpc_port); expect(rpcUsernameInput.value).toMatch(testRemoteCredentials.rpc_user); expect(rpcPasswordInput.value).toMatch(testRemoteCredentials.rpc_pass); @@ -111,39 +111,41 @@ test("test remote daemon form", async () => { expect(rpcHostInput.value).toMatch(testRemoteCredentials.rpc_host); expect(rpcPortInput.value).toMatch(testRemoteCredentials.rpc_port); - user.click(screen.getByText(/use remote daemon/i)); + await user.click(screen.getByText(/use remote daemon/i)); expect(mockSetRemoteCredentials).toHaveBeenCalled(); expect(mockConnectDaemon).toHaveBeenCalledWith(testRemoteCredentials, true); - await wait(() => screen.getByText(JSON.stringify(testConnectDaemonErrorMsg))); + await waitFor(() => + screen.getByText(JSON.stringify(testConnectDaemonErrorMsg)) + ); }); test("test local daemon form", async () => { render(); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); - user.click(screen.getByTestId("switch")); + await user.click(screen.getByTestId("switch")); expect(screen.getByText("Daemon Data Directory:")).toBeInTheDocument(); const daemonDataDirectoryInput = screen.getByPlaceholderText( "Daemon Data Directory" ); - user.type(daemonDataDirectoryInput, testDaemonDataDirectory); - user.clear(daemonDataDirectoryInput); + await user.type(daemonDataDirectoryInput, testDaemonDataDirectory); + await user.clear(daemonDataDirectoryInput); expect(screen.getAllByText(/this field is required/i).length).toBe(1); // type data directory again - user.type(daemonDataDirectoryInput, testDaemonDataDirectory); + await user.type(daemonDataDirectoryInput, testDaemonDataDirectory); - user.click(screen.getByText(/start appdata daemon/i)); + await user.click(screen.getByText(/start appdata daemon/i)); expect(mockSetAppdataPath).toHaveBeenCalledWith(testDaemonDataDirectory); expect(mockStartDaemon).toHaveBeenCalledWith({ appdata: testDaemonDataDirectory }); - await wait(() => screen.getByText(testStartDaemonErrorMsg)); + await waitFor(() => screen.getByText(testStartDaemonErrorMsg)); }); test("test skip link", async () => { render(); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); - user.click(screen.getByText(/skip/i)); - await wait(() => screen.getByText(testStartDaemonErrorMsg)); + await user.click(screen.getByText(/skip/i)); + await waitFor(() => screen.getByText(testStartDaemonErrorMsg)); }); diff --git a/test/unit/components/views/GetStaredPage/CreateWallet.spec.js b/test/unit/components/views/GetStaredPage/CreateWallet.spec.js index 0135854aba..94aa0b9b7d 100644 --- a/test/unit/components/views/GetStaredPage/CreateWallet.spec.js +++ b/test/unit/components/views/GetStaredPage/CreateWallet.spec.js @@ -10,7 +10,7 @@ import { selectedSeedWordsCount } from "components/views/GetStartedPage/CreateWalletPage/ConfirmSeed/utils.js"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; import * as sel from "selectors"; import * as wla from "actions/WalletLoaderActions"; import * as da from "actions/DaemonActions"; @@ -95,33 +95,39 @@ beforeEach(() => { const goToCopySeedView = async () => { render(); - await wait(() => screen.getByText(/welcome to decrediton/i)); - user.click(screen.getByText(/create a new wallet/i)); - await wait(() => screen.getByText("Wallet Name")); - user.type(screen.getByPlaceholderText(/choose a name/i), testWalletName); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); + fireEvent.click(screen.getByText(/create a new wallet/i)); + await waitFor(() => screen.getByText("Wallet Name")); + await user.type( + screen.getByPlaceholderText(/choose a name/i), + testWalletName + ); - user.click(screen.getByText(/creating/i)); - await wait(() => screen.getByText(/copy seed words to clipboard/i)); + await user.click(screen.getByText(/creating/i)); + await waitFor(() => screen.getByText(/copy seed words to clipboard/i)); }; const goToConfirmView = async () => { - await goToCopySeedView(); - user.click(screen.getByText(/continue/i)); - await wait(() => screen.getByText("Seed phrase verification")); + await goToCopySeedView(user); + await user.click(screen.getByText(/continue/i)); + await waitFor(() => screen.getByText("Seed phrase verification")); }; const goToRestoreView = async () => { render(); - await wait(() => screen.getByText(/welcome to decrediton/i)); - user.click(screen.getByText(/restore existing wallet/i)); - await wait(() => screen.getByText("Wallet Name")); - user.type(screen.getByPlaceholderText("Choose a Name"), testWalletName); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); + const restoreButton = screen.getByRole("button", { + name: /restore existing wallet/i + }); + fireEvent.click(restoreButton); + await waitFor(() => screen.getByText("Wallet Name")); + await user.type(screen.getByPlaceholderText("Choose a Name"), testWalletName); }; const goToExistingSeedView = async () => { await goToRestoreView(); - user.click(screen.getByText("Continue")); - await wait(() => screen.getByText("Confirm Seed Key")); + await user.click(screen.getByText("Continue")); + await waitFor(() => screen.getByText("Confirm Seed Key")); }; const testPrivatePassphraseInputs = async ( @@ -148,8 +154,8 @@ const testPrivatePassphraseInputs = async ( fireEvent.change(repeatPrivatePassphraseInput, { target: { value: `mistyped ${testPassword}` } }); - await wait(() => screen.getByText(/passphrases do not match/i)); - user.clear(repeatPrivatePassphraseInput); + await waitFor(() => screen.getByText(/passphrases do not match/i)); + await user.clear(repeatPrivatePassphraseInput); } fireEvent.change(repeatPrivatePassphraseInput, { target: { value: testPassword } @@ -164,13 +170,12 @@ const firePasteEvent = (combobox, text) => { } }; const pasteEvent = createEvent.paste(combobox, eventProperties); - pasteEvent.clipboardData = eventProperties.clipboardData; fireEvent(combobox, pasteEvent); }; -const fillSeedWordEntryUsingEnterKey = (combobox, word) => { - user.click(combobox); - user.type(combobox, word); +const fillSeedWordEntryUsingEnterKey = async (combobox, word) => { + await user.click(combobox); + await user.type(combobox, word); fireEvent.keyDown(combobox, { key: "Enter", code: "Enter", charCode: 13 }); }; @@ -184,30 +189,37 @@ test("test copy seed view", async () => { expect(seedWordLabel).toBeInTheDocument(); expect(seedWordLabel.previousSibling.textContent).toMatch(`${i + 1}.`); }); - user.click(screen.getByText(/copy seed words to clipboard/i)); - expect(screen.getByText(/seed clipboard copy warning/i)).toBeInTheDocument(); + fireEvent.click(screen.getByText(/copy seed words to clipboard/i)); + await screen.findByText(/seed clipboard copy warning/i); // cancel and reopen modal - user.click(screen.getByRole("button", { name: "Cancel seed copy" })); - expect( - screen.queryByText(/seed clipboard copy warning/i) - ).not.toBeInTheDocument(); - user.click(screen.getByText(/copy seed words to clipboard/i)); + fireEvent.click(screen.getByRole("button", { name: "Cancel seed copy" })); + await waitFor(() => { + expect( + screen.queryByText(/seed clipboard copy warning/i) + ).not.toBeInTheDocument(); + }); + fireEvent.click(screen.getByText(/copy seed words to clipboard/i)); const confirmSeedCopyButton = screen.getByText(/confirm seed copy/i); - expect(confirmSeedCopyButton).toBeInTheDocument(); + await waitFor(() => { + expect(confirmSeedCopyButton).toBeInTheDocument(); + }); expect(confirmSeedCopyButton.disabled).toBe(true); const inputControl = screen.getByRole("textbox"); - user.type(inputControl, "some random text"); + fireEvent.change(inputControl, { target: { value: "some random text" } }); expect(confirmSeedCopyButton.disabled).toBe(true); - user.clear(inputControl); - user.type(inputControl, "I understand the risks"); + fireEvent.change(inputControl, { + target: { value: "I understand the risks" } + }); expect(confirmSeedCopyButton.disabled).toBe(false); - user.click(confirmSeedCopyButton); - expect(mockCopySeedToClipboard).toHaveBeenCalledWith(testSeedMnemonic); + fireEvent.click(confirmSeedCopyButton); + await waitFor(() => { + expect(mockCopySeedToClipboard).toHaveBeenCalledWith(testSeedMnemonic); + }); }); const clickOnSeedButton = async (i, clickToTheFake) => { @@ -220,15 +232,15 @@ const clickOnSeedButton = async (i, clickToTheFake) => { if (!clickToTheFake) { if (buttons[index].textContent === word) { foundTheButton = true; - user.click(buttons[index]); - await wait(() => expect(buttons[index].disabled).toBeTruthy()); + fireEvent.click(buttons[index]); + await waitFor(() => expect(buttons[index].disabled).toBeTruthy()); } else { index++; } } else if (buttons[index].textContent !== word) { foundTheButton = true; - user.click(buttons[index]); - await wait(() => expect(buttons[index].disabled).toBeTruthy()); + fireEvent.click(buttons[index]); + await waitFor(() => expect(buttons[index].disabled).toBeTruthy()); } else { index++; } @@ -279,12 +291,14 @@ test("test confim seed view", async () => { ); await clickOnSeedButton(testSeedArray.length - 1, true); await clickOnSeedButton(testSeedArray.length - 1, false); - expect(mockDecodeSeed).toHaveBeenCalledWith(testSeedMnemonic); - expect(createWalletButton.disabled).toBeFalsy(); + await waitFor(() => { + expect(mockDecodeSeed).toHaveBeenCalledWith(testSeedMnemonic); + expect(createWalletButton.disabled).toBeFalsy(); + }); - user.click(createWalletButton); - await wait(() => expect(mockCreateWallet).toHaveBeenCalled()); -}); + fireEvent.click(createWalletButton); + await waitFor(() => expect(mockCreateWallet).toHaveBeenCalled()); +}, 30000); test("test confirm seed view in testnet mode (allows verification skip in dev)", async () => { mockIsTestNet = selectors.isTestNet = jest.fn(() => true); @@ -307,10 +321,12 @@ test("test confirm seed view in testnet mode (allows verification skip in dev)", }) ); - await wait(() => expect(createWalletButton.disabled).toBe(false)); - user.click(createWalletButton); - expect(mockCreateWallet).toHaveBeenCalled(); - expect(mockIsTestNet).toHaveBeenCalled(); + await waitFor(() => expect(createWalletButton.disabled).toBe(false)); + fireEvent.click(createWalletButton); + await waitFor(() => { + expect(mockCreateWallet).toHaveBeenCalled(); + expect(mockIsTestNet).toHaveBeenCalled(); + }); }); test("test typing a valid seed word on existing seed view", async () => { @@ -328,9 +344,9 @@ test("test typing a valid seed word on existing seed view", async () => { const comboboxArray = screen.getAllByRole("combobox"); expect(comboboxArray.length).toBe(testSeedArray.length); - fillSeedWordEntryUsingEnterKey(comboboxArray[0], testSeedArray[0]); + await fillSeedWordEntryUsingEnterKey(comboboxArray[0], testSeedArray[0]); expect(mockDecodeSeed).toHaveBeenCalled(); - await wait(() => + await waitFor(() => expect(screen.getByText("1.").parentNode.className).toMatch(/populated/) ); }); @@ -342,7 +358,7 @@ test("pasting just 32 seed words on existing seed view", async () => { firePasteEvent(screen.getAllByRole("combobox")[0], testInvalidSeedMnemonic); - await wait(() => screen.getByText(/please paste a valid 33 word seed./i)); + await waitFor(() => screen.getByText(/please paste a valid 33 word seed./i)); }); test("pasting invalid seed words on existing seed view", async () => { @@ -352,7 +368,7 @@ test("pasting invalid seed words on existing seed view", async () => { await goToExistingSeedView(); firePasteEvent(screen.getAllByRole("combobox")[0], testSeedMnemonic); - await wait(() => screen.getByText(/Error: seed is not valid./i)); + await waitFor(() => screen.getByText(/Error: seed is not valid./i)); }); test("pasting valid seed words on existing seed view and receive create wallet request error", async () => { @@ -369,19 +385,17 @@ test("pasting valid seed words on existing seed view and receive create wallet r firePasteEvent(screen.getAllByRole("combobox")[0], testSeedMnemonic); - await wait(() => + await waitFor(() => screen.getByText( /please make sure you also have a physical, written down copy of your seed./i ) ); await testPrivatePassphraseInputs(); - user.click(screen.getByText(/create wallet/i)); - expect(mockCreateWallet).toHaveBeenCalled(); - expect(mockCreateWalletRequest).toHaveBeenCalled(); + await user.click(screen.getByText(/create wallet/i)); // expect to jump back to the wallet choose view, and display // the error msg received from createWalletRequest - await wait(() => screen.getByText(testCreateWalletRequestErrorMsg)); + await waitFor(() => screen.getByText(testCreateWalletRequestErrorMsg)); screen.getByText(/choose the wallet to access/i); }); @@ -399,7 +413,7 @@ test("pasting valid seed words on existing seed view and successfully create wal firePasteEvent(screen.getAllByRole("combobox")[0], testSeedMnemonic); - await wait(() => + await waitFor(() => screen.getByText( /please make sure you also have a physical, written down copy of your seed./i ) @@ -416,12 +430,15 @@ test("pasting valid seed words on existing seed view and successfully create wal true ); - user.click(screen.getByText(/create wallet/i)); - expect(mockCreateWallet).toHaveBeenCalled(); - expect(mockCreateWalletRequest).toHaveBeenCalled(); - await wait(() => - expect(screen.getByText(/choose the wallet to access/i)).toBeInTheDocument() - ); + fireEvent.click(screen.getByText(/create wallet/i)); + await waitFor(() => { + expect(mockCreateWallet).toHaveBeenCalled(); + expect(mockCreateWalletRequest).toHaveBeenCalled(); + + expect( + screen.getByText(/choose the wallet to access/i) + ).toBeInTheDocument(); + }); }); test("check passphrase errors on restore view", async () => { @@ -448,13 +465,19 @@ test("check passphrase errors on restore view", async () => { ); // different passphrases - user.type(repeatPrivatePassphraseInput, "plus-string"); - screen.getByText("*Passphrases do not match"); + fireEvent.change(repeatPrivatePassphraseInput, { + target: { value: "plus-string" } + }); + await waitFor(() => { + expect(screen.getByText("*Passphrases do not match")).toBeInTheDocument(); + }); // clear passphrases - user.clear(privatePassphraseInput); - user.clear(repeatPrivatePassphraseInput); - screen.getByText("*Please enter your private passphrase"); + await user.clear(privatePassphraseInput); + await user.clear(repeatPrivatePassphraseInput); + await waitFor(() => { + expect(screen.getByText("*Please enter your private passphrase")); + }); }); test("create wallet button must be disabled if any of the inputs are invalid", async () => { @@ -472,7 +495,7 @@ test("create wallet button must be disabled if any of the inputs are invalid", a const comboboxArray = screen.getAllByRole("combobox"); firePasteEvent(comboboxArray[0], testSeedMnemonic); - await wait(() => + await waitFor(() => screen.getByText( "*Please make sure you also have a physical, written down copy of your seed." ) @@ -494,8 +517,8 @@ test("create wallet button must be disabled if any of the inputs are invalid", a mockDecodeSeed = wlActions.decodeSeed = jest.fn( () => () => Promise.reject({}) ); - fillSeedWordEntryUsingEnterKey(comboboxArray[0], testSeedArray[1]); - await wait(() => expect(createWallet).toHaveAttribute("disabled")); + await fillSeedWordEntryUsingEnterKey(comboboxArray[0], testSeedArray[1]); + await waitFor(() => expect(createWallet).toHaveAttribute("disabled")); // fix, button should be enabled mockDecodeSeed = wlActions.decodeSeed = jest.fn( @@ -504,8 +527,8 @@ test("create wallet button must be disabled if any of the inputs are invalid", a decodedSeed: testSeedArray }) ); - fillSeedWordEntryUsingEnterKey(comboboxArray[0], testSeedArray[0]); - await wait(() => expect(createWallet).not.toHaveAttribute("disabled")); + await fillSeedWordEntryUsingEnterKey(comboboxArray[0], testSeedArray[0]); + await waitFor(() => expect(createWallet).not.toHaveAttribute("disabled")); }, 30000); test("test POSITION_ERROR handling on restore view (missing words)", async () => { @@ -516,9 +539,9 @@ test("test POSITION_ERROR handling on restore view (missing words)", async () => () => () => Promise.reject({}) ); const comboboxArray = screen.getAllByRole("combobox"); - fillSeedWordEntryUsingEnterKey(comboboxArray[0], testSeedArray[0]); + await fillSeedWordEntryUsingEnterKey(comboboxArray[0], testSeedArray[0]); expect(screen.getByText("1.").parentNode.className).toMatch(/populated/); - fillSeedWordEntryUsingEnterKey(comboboxArray[1], testSeedArray[1]); + await fillSeedWordEntryUsingEnterKey(comboboxArray[1], testSeedArray[1]); expect(screen.getByText("2.").parentNode.className).toMatch(/populated/); mockDecodeSeed = wlActions.decodeSeed = jest.fn( @@ -527,8 +550,8 @@ test("test POSITION_ERROR handling on restore view (missing words)", async () => toString: () => `is ${POSITION_ERROR} 2, check for missing words` }) ); - fillSeedWordEntryUsingEnterKey(comboboxArray[2], testSeedArray[2]); - await wait(() => + await fillSeedWordEntryUsingEnterKey(comboboxArray[2], testSeedArray[2]); + await waitFor(() => expect(screen.getByText("3.").parentNode.className).toMatch(/error/) ); }, 30000); @@ -541,26 +564,26 @@ test("test POSITION_ERROR handling on restore view (mismatch error)", async () = () => () => Promise.reject({}) ); const comboboxArray = screen.getAllByRole("combobox"); - fillSeedWordEntryUsingEnterKey(comboboxArray[0], testSeedArray[0]); + await fillSeedWordEntryUsingEnterKey(comboboxArray[0], testSeedArray[0]); expect(screen.getByText("1.").parentNode.className).toMatch(/populated/); - fillSeedWordEntryUsingEnterKey(comboboxArray[1], testSeedArray[1]); + await fillSeedWordEntryUsingEnterKey(comboboxArray[1], testSeedArray[1]); expect(screen.getByText("2.").parentNode.className).toMatch(/populated/); mockDecodeSeed = wlActions.decodeSeed = jest.fn( () => () => Promise.reject({ details: MISMATCH_ERROR }) ); - fillSeedWordEntryUsingEnterKey(comboboxArray[4], testSeedArray[4]); - await wait(() => + await fillSeedWordEntryUsingEnterKey(comboboxArray[4], testSeedArray[4]); + await waitFor(() => expect(screen.getByText("5.").parentNode.className).toMatch(/populated/) ); // if entered the same word, the decodeSeed should not be called mockDecodeSeed.mockClear(); - fillSeedWordEntryUsingEnterKey(comboboxArray[4], testSeedArray[4]); + await fillSeedWordEntryUsingEnterKey(comboboxArray[4], testSeedArray[4]); expect(mockDecodeSeed).not.toHaveBeenCalled(); - fillSeedWordEntryUsingEnterKey(comboboxArray[5], testSeedArray[5]); - await wait(() => + await fillSeedWordEntryUsingEnterKey(comboboxArray[5], testSeedArray[5]); + await waitFor(() => expect(screen.getByText("6.").parentNode.className).toMatch(/populated/) ); }, 30000); @@ -575,10 +598,10 @@ test("test invalid POSITION_ERROR msg format handling on restore view", async () }) ); const comboboxArray = screen.getAllByRole("combobox"); - fillSeedWordEntryUsingEnterKey(comboboxArray[0], testSeedArray[0]); - fillSeedWordEntryUsingEnterKey(comboboxArray[1], testSeedArray[1]); - fillSeedWordEntryUsingEnterKey(comboboxArray[3], testSeedArray[3]); - await wait(() => + await fillSeedWordEntryUsingEnterKey(comboboxArray[0], testSeedArray[0]); + await fillSeedWordEntryUsingEnterKey(comboboxArray[1], testSeedArray[1]); + await fillSeedWordEntryUsingEnterKey(comboboxArray[3], testSeedArray[3]); + await waitFor(() => expect(screen.getByText("4.").parentNode.className).toMatch(/populated/) ); }, 30000); @@ -588,27 +611,33 @@ test("test hex input tab on restore view", async () => { const wordsTab = screen.getByText("words"); const hexTab = screen.getByText("hex"); - user.click(hexTab); + fireEvent.click(hexTab); const hexInputPlaceholderText = "Enter the hex representation of your seed..."; const hexSeedErrorMsg = "Invalid hex seed. Hex seeds need to be between 32 and 128 characters long."; const hexSeedWarningMsg = "Error: seed is not 32 bytes, such comes from a non-supported software and may have unintended consequences."; - expect( - screen.getByPlaceholderText(hexInputPlaceholderText) - ).toBeInTheDocument(); + waitFor(() => { + expect( + screen.getByPlaceholderText(hexInputPlaceholderText) + ).toBeInTheDocument(); + }); // go back to words tab - user.click(wordsTab); - expect( - screen.queryByPlaceholderText(hexInputPlaceholderText) - ).not.toBeInTheDocument(); + fireEvent.click(wordsTab); + waitFor(() => { + expect( + screen.queryByPlaceholderText(hexInputPlaceholderText) + ).not.toBeInTheDocument(); + }); // go back to hex tab again - user.click(hexTab); + fireEvent.click(hexTab); const hexInput = screen.getByPlaceholderText(hexInputPlaceholderText); - expect(hexInput).toBeInTheDocument(); + waitFor(() => { + expect(hexInput).toBeInTheDocument(); + }); // test too short hex word fireEvent.change(hexInput, { @@ -626,31 +655,37 @@ test("test hex input tab on restore view", async () => { ); // Test valid (but short, incompatible) hex seed. - user.clear(hexInput); + await user.clear(hexInput); fireEvent.change(hexInput, { target: { value: testShortHexSeed } }); - await wait(() => expect(mockDecodeSeed).toHaveBeenCalled()); - expect(screen.queryByText(hexSeedErrorMsg)).not.toBeInTheDocument(); - expect(screen.getByText(hexSeedWarningMsg)).toBeInTheDocument(); + await waitFor(() => { + expect(mockDecodeSeed).toHaveBeenCalled(); + expect(screen.queryByText(hexSeedErrorMsg)).not.toBeInTheDocument(); + expect(screen.getByText(hexSeedWarningMsg)).toBeInTheDocument(); + }); // Test valid and compatible hex seed - user.clear(hexInput); + await user.clear(hexInput); fireEvent.change(hexInput, { target: { value: testCompatibleHexSeed } }); - await wait(() => expect(mockDecodeSeed).toHaveBeenCalled()); - expect(screen.queryByText(hexSeedErrorMsg)).not.toBeInTheDocument(); - expect(screen.queryByText(hexSeedWarningMsg)).not.toBeInTheDocument(); + await waitFor(() => { + expect(mockDecodeSeed).toHaveBeenCalled(); + expect(screen.queryByText(hexSeedErrorMsg)).not.toBeInTheDocument(); + expect(screen.queryByText(hexSeedWarningMsg)).not.toBeInTheDocument(); + }); // Test valid hex seed with max chars - user.clear(hexInput); + await user.clear(hexInput); fireEvent.change(hexInput, { target: { value: testMaxHexSeed } }); - await wait(() => expect(mockDecodeSeed).toHaveBeenCalled()); - expect(screen.queryByText(hexSeedErrorMsg)).not.toBeInTheDocument(); - expect(screen.getByText(hexSeedWarningMsg)).toBeInTheDocument(); + await waitFor(() => { + expect(mockDecodeSeed).toHaveBeenCalled(); + expect(screen.queryByText(hexSeedErrorMsg)).not.toBeInTheDocument(); + expect(screen.getByText(hexSeedWarningMsg)).toBeInTheDocument(); + }); // The next tests all assume a failed decode. mockDecodeSeed = wlActions.decodeSeed = jest.fn( @@ -658,7 +693,7 @@ test("test hex input tab on restore view", async () => { ); // Test too long hex seed. - user.clear(hexInput); + await user.clear(hexInput); fireEvent.change(hexInput, { target: { value: testTooLongHexSeed } }); @@ -667,7 +702,7 @@ test("test hex input tab on restore view", async () => { expect(hexInput.value).toMatch(""); // Test hex seed with odd number of characters. - user.clear(hexInput); + await user.clear(hexInput); fireEvent.change(hexInput, { target: { value: testOddHexSeed } }); @@ -676,7 +711,7 @@ test("test hex input tab on restore view", async () => { expect(hexInput.value).toMatch(""); // Test hex seed with invalid character. - user.clear(hexInput); + await user.clear(hexInput); fireEvent.change(hexInput, { target: { value: testInvalidCharHexSeed } }); @@ -689,10 +724,10 @@ test("space button should be disabled on seed combobox", async () => { await goToExistingSeedView(); const combobox = screen.getAllByRole("combobox")[0]; - user.click(combobox); - user.type(combobox, testSeedArray[0].charAt(0)); + await user.click(combobox); + await user.type(combobox, testSeedArray[0].charAt(0)); expect(combobox.value).toMatch(testSeedArray[0].charAt(0)); - user.type(combobox, " "); + await user.type(combobox, " "); expect(combobox.value).toMatch(testSeedArray[0].charAt(0)); }); @@ -709,7 +744,7 @@ test("middle mouse button down and paste on seed combobox", async () => { const combobox = screen.getAllByRole("combobox")[0]; fireEvent.mouseDown(combobox, { which: 2 }); - await wait(() => expect(mockClipboardReadText).toHaveBeenCalled()); + await waitFor(() => expect(mockClipboardReadText).toHaveBeenCalled()); for (let i = 0; i < testSeedArray.length; i++) { expect(getComboboxByName(testSeedArray[i])).toBeInTheDocument(); } @@ -718,9 +753,9 @@ test("middle mouse button down and paste on seed combobox", async () => { test("test cancel button on existing seed view", async () => { await goToExistingSeedView(); - user.click(screen.getByText("Cancel")); - await wait(() => expect(mockCancelCreateWallet).toHaveBeenCalled()); - await wait(() => + await user.click(screen.getByText("Cancel")); + await waitFor(() => expect(mockCancelCreateWallet).toHaveBeenCalled()); + await waitFor(() => expect(screen.getByText(/choose the wallet to access/i)).toBeInTheDocument() ); }); @@ -728,9 +763,9 @@ test("test cancel button on existing seed view", async () => { test("test cancel button on copy seed view", async () => { await goToCopySeedView(); - user.click(screen.getByText("Cancel")); - await wait(() => expect(mockCancelCreateWallet).toHaveBeenCalled()); - await wait(() => + await user.click(screen.getByText("Cancel")); + await waitFor(() => expect(mockCancelCreateWallet).toHaveBeenCalled()); + await waitFor(() => expect(screen.getByText(/choose the wallet to access/i)).toBeInTheDocument() ); }); @@ -738,15 +773,14 @@ test("test cancel button on copy seed view", async () => { test("test back button on confirm view", async () => { await goToConfirmView(); - user.click(screen.getByText("Back")); - await wait(() => screen.getByText(/copy seed words to clipboard/i)); + await user.click(screen.getByText("Back")); + await waitFor(() => screen.getByText(/copy seed words to clipboard/i)); }); test("test go back button on existing seed view", async () => { await goToExistingSeedView(); - - user.click(screen.getByText(/go back/i).nextElementSibling); - await wait(() => + await user.click(screen.getByText(/go back/i).nextElementSibling); + await waitFor(() => expect(screen.getByText(/choose the wallet to access/i)).toBeInTheDocument() ); }); @@ -754,8 +788,8 @@ test("test go back button on existing seed view", async () => { test("test go back button on copy seed view", async () => { await goToCopySeedView(); - user.click(screen.getByText(/go back/i).nextElementSibling); - await wait(() => + await user.click(screen.getByText(/go back/i).nextElementSibling); + await waitFor(() => expect(screen.getByText(/choose the wallet to access/i)).toBeInTheDocument() ); }); diff --git a/test/unit/components/views/GetStaredPage/GetStartedPage.spec.js b/test/unit/components/views/GetStaredPage/GetStartedPage.spec.js index f290ae588d..5807796c97 100644 --- a/test/unit/components/views/GetStaredPage/GetStartedPage.spec.js +++ b/test/unit/components/views/GetStaredPage/GetStartedPage.spec.js @@ -2,7 +2,7 @@ import GetStartedPage from "components/views/GetStartedPage/GetStartedPage"; import { DEFAULT_LIGHT_THEME_NAME } from "pi-ui"; import { render } from "test-utils.js"; import user from "@testing-library/user-event"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; import * as sel from "selectors"; import * as wla from "actions/WalletLoaderActions"; import * as da from "actions/DaemonActions"; @@ -78,7 +78,7 @@ beforeEach(() => { test("render empty wallet chooser view", async () => { render(); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); expect(screen.getByText(/logs/i)).toBeInTheDocument(); expect(screen.getByText(/settings/i)).toBeInTheDocument(); @@ -92,29 +92,29 @@ test("render empty wallet chooser view", async () => { // check tutorials expect(screen.getByText(/learn about decred/i)).toBeInTheDocument(); - user.click(screen.getByText("Decred Intro")); - await wait(() => screen.getByText("Back")); + await user.click(screen.getByText("Decred Intro")); + await waitFor(() => screen.getByText("Back")); // go back - user.click(screen.getByText("Back").nextElementSibling); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await user.click(screen.getByText("Back").nextElementSibling); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); // open onboard tutorial again and go back by finishing it - user.click(screen.getByText("Decred Intro")); - await wait(() => screen.getByText("Back")); + await user.click(screen.getByText("Decred Intro")); + await waitFor(() => screen.getByText("Back")); // step forward const nextButton = screen.getByRole("button", { name: "Next" }); - user.click(nextButton); + await user.click(nextButton); expect(screen.getAllByText("Governance systems").length).toBeTruthy(); // finish - user.click(nextButton); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await user.click(nextButton); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); // check learn the basics - user.click(screen.getByRole("button", { name: "Learn the Basics" })); - await wait(() => screen.getByText("Skip")); + await user.click(screen.getByRole("button", { name: "Learn the Basics" })); + await waitFor(() => screen.getByText("Skip")); // go back - user.click(screen.getByText("Skip")); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await user.click(screen.getByText("Skip")); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); expect(mockGetDaemonSynced).toHaveBeenCalled(); expect(mockIsSPV).toHaveBeenCalled(); @@ -129,7 +129,7 @@ test("render empty wallet chooser view in testnet mode", async () => { mockIsTestNet = selectors.isTestNet = jest.fn(() => true); render(); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); expect(screen.getByTestId("getstarted-pagebody").className).toMatch( /testnetBody/ ); @@ -143,74 +143,74 @@ test("render empty wallet chooser view and click-on&test release notes", async ( const oldestVersionNumber = 130; render(); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); - user.click(screen.getByText(/Release Info/i)); - await wait(() => screen.getByText(/newer version/i)); + await user.click(screen.getByText(/Release Info/i)); + await waitFor(() => screen.getByText(/newer version/i)); const header = screen.getByText(/Decrediton (.*) Released/i); expect(header).toBeInTheDocument(); const newestVersionNumber = readRenderedVersionNumber(header.textContent); // click on `newer version` button in vain - user.click(screen.getByText(/newer version/i)); + await user.click(screen.getByText(/newer version/i)); expect(+readRenderedVersionNumber(header.textContent)).toBe( +newestVersionNumber ); // click on `older version` button until the oldest version reached const olderVersionButton = screen.getByText(/older version/i); - user.click(olderVersionButton); + await user.click(olderVersionButton); let olderVersionNumber = readRenderedVersionNumber(header.textContent); expect(+olderVersionNumber).toBeLessThan(+newestVersionNumber); do { - user.click(olderVersionButton); + await user.click(olderVersionButton); olderVersionNumber = readRenderedVersionNumber(header.textContent); expect(+olderVersionNumber).toBeLessThan(+newestVersionNumber); } while (+olderVersionNumber > +oldestVersionNumber); // click on `older version` button in vain - user.click(olderVersionButton); + await user.click(olderVersionButton); expect(+readRenderedVersionNumber(header.textContent)).toBe( +oldestVersionNumber ); // go back to the newer versions view - user.click(screen.getByText(/newer version/i)); + await user.click(screen.getByText(/newer version/i)); expect(+readRenderedVersionNumber(header.textContent)).toBeGreaterThan( +oldestVersionNumber ); // go back to the wallet chooser view - user.click(screen.getByText(/go back/i).nextElementSibling); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await user.click(screen.getByText(/go back/i).nextElementSibling); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); }); test("click on settings link and go back", async () => { render(); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); - user.click(screen.getByText(/settings/i)); - await wait(() => screen.getByText("Connectivity")); + await user.click(screen.getByText(/settings/i)); + await waitFor(() => screen.getByText("Connectivity")); // go back - user.click( + await user.click( screen.getByRole("button", { name: "Go back" }) ); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); }); test("click on logs view", async () => { render(); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); - user.click(screen.getByText(/logs/i)); - await wait(() => screen.queryByText(/system logs/i)); + await user.click(screen.getByText(/logs/i)); + await waitFor(() => screen.queryByText(/system logs/i)); // go back - user.click(screen.getByText(/go back/i).nextElementSibling); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await user.click(screen.getByText(/go back/i).nextElementSibling); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); }); test("test if app receive daemon connection data from cli", async () => { @@ -229,7 +229,7 @@ test("test if app receive daemon connection data from cli", async () => { }); render(); - await wait(() => + await waitFor(() => expect(mockConnectDaemon).toHaveBeenCalledWith( { rpc_user: rpcCreds.rpcUser, @@ -258,7 +258,7 @@ test("start regular daemon and not receive available wallet", async () => { mockIsSPV = selectors.isSPV = jest.fn(() => false); render(); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); expect(mockStartDaemon).toHaveBeenCalled(); expect(mockSyncDaemon).toHaveBeenCalled(); expect(mockCheckNetworkMatch).toHaveBeenCalled(); @@ -281,7 +281,7 @@ test("start regular daemon and receive sync daemon error", async () => { render(); expect(screen.getByText("Starting Daemon...")).toBeInTheDocument(); - await wait(() => expect(mockSyncDaemon).toHaveBeenCalled()); + await waitFor(() => expect(mockSyncDaemon).toHaveBeenCalled()); expect(screen.queryByText(/Learn about decred/i)).not.toBeInTheDocument(); expect(mockStartDaemon).toHaveBeenCalled(); expect(mockCheckNetworkMatch).not.toHaveBeenCalled(); @@ -302,7 +302,7 @@ test("start regular daemon and receive network match error", async () => { mockIsSPV = selectors.isSPV = jest.fn(() => false); render(); - await wait(() => expect(mockSyncDaemon).toHaveBeenCalled()); + await waitFor(() => expect(mockSyncDaemon).toHaveBeenCalled()); expect(screen.queryByText(/Learn about decred/i)).not.toBeInTheDocument(); expect(mockStartDaemon).toHaveBeenCalled(); expect(mockCheckNetworkMatch).toHaveBeenCalled(); @@ -320,7 +320,7 @@ test("test daemon warning", async () => { () => testDaemonWarningText ); render(); - await wait(() => screen.getByText(testDaemonWarningText)); + await waitFor(() => screen.getByText(testDaemonWarningText)); expect(mockDaemonWarning).toHaveBeenCalled(); wl.getCLIOptions.mockRestore(); }); diff --git a/test/unit/components/views/GetStaredPage/LanguageSelectPage.spec.js b/test/unit/components/views/GetStaredPage/LanguageSelectPage.spec.js index 2619dcf9eb..8c137bd342 100644 --- a/test/unit/components/views/GetStaredPage/LanguageSelectPage.spec.js +++ b/test/unit/components/views/GetStaredPage/LanguageSelectPage.spec.js @@ -29,7 +29,7 @@ beforeEach(() => { selectors.stakeTransactions = jest.fn(() => []); }); -test("render language select page", () => { +test("render language select page", async () => { render(); expect(screen.getByText(/welcome to decrediton/i)).toBeInTheDocument(); expect(screen.getByText(/choose your language/i)).toBeInTheDocument(); @@ -45,7 +45,7 @@ test("render language select page", () => { ).not.toBeInTheDocument(); } - user.click(screen.getByText(testLocalesArray[0].description)); + await user.click(screen.getByText(testLocalesArray[0].description)); expect(mockSortedLocales).toHaveBeenCalled(); mockSortedLocales.mockRestore(); @@ -56,7 +56,7 @@ test("render language select page", () => { ).toBeInTheDocument(); } - user.click( + await user.click( screen.getByText(testLocalesArray[testLocalesArray.length - 1].description) ); // just the choosen language should be visible @@ -69,7 +69,7 @@ test("render language select page", () => { ).not.toBeInTheDocument(); } - user.click(screen.getByText(/continue/i)); + await user.click(screen.getByText(/continue/i)); expect(mockSelectLanguage).toHaveBeenCalledWith({ ...testLocalesArray[testLocalesArray.length - 1], label: testLocalesArray[testLocalesArray.length - 1].description, diff --git a/test/unit/components/views/GetStaredPage/Logs.spec.js b/test/unit/components/views/GetStaredPage/Logs.spec.js index 25f331fb6a..f6df3ce0f4 100644 --- a/test/unit/components/views/GetStaredPage/Logs.spec.js +++ b/test/unit/components/views/GetStaredPage/Logs.spec.js @@ -1,7 +1,8 @@ import Logs from "components/views/GetStartedPage/Logs/Logs"; import { render } from "test-utils.js"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; import user from "@testing-library/user-event"; +import { act } from "react-dom/test-utils"; import * as wa from "wallet"; import * as sel from "selectors"; @@ -47,7 +48,7 @@ const expandLogs = async (linkText, expectedLogs) => { user.click(screen.getByText(linkText)); - await wait(() => + await waitFor(() => Promise.resolve(expect(screen.getByText(expectedLogs)).toBeInTheDocument()) ); }; @@ -57,7 +58,7 @@ const collapseLogs = async (linkText, expectedLogs) => { user.click(screen.getByText(linkText)); - await wait(() => + await waitFor(() => Promise.resolve( expect(screen.queryByText(expectedLogs)).not.toBeInTheDocument() ) @@ -83,6 +84,7 @@ test("render default logs page", async () => { }); test("render all logs and test if auto refresh is working", async () => { + jest.useFakeTimers(); mockLnActive = selectors.lnActive = jest.fn(() => true); mockGetDaemonStarted = selectors.getDaemonStarted = jest.fn(() => true); mockGetWalletReady = selectors.getWalletReady = jest.fn(() => true); @@ -120,18 +122,22 @@ test("render all logs and test if auto refresh is working", async () => { mockGetDcrdLogs = wallet.getDcrlndLogs = jest.fn(() => Promise.resolve(Buffer.from(testDcrlnLogString + "+", "utf-8")) ); - await wait(() => + + act(() => { + jest.advanceTimersByTime(1001); + }); + await waitFor(() => expect(screen.getByText(testDcrdLogString + "+")).toBeInTheDocument() ); - await wait(() => + await waitFor(() => expect(screen.getByText(testDcrwalletLogString + "+")).toBeInTheDocument() ); - await wait(() => + await waitFor(() => expect( screen.getByText(testDcrDecreditonLogString + "+") ).toBeInTheDocument() ); - await wait(() => + await waitFor(() => expect(screen.getByText(testDcrlnLogString + "+")).toBeInTheDocument() ); }); diff --git a/test/unit/components/views/GetStaredPage/PreCreateWallet.spec.js b/test/unit/components/views/GetStaredPage/PreCreateWallet.spec.js index 3a68a3cb69..7fca5dc1dd 100644 --- a/test/unit/components/views/GetStaredPage/PreCreateWallet.spec.js +++ b/test/unit/components/views/GetStaredPage/PreCreateWallet.spec.js @@ -2,7 +2,7 @@ import GetStartedPage from "components/views/GetStartedPage/GetStartedPage"; import { render } from "test-utils.js"; import { fireEvent } from "@testing-library/react"; import user from "@testing-library/user-event"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; import * as sel from "selectors"; import * as wla from "actions/WalletLoaderActions"; import * as da from "actions/DaemonActions"; @@ -10,7 +10,7 @@ import * as trza from "actions/TrezorActions"; import * as ca from "actions/ControlActions"; const testWalletName = "test-wallet-name"; -const invalidCharacters = "/\\.:;#[]$%~"; +const invalidCharacters = "/\\.:;#[[]]$%~"; const testWalletCreateErrorMsg = "test-error-msg"; const usedTestWalletName = "usedTestWalletName"; const testWalletCreationMasterPubKey = "test-wallet-creation-master-pubkey"; @@ -91,31 +91,37 @@ beforeEach(() => { const goToGetStartedView = async () => { render(); - return await wait(() => screen.getByText(/welcome to decrediton/i)); + return await waitFor(() => screen.getByText(/welcome to decrediton/i)); }; const goToCreateNewWalletView = async () => { await goToGetStartedView(); - user.click(screen.getByText(/create a new wallet/i)); - return await wait(() => screen.getByText("Wallet Name")); + fireEvent.click(screen.getByText(/create a new wallet/i)); + return await waitFor(() => screen.getByText("Wallet Name")); }; const goToRestoreWalletView = async () => { await goToGetStartedView(); - user.click(screen.getByText(/restore existing wallet/i)); - return await wait(() => screen.getByText("Wallet Name")); + const restoreButton = screen.getByRole("button", { + name: /restore existing wallet/i + }); + fireEvent.click(restoreButton); + return await waitFor(() => screen.getByText("Wallet Name")); }; test("test when createWallet has been rejected", async () => { await goToCreateNewWalletView(); - user.type(screen.getByPlaceholderText(/choose a name/i), testWalletName); + await user.type( + screen.getByPlaceholderText(/choose a name/i), + testWalletName + ); - user.click(screen.getByText(/creating/i)); - await wait(() => screen.getByText(testWalletCreateErrorMsg)); + await user.click(screen.getByText(/creating/i)); + await waitFor(() => screen.getByText(testWalletCreateErrorMsg)); expect(mockCreateWallet).toHaveBeenCalledWith(testSelectedWallet); - user.click(screen.getByText(/cancel/i)); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await user.click(screen.getByText(/cancel/i)); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); }); test("daemon response success through createWallet function and createWalletPage has been reached", async () => { @@ -123,10 +129,13 @@ test("daemon response success through createWallet function and createWalletPage () => () => Promise.resolve(testSelectedWallet) ); await goToCreateNewWalletView(); - user.type(screen.getByPlaceholderText(/choose a name/i), testWalletName); + await user.type( + screen.getByPlaceholderText(/choose a name/i), + testWalletName + ); - user.click(screen.getByText(/creating/i)); - await wait(() => screen.getByText(/copy seed words to clipboard/i)); + await user.click(screen.getByText(/creating/i)); + await waitFor(() => screen.getByText(/copy seed words to clipboard/i)); expect(mockCreateWallet).toHaveBeenCalledWith(testSelectedWallet); expect(mockGenerateSeed).toHaveBeenCalled(); }); @@ -137,22 +146,22 @@ test("test wallet name input on creating new wallet view", async () => { const continueButton = screen.getByText(/creating/i); const walletNameInput = screen.getByPlaceholderText(/choose a name/i); - user.click(continueButton); + await user.click(continueButton); // walletNameInput is requiered expect(screen.getByText(/this field is required/i)).toBeInTheDocument(); // enter reserved wallet name - user.type(walletNameInput, usedTestWalletName); + await user.type(walletNameInput, usedTestWalletName); expect( screen.getByText(/please choose an unused wallet name/i) ).toBeInTheDocument(); // invalid characters have to removed from walletNameInput's value - user.clear(walletNameInput); - user.type(walletNameInput, testWalletName + invalidCharacters); + await user.clear(walletNameInput); + await user.type(walletNameInput, testWalletName + invalidCharacters); expect(walletNameInput).toHaveValue(testWalletName); - user.click(continueButton); - await wait(() => + await user.click(continueButton); + await waitFor(() => expect(mockCreateWallet).toHaveBeenCalledWith(testSelectedWallet) ); }); @@ -168,21 +177,21 @@ test("test wallet name input on restore wallet", async () => { const walletNameInput = screen.getByPlaceholderText(/choose a name/i); // walletNameInput is requiered - user.click(continueButton); + fireEvent.click(continueButton); expect(screen.getByText(/this field is required/i)).toBeInTheDocument(); // enter reserved wallet name - user.type(walletNameInput, usedTestWalletName); + await user.type(walletNameInput, usedTestWalletName); expect( screen.getByText(/please choose an unused wallet name/i) ).toBeInTheDocument(); // invalid characters have to removed from walletNameInput's value - user.clear(walletNameInput); - user.type(walletNameInput, testWalletName + invalidCharacters); + await user.clear(walletNameInput); + await user.type(walletNameInput, testWalletName + invalidCharacters); expect(walletNameInput).toHaveValue(testWalletName); - user.click(continueButton); - await wait(() => + await user.click(continueButton); + await waitFor(() => expect(mockCreateWallet).toHaveBeenCalledWith(testRestoreSelectedWallet) ); }); @@ -195,29 +204,32 @@ test("test watch only control on restore wallet", async () => { await goToRestoreWalletView(); const continueButton = screen.getByText(/continue/i); - user.type(screen.getByPlaceholderText(/choose a name/i), testWalletName); - user.click(screen.getByText("Advanced Options")); + await user.type( + screen.getByPlaceholderText(/choose a name/i), + testWalletName + ); + await user.click(screen.getByText("Advanced Options")); // toggle Watch only switch const watchOnlySwitch = screen.getByLabelText(/watch only/i); - user.click(watchOnlySwitch); + await user.click(watchOnlySwitch); expect(watchOnlySwitch.checked).toBe(true); const masterPubKeyInput = screen.getByPlaceholderText(/master pub key/i); expect(masterPubKeyInput).toBeInTheDocument(); // master pub key is required - user.click(continueButton); + await user.click(continueButton); expect(screen.getByText(/this field is required/i)).toBeInTheDocument(); // test invalid master pub key const testInvalidMasterPubKey = "test-invalid-master-pub-key"; fireEvent.change(masterPubKeyInput, { target: { value: testInvalidMasterPubKey } }); - await wait(() => + await waitFor(() => { expect(mockValidateMasterPubKey).toHaveBeenCalledWith( testInvalidMasterPubKey - ) - ); - expect(screen.getByText(/invalid master pubkey/i)).toBeInTheDocument(); + ); + expect(screen.getByText(/invalid master pubkey/i)).toBeInTheDocument(); + }); // test valid master pub key const testValidMasterPubKey = "test-valid-master-pub-key"; mockValidateMasterPubKey = controlActions.validateMasterPubKey = jest.fn( @@ -228,35 +240,39 @@ test("test watch only control on restore wallet", async () => { fireEvent.change(masterPubKeyInput, { target: { value: testValidMasterPubKey } }); - await wait(() => - expect(mockValidateMasterPubKey).toHaveBeenCalledWith(testValidMasterPubKey) - ); - expect(screen.queryByText(/invalid master pubkey/i)).not.toBeInTheDocument(); + await waitFor(() => { + expect(mockValidateMasterPubKey).toHaveBeenCalledWith( + testValidMasterPubKey + ); + expect( + screen.queryByText(/invalid master pubkey/i) + ).not.toBeInTheDocument(); + }); // test empty input fireEvent.change(masterPubKeyInput, { target: { value: "" } }); - await wait(() => + await waitFor(() => expect(screen.getByText(/this field is required/i)).toBeInTheDocument() ); fireEvent.change(masterPubKeyInput, { target: { value: testValidMasterPubKey } }); - await wait(() => - expect(mockValidateMasterPubKey).toHaveBeenCalledWith(testValidMasterPubKey) + await waitFor(() => + expect( + screen.queryByText(/this field is required/i) + ).not.toBeInTheDocument() ); + expect(mockValidateMasterPubKey).toHaveBeenCalledWith(testValidMasterPubKey); mockCreateWallet = daemonActions.createWallet = jest.fn( () => () => Promise.resolve(true) ); mockCreateWatchOnlyWalletRequest = wlActions.createWatchOnlyWalletRequest = jest.fn(() => () => Promise.resolve()); - user.click(continueButton); - await wait(() => - expect(mockCreateWallet).toHaveBeenCalledWith(testRestoreSelectedWallet) - ); - await wait(() => + fireEvent.click(continueButton); + await waitFor(() => expect(mockCreateWatchOnlyWalletRequest).toHaveBeenCalledWith( testValidMasterPubKey, undefined, @@ -264,23 +280,24 @@ test("test watch only control on restore wallet", async () => { "" ) ); + expect(mockCreateWallet).toHaveBeenCalledWith(testRestoreSelectedWallet); }, 30000); test("test create trezor-backed wallet page (trezor device is NOT connected)", async () => { await goToGetStartedView(); - user.click(screen.getByText("Setup a Trezor Wallet").parentElement); - await wait(() => screen.getByText(/no trezor is detected/i)); + fireEvent.click(screen.getByText("Setup a Trezor Wallet")); + await waitFor(() => screen.getByText(/no trezor is detected/i)); expect( screen.getByText(/no trezor is detected/i).textContent ).toMatchInlineSnapshot( '"No Trezor is detected. Connect the Device and check if Trezor bridge is installed and running on latest firmware."' ); - user.click(screen.getByText(/connect to trezor/i)); + await user.click(screen.getByText(/connect to trezor/i)); expect(mockTrezorConnect).toHaveBeenCalled(); // go back - user.click(screen.getByText("Back")); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await user.click(screen.getByText("Back")); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); }); test("test create trezor-backed wallet page (trezor device is connected)", async () => { @@ -312,10 +329,10 @@ test("test create trezor-backed wallet page (trezor device is connected)", async } } }); - await wait(() => screen.getByText(/welcome to decrediton/i)); - user.click(screen.getByText("Setup a Trezor Wallet").parentElement); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); + fireEvent.click(screen.getByText("Setup a Trezor Wallet")); - await wait(() => screen.getByText("Wallet Name")); + await waitFor(() => screen.getByText("Wallet Name")); expect(mockEnableTrezor).toHaveBeenCalled(); expect(screen.getByText(`${mockDeviceLabel} Trezor`)).toBeInTheDocument(); @@ -323,22 +340,22 @@ test("test create trezor-backed wallet page (trezor device is connected)", async expect(mockTrezorDevice).toHaveBeenCalled(); // go to trezor config view when device is connected - user.click(screen.getByText("Device Setup")); - await wait(() => screen.getByText("Security")); + await user.click(screen.getByText("Device Setup")); + await waitFor(() => screen.getByText("Security")); // go back - user.click(screen.getByText("Create a Wallet")); - await wait(() => screen.getByText("Wallet Name")); + await user.click(screen.getByText("Create a Wallet")); + await waitFor(() => screen.getByText("Wallet Name")); const continueButton = screen.getByText("Create Wallet"); - user.type( + await user.type( screen.getByPlaceholderText("Choose a name for your Trezor Wallet"), testWalletName ); - user.click(continueButton); - await wait(() => expect(screen.getByTestId("decred-loading"))); + await user.click(continueButton); + await waitFor(() => expect(screen.getByTestId("decred-loading"))); expect(mockGetWalletCreationMasterPubKey).toHaveBeenCalled(); - await wait(() => + await waitFor(() => expect(mockCreateWatchOnlyWalletRequest).toHaveBeenCalledWith( testWalletCreationMasterPubKey, undefined, @@ -360,16 +377,16 @@ test("trezor has to auto-disable when step back from create trezor-backed wallet } } }); - await wait(() => screen.getByText(/welcome to decrediton/i)); - user.click(screen.getByText("Setup a Trezor Wallet").parentElement); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); + fireEvent.click(screen.getByText("Setup a Trezor Wallet")); - await wait(() => screen.getByText("Wallet Name")); + await waitFor(() => screen.getByText("Wallet Name")); expect(mockEnableTrezor).toHaveBeenCalled(); // did not receive deviceLabel expect(screen.getByText("New DCR Trezor")).toBeInTheDocument(); - user.click(screen.getByText(/cancel/i)); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await user.click(screen.getByText(/cancel/i)); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); expect(mockDisableTrezor).toHaveBeenCalled(); }); @@ -385,9 +402,12 @@ test("test testnet logo on creating new wallet view", async () => { /testnetBody/ ); - user.type(screen.getByPlaceholderText(/choose a name/i), testWalletName); - user.click(screen.getByText(/creating/i)); - await wait(() => + await user.type( + screen.getByPlaceholderText(/choose a name/i), + testWalletName + ); + await user.click(screen.getByText(/creating/i)); + await waitFor(() => expect(mockCreateWallet).toHaveBeenCalledWith(testRestoreSelectedWallet) ); }); @@ -405,9 +425,12 @@ test("test testnet logo on restore wallet view", async () => { /testnetBody/ ); - user.type(screen.getByPlaceholderText(/choose a name/i), testWalletName); - user.click(screen.getByText(/continue/i)); - await wait(() => + await user.type( + screen.getByPlaceholderText(/choose a name/i), + testWalletName + ); + await user.click(screen.getByText(/continue/i)); + await waitFor(() => expect(mockCreateWallet).toHaveBeenCalledWith(testRestoreSelectedWallet) ); }); @@ -424,22 +447,22 @@ test("test disable coin type upgrades and gap limit inputs on restore wallet", a }; await goToRestoreWalletView(); - user.click(screen.getByText("Advanced Options")); + await user.click(screen.getByText("Advanced Options")); const continueButton = screen.getByText(/continue/i); const walletNameInput = screen.getByPlaceholderText(/choose a name/i); - user.type(walletNameInput, testWalletName); + await user.type(walletNameInput, testWalletName); const disableCoinTypeUpgradesSwitch = screen.getByLabelText( /disable coin type upgrades/i ); - user.click(disableCoinTypeUpgradesSwitch); + await user.click(disableCoinTypeUpgradesSwitch); expect(disableCoinTypeUpgradesSwitch.checked).toBe(true); const gapLimitInput = screen.getByLabelText(/gap limit:/i); - user.type(gapLimitInput, testRestoreSelectedWallet.value.gapLimit); + await user.type(gapLimitInput, testRestoreSelectedWallet.value.gapLimit); - user.click(continueButton); - await wait(() => + await user.click(continueButton); + await waitFor(() => expect(mockCreateWallet).toHaveBeenCalledWith(testRestoreSelectedWallet) ); }); diff --git a/test/unit/components/views/GetStaredPage/PrivacyPage.spec.js b/test/unit/components/views/GetStaredPage/PrivacyPage.spec.js index 83ef431789..250e2f2ea9 100644 --- a/test/unit/components/views/GetStaredPage/PrivacyPage.spec.js +++ b/test/unit/components/views/GetStaredPage/PrivacyPage.spec.js @@ -38,7 +38,7 @@ beforeEach(() => { selectors.stakeTransactions = jest.fn(() => []); }); -test("render privacy page", () => { +test("render privacy page", async () => { render(); expect(screen.getByTestId("getstarted-pagebody").className).not.toMatch( @@ -56,7 +56,7 @@ test("render privacy page", () => { expect(standardLabel.nextSibling.textContent).toMatchInlineSnapshot( '"Enables connections to most services for a better user experience and full access to features (such as version update, VSP listing, Politeia, etc). Recommended for most users."' ); - user.click(standardLabel); + await user.click(standardLabel); expect(mockSetupStandardPrivacy).toHaveBeenCalledTimes(1); const noOutboundConnectionsLabel = screen.getByText( @@ -68,7 +68,7 @@ test("render privacy page", () => { ).toMatchInlineSnapshot( '"Disables all connections to third party (non-dcrd/non-dcrwallet) services. This may prevent you from using certain features of the app. Recommended for advanced users."' ); - user.click(noOutboundConnectionsLabel); + await user.click(noOutboundConnectionsLabel); expect(mockSetupDisabledPrivacy).toHaveBeenCalledTimes(1); const customizeAllowedConnectionsLabel = screen.getByText( @@ -84,12 +84,12 @@ test("render privacy page", () => { expect(mockTempSettings).toHaveBeenCalled(); }); -test("test custom privacy options", () => { +test("test custom privacy options", async () => { render(); const customizeAllowedConnectionsLabel = screen.getByText( /customize allowed connections/i ); - user.click(customizeAllowedConnectionsLabel); + await user.click(customizeAllowedConnectionsLabel); const customPrivacyOptionsLabel = screen.getByText(/custom privacy options/i); expect(customPrivacyOptionsLabel).toBeInTheDocument(); @@ -103,7 +103,9 @@ test("test custom privacy options", () => { expect( updateCheckLabel.parentElement.nextSibling.textContent ).toMatchInlineSnapshot('"Get latest released version from github.org"'); - user.click(updateCheckLabel.parentElement.getElementsByTagName("input")[0]); + await user.click( + updateCheckLabel.parentElement.getElementsByTagName("input")[0] + ); const networkInformationLabel = screen.getByText("Network Information"); expect( @@ -111,7 +113,7 @@ test("test custom privacy options", () => { ).toMatchInlineSnapshot( '"General network information (block height, etc) from decred.org"' ); - user.click( + await user.click( networkInformationLabel.parentElement.getElementsByTagName("input")[0] ); @@ -121,29 +123,33 @@ test("test custom privacy options", () => { ).toMatchInlineSnapshot( '"List and vote on proposals on proposals.decred.org"' ); - user.click(politeiaLabel.parentElement.getElementsByTagName("input")[0]); + await user.click( + politeiaLabel.parentElement.getElementsByTagName("input")[0] + ); const vspListingLabel = screen.getByText(/vsp listing/i); expect( vspListingLabel.parentElement.nextSibling.textContent ).toMatchInlineSnapshot('"List of currently available VSPs from decred.org"'); - user.click(vspListingLabel.parentElement.getElementsByTagName("input")[0]); + await user.click( + vspListingLabel.parentElement.getElementsByTagName("input")[0] + ); const decredBlockExplorerLabel = screen.getByText(/decred block explorer/i); expect( decredBlockExplorerLabel.parentElement.nextSibling.textContent ).toMatchInlineSnapshot('"Access chain information from dcrdata.decred.org"'); - user.click( + await user.click( decredBlockExplorerLabel.parentElement.getElementsByTagName("input")[0] ); expect(mockUpdateStateSettingsChanged).toHaveBeenCalledTimes(5); - user.click(screen.getByText(/accept/i)); + await user.click(screen.getByText(/accept/i)); expect(mockSaveSettings).toHaveBeenCalledTimes(1); expect(mockFinishPrivacy).toHaveBeenCalledTimes(1); - user.click(screen.getByText(/cancel/i)); + await user.click(screen.getByText(/cancel/i)); expect( screen.getByText(/customize allowed connections/i) ).toBeInTheDocument(); diff --git a/test/unit/components/views/GetStaredPage/SetMixedAcctPage.spec.js b/test/unit/components/views/GetStaredPage/SetMixedAcctPage.spec.js index 66ae3cdfe8..f60525d1fd 100644 --- a/test/unit/components/views/GetStaredPage/SetMixedAcctPage.spec.js +++ b/test/unit/components/views/GetStaredPage/SetMixedAcctPage.spec.js @@ -1,6 +1,6 @@ import SetMixedAcctPage from "components/views/GetStartedPage/SetupWallet/SetMixedAcctPage/SetMixedAcctPage"; import { render } from "test-utils.js"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; import { fireEvent } from "@testing-library/react"; import user from "@testing-library/user-event"; import * as ama from "actions/AccountMixerActions"; @@ -40,7 +40,7 @@ beforeEach(() => { test("test SetMixedAcctPage", async () => { render(); expect(mockGetCoinjoinOutputspByAcct).toHaveBeenCalled(); - await wait(() => screen.getByText(/continue/i)); + await waitFor(() => screen.getByText(/continue/i)); expect(screen.getByText(/looks like you/i).textContent) .toMatchInlineSnapshot(` "Looks like you have accounts with coinjoin outputs. Past @@ -69,11 +69,11 @@ test("test SetMixedAcctPage", async () => { const secondUnMixedCheckbox = unmixedAccountCheckboxes[1]; fireEvent.click(firstMixedCheckbox); - await wait(() => expect(firstMixedCheckbox.checked).toEqual(true)); + await waitFor(() => expect(firstMixedCheckbox.checked).toEqual(true)); // clicking on unmixed checkbox should uncheck the same account's mixed checkbox fireEvent.click(firstUnMixedCheckbox); - await wait(() => expect(firstMixedCheckbox.checked).toEqual(false)); + await waitFor(() => expect(firstMixedCheckbox.checked).toEqual(false)); expect( screen.getByText(/you need to set/i).textContent ).toMatchInlineSnapshot( @@ -82,23 +82,23 @@ test("test SetMixedAcctPage", async () => { // Click on another account for change account fireEvent.click(secondUnMixedCheckbox); - await wait(() => expect(secondUnMixedCheckbox.checked).toEqual(true)); - await wait(() => expect(firstUnMixedCheckbox.checked).toEqual(false)); + await waitFor(() => expect(secondUnMixedCheckbox.checked).toEqual(true)); + await waitFor(() => expect(firstUnMixedCheckbox.checked).toEqual(false)); // clicking on mixed checkbox should uncheck the same account's unmixed checkbox fireEvent.click(secondMixedCheckbox); - await wait(() => expect(secondMixedCheckbox.checked).toEqual(true)); - await wait(() => expect(secondUnMixedCheckbox.checked).toEqual(false)); + await waitFor(() => expect(secondMixedCheckbox.checked).toEqual(true)); + await waitFor(() => expect(secondUnMixedCheckbox.checked).toEqual(false)); // // Click on the first account for unmixed account fireEvent.click(firstUnMixedCheckbox); - await wait(() => expect(firstUnMixedCheckbox.checked).toEqual(true)); - await wait(() => expect(secondUnMixedCheckbox.checked).toEqual(false)); - await wait(() => expect(secondMixedCheckbox.checked).toEqual(true)); + await waitFor(() => expect(firstUnMixedCheckbox.checked).toEqual(true)); + await waitFor(() => expect(secondUnMixedCheckbox.checked).toEqual(false)); + await waitFor(() => expect(secondMixedCheckbox.checked).toEqual(true)); expect(screen.queryByText(/you need to set/i)).not.toBeInTheDocument(); expect(continueButton.disabled).toBe(false); - user.click(continueButton); + await user.click(continueButton); expect(mockRenameAccountAttempt).toHaveBeenCalledWith( testCoinjoinSumByAcct[1].acctIdx, CHANGE_ACCOUNT diff --git a/test/unit/components/views/GetStaredPage/SetupWallet/ProcessManagedTickets.spec.js b/test/unit/components/views/GetStaredPage/SetupWallet/ProcessManagedTickets.spec.js index 852fa3e84e..89b95efa3c 100644 --- a/test/unit/components/views/GetStaredPage/SetupWallet/ProcessManagedTickets.spec.js +++ b/test/unit/components/views/GetStaredPage/SetupWallet/ProcessManagedTickets.spec.js @@ -1,7 +1,6 @@ import ProcessManagedTickets from "components/views/GetStartedPage/SetupWallet/ProcessManagedTickets"; import { render } from "test-utils.js"; -import { screen, wait } from "@testing-library/react"; -import user from "@testing-library/user-event"; +import { screen, waitFor } from "@testing-library/react"; import * as sel from "selectors"; import * as wal from "wallet"; import * as arrs from "../../../../../../app/helpers/arrays"; @@ -108,37 +107,40 @@ const initialState = { } }; -test("skip ProcessManagedTickets and show error", () => { - render( +test("skip ProcessManagedTickets and show error", async () => { + const { user } = render( ); - user.click(getSkipButton()); + await user.click(getSkipButton()); expect(screen.getByText(testError)).toBeInTheDocument(); expect(mockCancel).toHaveBeenCalled(); }); test("do ProcessManagedTickets - in a private wallet", async () => { mockUnlockLockAndGetAccountsAttempt(); - render(, { - initialState - }); + const { user } = render( + , + { + initialState + } + ); const continueButton = getContinueButton(); - user.click(continueButton); + await user.click(continueButton); expect(screen.getByText("Passphrase")).toBeInTheDocument(); // cancel first - user.click(getCancelButton()); + await user.click(getCancelButton()); - user.click(continueButton); - user.type(screen.getByLabelText("Private Passphrase"), testPassphrase); - user.click(getModalContinueButton()); + await user.click(continueButton); + await user.type(screen.getByLabelText("Private Passphrase"), testPassphrase); + await user.click(getModalContinueButton()); - await wait(() => expect(mockSend).toHaveBeenCalled()); + await waitFor(() => expect(mockSend).toHaveBeenCalled()); expect(mockProcessManagedTickets).toHaveBeenNthCalledWith( 1, @@ -201,22 +203,25 @@ test("do ProcessManagedTickets - in a default wallet, available vps pubkeys have selectors.getMixedAccount = jest.fn(() => null); selectors.getChangeAccount = jest.fn(() => null); mockUnlockLockAndGetAccountsAttempt(); - render(, { - initialState - }); + const { user } = render( + , + { + initialState + } + ); const continueButton = getContinueButton(); - user.click(continueButton); + await user.click(continueButton); expect(screen.getByText("Passphrase")).toBeInTheDocument(); // cancel first - user.click(getCancelButton()); + await user.click(getCancelButton()); - user.click(continueButton); - user.type(screen.getByLabelText("Private Passphrase"), testPassphrase); - user.click(getModalContinueButton()); + await user.click(continueButton); + await user.type(screen.getByLabelText("Private Passphrase"), testPassphrase); + await user.click(getModalContinueButton()); - await wait(() => expect(mockSend).toHaveBeenCalled()); + await waitFor(() => expect(mockSend).toHaveBeenCalled()); expect(mockProcessManagedTickets).toHaveBeenNthCalledWith( 1, @@ -279,22 +284,25 @@ test("do ProcessManagedTickets - in a default wallet, available vps pubkeys have selectors.getMixedAccount = jest.fn(() => null); selectors.getChangeAccount = jest.fn(() => null); mockUnlockLockAndGetAccountsAttempt(); - render(, { - initialState - }); + const { user } = render( + , + { + initialState + } + ); const continueButton = getContinueButton(); - user.click(continueButton); + await user.click(continueButton); expect(screen.getByText("Passphrase")).toBeInTheDocument(); // cancel first - user.click(getCancelButton()); + await user.click(getCancelButton()); - user.click(continueButton); - user.type(screen.getByLabelText("Private Passphrase"), testPassphrase); - user.click(getModalContinueButton()); + await user.click(continueButton); + await user.type(screen.getByLabelText("Private Passphrase"), testPassphrase); + await user.click(getModalContinueButton()); - await wait(() => expect(mockSend).toHaveBeenCalled()); + await waitFor(() => expect(mockSend).toHaveBeenCalled()); expect(mockProcessManagedTickets).toHaveBeenNthCalledWith( 1, @@ -355,16 +363,19 @@ test("do ProcessManagedTickets - failed to fetch vsps", async () => { mockGetAllVSPs = wallet.getAllVSPs = jest.fn(() => { throw testError; }); - render(, { - initialState - }); + const { user } = render( + , + { + initialState + } + ); const continueButton = getContinueButton(); - user.click(continueButton); + await user.click(continueButton); - user.type(screen.getByLabelText("Private Passphrase"), testPassphrase); - user.click(getModalContinueButton()); + await user.type(screen.getByLabelText("Private Passphrase"), testPassphrase); + await user.click(getModalContinueButton()); - await wait(() => expect(mockSend).toHaveBeenCalled()); + await waitFor(() => expect(mockSend).toHaveBeenCalled()); expect(mockGetAllVSPs).toHaveBeenCalled(); expect(mockProcessManagedTickets).not.toHaveBeenCalled(); diff --git a/test/unit/components/views/GetStaredPage/SetupWallet/ProcessUnmanagedTickets.spec.js b/test/unit/components/views/GetStaredPage/SetupWallet/ProcessUnmanagedTickets.spec.js index 791647422a..408f0283ff 100644 --- a/test/unit/components/views/GetStaredPage/SetupWallet/ProcessUnmanagedTickets.spec.js +++ b/test/unit/components/views/GetStaredPage/SetupWallet/ProcessUnmanagedTickets.spec.js @@ -1,7 +1,6 @@ import ProcessUnmanagedTickets from "components/views/GetStartedPage/SetupWallet/ProcessUnmanagedTickets"; import { render } from "test-utils.js"; -import { screen, wait } from "@testing-library/react"; -import user from "@testing-library/user-event"; +import { screen, waitFor } from "@testing-library/react"; import * as sel from "selectors"; import * as wal from "wallet"; import * as arrs from "../../../../../../app/helpers/arrays"; @@ -123,8 +122,8 @@ const initialState = { } }; -test("skip ProcessUnmanagedTickets and show error", () => { - render( +test("skip ProcessUnmanagedTickets and show error", async () => { + const { user } = render( { /> ); expect(screen.getByText(testError)).toBeInTheDocument(); - user.click(getSkipButton()); + await user.click(getSkipButton()); expect(mockCancel).toHaveBeenCalled(); }); test("do ProcessUnmanagedTickets - in a private wallet", async () => { mockUnlockLockAndGetAccountsAttempt(); - render(, { - initialState - }); + const { user } = render( + , + { + initialState + } + ); const continueButton = getContinueButton(); expect(continueButton.disabled).toBe(true); - user.click(screen.getByText("Select VSP...")); - user.click(screen.getByText(mockAvailableMainnetVsps[0].host)); + await user.click(screen.getByText("Select VSP...")); + await user.click(screen.getByText(mockAvailableMainnetVsps[0].host)); expect(screen.getByText("Loading")).toBeInTheDocument(); - await wait(() => expect(getContinueButton().disabled).toBeFalsy()); + await waitFor(() => expect(getContinueButton().disabled).toBeFalsy()); expect(screen.queryByText("Loading")).not.toBeInTheDocument(); expect( screen.getByText(mockAvailableMainnetVsps[0].host) ).toBeInTheDocument(); - user.click(continueButton); + await user.click(continueButton); expect(screen.getByText("Passphrase")).toBeInTheDocument(); // cancel first - user.click(getCancelButton()); + await user.click(getCancelButton()); - user.click(continueButton); - user.type(screen.getByLabelText("Private Passphrase"), testPassphrase); - user.click(getModalContinueButton()); + await user.click(continueButton); + await user.type(screen.getByLabelText("Private Passphrase"), testPassphrase); + await user.click(getModalContinueButton()); - await wait(() => expect(mockSend).toHaveBeenCalled()); + await waitFor(() => expect(mockSend).toHaveBeenCalled()); expect(mockProcessUnmanagedTickets).toHaveBeenCalledWith( testWalletService, @@ -202,32 +204,35 @@ test("do ProcessUnmanagedTickets - in a default wallet", async () => { selectors.getMixedAccount = jest.fn(() => null); selectors.getChangeAccount = jest.fn(() => null); mockUnlockLockAndGetAccountsAttempt(); - render(, { - initialState - }); + const { user } = render( + , + { + initialState + } + ); const continueButton = getContinueButton(); expect(continueButton.disabled).toBe(true); - user.click(screen.getByText("Select VSP...")); - user.click(screen.getByText(mockAvailableMainnetVsps[0].host)); + await user.click(screen.getByText("Select VSP...")); + await user.click(screen.getByText(mockAvailableMainnetVsps[0].host)); expect(screen.getByText("Loading")).toBeInTheDocument(); - await wait(() => expect(getContinueButton().disabled).toBeFalsy()); + await waitFor(() => expect(getContinueButton().disabled).toBeFalsy()); expect(screen.queryByText("Loading")).not.toBeInTheDocument(); expect( screen.getByText(mockAvailableMainnetVsps[0].host) ).toBeInTheDocument(); - user.click(continueButton); + await user.click(continueButton); expect(screen.getByText("Passphrase")).toBeInTheDocument(); // cancel first - user.click(getCancelButton()); + await user.click(getCancelButton()); - user.click(continueButton); - user.type(screen.getByLabelText("Private Passphrase"), testPassphrase); - user.click(getModalContinueButton()); + await user.click(continueButton); + await user.type(screen.getByLabelText("Private Passphrase"), testPassphrase); + await user.click(getModalContinueButton()); - await wait(() => expect(mockSend).toHaveBeenCalled()); + await waitFor(() => expect(mockSend).toHaveBeenCalled()); expect(mockProcessUnmanagedTickets).toHaveBeenCalledWith( testWalletService, @@ -264,40 +269,42 @@ test("do ProcessUnmanagedTickets - in a default wallet", async () => { test("do ProcessUnmanagedTickets - vsp listing is not enabled", async () => { mockUnlockLockAndGetAccountsAttempt(); - render(, { - initialState: cloneDeep({ - ...initialState, - settings: { - ...initialState.settings, - tempSettings: { - ...initialState.settings.tempSettings, - allowedExternalRequests: [] + const { user } = render( + , + { + initialState: cloneDeep({ + ...initialState, + settings: { + ...initialState.settings, + tempSettings: { + ...initialState.settings.tempSettings, + allowedExternalRequests: [] + } } - } - }) - }); + }) + } + ); const continueButton = getContinueButton(); expect(continueButton.disabled).toBe(true); - user.type(screen.getByRole("combobox"), testCustomVspHost); - user.click(screen.getByText(`Create "${testCustomVspHost}"`)); - expect(screen.getByText("Loading")).toBeInTheDocument(); - await wait(() => + await user.type(screen.getByRole("combobox"), testCustomVspHost); + await user.click(screen.getByText(`Create "${testCustomVspHost}"`)); + await waitFor(() => expect(screen.getByText(testCustomVspHost)).toBeInTheDocument() ); - user.click(continueButton); + await user.click(continueButton); expect(screen.getByText("Passphrase")).toBeInTheDocument(); // cancel first - user.click(getCancelButton()); + await user.click(getCancelButton()); - user.click(continueButton); - user.type(screen.getByLabelText("Private Passphrase"), testPassphrase); - user.click(getModalContinueButton()); + await user.click(continueButton); + await user.type(screen.getByLabelText("Private Passphrase"), testPassphrase); + await user.click(getModalContinueButton()); - await wait(() => expect(mockSend).toHaveBeenCalled()); + await waitFor(() => expect(mockSend).toHaveBeenCalled()); expect(mockProcessUnmanagedTickets).toHaveBeenCalledWith( testWalletService, @@ -339,29 +346,32 @@ test("do ProcessUnManagedTickets - failed", async () => { } ); mockUnlockLockAndGetAccountsAttempt(); - render(, { - initialState - }); + const { user } = render( + , + { + initialState + } + ); const continueButton = getContinueButton(); expect(continueButton.disabled).toBe(true); - user.click(screen.getByText("Select VSP...")); - user.click(screen.getByText(mockAvailableMainnetVsps[0].host)); - expect(screen.getByText("Loading")).toBeInTheDocument(); - await wait(() => expect(getContinueButton().disabled).toBeFalsy()); + await user.click(screen.getByText("Select VSP...")); + await user.click(screen.getByText(mockAvailableMainnetVsps[0].host)); + await waitFor(() => screen.getByText("Loading")); + await waitFor(() => expect(getContinueButton().disabled).toBeFalsy()); expect(screen.queryByText("Loading")).not.toBeInTheDocument(); expect( screen.getByText(mockAvailableMainnetVsps[0].host) ).toBeInTheDocument(); - user.click(continueButton); + await user.click(continueButton); expect(screen.getByText("Passphrase")).toBeInTheDocument(); - user.click(continueButton); - user.type(screen.getByLabelText("Private Passphrase"), testPassphrase); - user.click(getModalContinueButton()); + await user.click(continueButton); + await user.type(screen.getByLabelText("Private Passphrase"), testPassphrase); + await user.click(getModalContinueButton()); - await wait(() => expect(mockSend).toHaveBeenCalled()); + await waitFor(() => expect(mockSend).toHaveBeenCalled()); expect(mockProcessUnmanagedTickets).toHaveBeenCalledWith( testWalletService, diff --git a/test/unit/components/views/GetStaredPage/SetupWallet/ResendVotesToRecentlyUpdatedVSPs.spec.js b/test/unit/components/views/GetStaredPage/SetupWallet/ResendVotesToRecentlyUpdatedVSPs.spec.js index 3274c4cfce..fc168194b8 100644 --- a/test/unit/components/views/GetStaredPage/SetupWallet/ResendVotesToRecentlyUpdatedVSPs.spec.js +++ b/test/unit/components/views/GetStaredPage/SetupWallet/ResendVotesToRecentlyUpdatedVSPs.spec.js @@ -1,6 +1,6 @@ import ResendVotesToRecentlyUpdatedVSPs from "components/views/GetStartedPage/SetupWallet/ResendVotesToRecentlyUpdatedVSPs"; import { render } from "test-utils.js"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; import user from "@testing-library/user-event"; import * as sel from "selectors"; import * as vspa from "actions/VSPActions"; @@ -70,11 +70,11 @@ const getPrivatePassphraseInput = () => const queryPrivatePassphraseInput = () => screen.queryByLabelText("Private Passphrase"); -test("skip ResendVotesToRecentlyUpdatedVSPs", () => { +test("skip ResendVotesToRecentlyUpdatedVSPs", async () => { render( ); - user.click(getSkipButton()); + await user.click(getSkipButton()); expect(mockCancel).toHaveBeenCalled(); }); @@ -129,28 +129,30 @@ test("test resend votes", async () => { ); const continueButton = getContinueButton(); - user.click(continueButton); + await user.click(continueButton); expect(getPrivatePassphraseInput()).toBeInTheDocument(); // cancel first - user.click(getCancelButton()); + await user.click(getCancelButton()); expect(queryPrivatePassphraseInput()).not.toBeInTheDocument(); // continue again - user.click(continueButton); + await user.click(continueButton); expect(getPrivatePassphraseInput()).toBeInTheDocument(); const continuePassphraseButton = getAllContinueButton()[1]; expect(continuePassphraseButton.disabled).toBeTruthy(); - user.type(getPrivatePassphraseInput(), testPrivatePassphrase); + await user.type(getPrivatePassphraseInput(), testPrivatePassphrase); expect(continuePassphraseButton.disabled).toBeFalsy(); - user.click(continuePassphraseButton); + await user.click(continuePassphraseButton); expect(queryPrivatePassphraseInput()).not.toBeInTheDocument(); expect(mockResendVSPDVoteChoices).toHaveBeenCalledWith( mockVSPs, testPrivatePassphrase ); - await wait(() => expect(mockSend).toHaveBeenCalledWith({ type: "CONTINUE" })); + await waitFor(() => + expect(mockSend).toHaveBeenCalledWith({ type: "CONTINUE" }) + ); }); test("check label when there is just one vsp", () => { @@ -186,18 +188,18 @@ test("test resend votes failed", async () => { ); // continue again - user.click(getContinueButton()); + await user.click(getContinueButton()); expect(getPrivatePassphraseInput()).toBeInTheDocument(); const continuePassphraseButton = getAllContinueButton()[1]; - user.type(getPrivatePassphraseInput(), testPrivatePassphrase); - user.click(continuePassphraseButton); + await user.type(getPrivatePassphraseInput(), testPrivatePassphrase); + await user.click(continuePassphraseButton); expect(queryPrivatePassphraseInput()).not.toBeInTheDocument(); expect(mockResendVSPDVoteChoices).toHaveBeenCalledWith( mockVSPs, testPrivatePassphrase ); - await wait(() => + await waitFor(() => expect(mockSend).toHaveBeenCalledWith({ type: "ERROR", error: resendVSPDVoteChoicesError diff --git a/test/unit/components/views/GetStaredPage/SpvChoicePage.spec.js b/test/unit/components/views/GetStaredPage/SpvChoicePage.spec.js index 571cfdd44a..d1af585bcd 100644 --- a/test/unit/components/views/GetStaredPage/SpvChoicePage.spec.js +++ b/test/unit/components/views/GetStaredPage/SpvChoicePage.spec.js @@ -17,7 +17,7 @@ beforeEach(() => { selectors.stakeTransactions = jest.fn(() => []); }); -test("render SPV choice page", () => { +test("render SPV choice page", async () => { render(); expect(screen.getByTestId("getstarted-pagebody").className).not.toMatch( @@ -35,7 +35,7 @@ test("render SPV choice page", () => { expect(enableSpvLabel.nextSibling.textContent).toMatchInlineSnapshot( "\"SPV will allow your wallets to be restored and used much more quickly. This speed comes at cost, with blocks not being fully verified. It's 'less secure' but very unlikely that there will be any problems.\"" ); - user.click(enableSpvLabel); + await user.click(enableSpvLabel); expect(mockToggleSpv).toHaveBeenCalledWith(true); mockToggleSpv.mockClear(); @@ -44,7 +44,7 @@ test("render SPV choice page", () => { expect(disableSpvLabel.nextSibling.textContent).toMatchInlineSnapshot( '"This will use the regular Decred daemon and fully verify blocks. This will take longer but is fully secure. Any block or mined transaction can be fully trusted."' ); - user.click(disableSpvLabel); + await user.click(disableSpvLabel); expect(mockToggleSpv).toHaveBeenCalledWith(false); }); diff --git a/test/unit/components/views/GetStaredPage/TutorialPage.spec.js b/test/unit/components/views/GetStaredPage/TutorialPage.spec.js index 70b1089d83..a0826f4132 100644 --- a/test/unit/components/views/GetStaredPage/TutorialPage.spec.js +++ b/test/unit/components/views/GetStaredPage/TutorialPage.spec.js @@ -28,48 +28,48 @@ const checkIndicator = (currentIndex) => { } }; -test("clicking through the tutorial page using `Next` button", () => { +test("clicking through the tutorial page using `Next` button", async () => { render(); // check first page checkIndicator(0); // check second page - user.click(screen.getByText(/next/i)); + await user.click(screen.getByText(/next/i)); checkIndicator(1); // check third page - user.click(screen.getByText(/next/i)); + await user.click(screen.getByText(/next/i)); checkIndicator(2); // check fourth page - user.click(screen.getByText(/next/i)); + await user.click(screen.getByText(/next/i)); checkIndicator(3); // leave page using `Next` button - user.click(screen.getByText(/next/i)); + await user.click(screen.getByText(/next/i)); // leave page using `Finish` button - user.click(screen.getByText(/finish/i)); + await user.click(screen.getByText(/finish/i)); expect(mockFinishTutorial).toHaveBeenCalledTimes(2); }); -test("clicking through the tutorial page using the indicators", () => { +test("clicking through the tutorial page using the indicators", async () => { render(); // check first page checkIndicator(0); // check second page - user.click(screen.getByRole("button", { name: "step-1" })); + await user.click(screen.getByRole("button", { name: "step-1" })); checkIndicator(1); // check third page - user.click(screen.getByRole("button", { name: "step-2" })); + await user.click(screen.getByRole("button", { name: "step-2" })); checkIndicator(2); // check fourth page - user.click(screen.getByRole("button", { name: "step-3" })); + await user.click(screen.getByRole("button", { name: "step-3" })); checkIndicator(3); // check first page again - user.click(screen.getByRole("button", { name: "step-0" })); + await user.click(screen.getByRole("button", { name: "step-0" })); checkIndicator(0); }); -test("leave tutorial page using `Skip` button", () => { +test("leave tutorial page using `Skip` button", async () => { render(); - user.click(screen.getByText(/skip/i)); + await user.click(screen.getByText(/skip/i)); expect(mockFinishTutorial).toHaveBeenCalledTimes(1); }); diff --git a/test/unit/components/views/GetStaredPage/WalletSelection.spec.js b/test/unit/components/views/GetStaredPage/WalletSelection.spec.js index 09a9401bad..fba0d6e62b 100644 --- a/test/unit/components/views/GetStaredPage/WalletSelection.spec.js +++ b/test/unit/components/views/GetStaredPage/WalletSelection.spec.js @@ -1,7 +1,7 @@ import GetStartedPage from "components/views/GetStartedPage/GetStartedPage"; import { render } from "test-utils.js"; import user from "@testing-library/user-event"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor, fireEvent } from "@testing-library/react"; import * as sel from "selectors"; import * as da from "actions/DaemonActions"; import * as wla from "actions/WalletLoaderActions"; @@ -117,7 +117,7 @@ beforeEach(() => { test("render wallet chooser view", async () => { render(); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); expect(mockSortedAvailableWallets).toHaveBeenCalled(); expect(mockGetSelectedWallet).toHaveBeenCalled(); @@ -161,31 +161,31 @@ test("render wallet chooser view", async () => { test("test editing wallets", async () => { render(); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); - user.click(screen.getByText(/edit wallets/i)); + await user.click(screen.getByText(/edit wallets/i)); expect(screen.getByText("Close")).toBeInTheDocument(); expect(screen.getAllByText(/remove wallet/i).length).toBe( testAvailableWallets.length ); // test the cancel flow - user.click(screen.getAllByText(/remove wallet/i)[0].nextElementSibling); + await user.click(screen.getAllByText(/remove wallet/i)[0].nextElementSibling); expect( screen.getByText(/warning this action/i).textContent ).toMatchInlineSnapshot( '"Warning this action is permanent! Please make sure you have backed up your wallet\'s seed before proceeding."' ); - user.click(screen.getByText(/cancel/i)); + await user.click(screen.getByText(/cancel/i)); expect(screen.queryByText(/warning this action/i)).not.toBeInTheDocument(); // test the confirm flow - user.click(screen.getAllByText(/remove wallet/i)[0].nextElementSibling); - user.click(screen.getByText(/confirm/i)); + await user.click(screen.getAllByText(/remove wallet/i)[0].nextElementSibling); + await user.click(screen.getByText(/confirm/i)); expect(mockRemoveWallet).toHaveBeenCalledWith(testAvailableWallets[0]); expect(screen.queryByText(/warning this action/i)).not.toBeInTheDocument(); - user.click(screen.getByText("Close")); + await user.click(screen.getByText("Close")); expect(screen.queryByText("Close")).not.toBeInTheDocument(); expect(screen.getByText(/edit wallets/i)).toBeInTheDocument(); }); @@ -210,22 +210,25 @@ const launchWallet = async (ms) => { }) ); render(); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); const wallet = screen.getByText(testAvailableWallets[1].value.wallet); - user.click(wallet); - await wait(() => + await user.click(wallet); + await waitFor(() => expect(mockStartWallet).toHaveBeenCalledWith(testAvailableWallets[1]) ); }; test("launch a wallet and click on Save button on Auto Wallet launching modal", async () => { await launchWallet(); - await wait(() => screen.getByText("Finishing to load wallet")); + await waitFor(() => { + screen.getByText("Finishing to load wallet"); + screen.getByRole("button", { name: "Open Wallet" }); + }); - user.click(screen.getByRole("button", { name: "Open Wallet" })); - user.click(screen.getByRole("button", { name: "Save" })); - await wait(() => screen.queryByText("Automatic Wallet Launching")); + await user.click(screen.getByRole("button", { name: "Open Wallet" })); + await user.click(screen.getByRole("button", { name: "Save" })); + await waitFor(() => screen.queryByText("Automatic Wallet Launching")); expect(screen.getByText("Setup Wallet")).toBeInTheDocument(); @@ -238,12 +241,15 @@ test("launch a wallet and click on Save button on Auto Wallet launching modal", test("launch a wallet and click on checkbox, then the Save button on Auto Wallet launching modal", async () => { await launchWallet(); - await wait(() => screen.getByText("Finishing to load wallet")); + await waitFor(() => { + screen.getByText("Finishing to load wallet"); + screen.getByRole("button", { name: "Open Wallet" }); + }); - user.click(screen.getByRole("button", { name: "Open Wallet" })); - user.click(screen.getByRole("checkbox")); - user.click(screen.getByRole("button", { name: "Save" })); - await wait(() => screen.queryByText("Automatic Wallet Launching")); + await user.click(screen.getByRole("button", { name: "Open Wallet" })); + await user.click(screen.getByRole("checkbox")); + await user.click(screen.getByRole("button", { name: "Save" })); + await waitFor(() => screen.queryByText("Automatic Wallet Launching")); expect(screen.getByText("Setup Wallet")).toBeInTheDocument(); expect(mockSetAutoWalletLaunching).toHaveBeenCalledWith(false); @@ -251,11 +257,14 @@ test("launch a wallet and click on checkbox, then the Save button on Auto Wallet test("launch a wallet and click on Ask me later button on Auto Wallet launching modal", async () => { await launchWallet(); - await wait(() => screen.getByText("Finishing to load wallet")); + await waitFor(() => { + screen.getByText("Finishing to load wallet"); + screen.getByRole("button", { name: "Open Wallet" }); + }); - user.click(screen.getByRole("button", { name: "Open Wallet" })); - user.click(screen.getByRole("button", { name: "Ask me later" })); - await wait(() => screen.queryByText("Automatic Wallet Launching")); + await user.click(screen.getByRole("button", { name: "Open Wallet" })); + await user.click(screen.getByRole("button", { name: "Ask me later" })); + await waitFor(() => screen.queryByText("Automatic Wallet Launching")); expect(screen.getByText("Setup Wallet")).toBeInTheDocument(); expect(mockSetAutoWalletLaunching).not.toHaveBeenCalled(); @@ -263,11 +272,14 @@ test("launch a wallet and click on Ask me later button on Auto Wallet launching test("launch a wallet and close Auto Wallet launching modal", async () => { await launchWallet(); - await wait(() => screen.getByText("Finishing to load wallet")); + await waitFor(() => { + screen.getByText("Finishing to load wallet"); + screen.getByRole("button", { name: "Open Wallet" }); + }); - user.click(screen.getByRole("button", { name: "Open Wallet" })); - user.click(screen.getByRole("button", { name: "Close" })); - await wait(() => screen.queryByText("Automatic Wallet Launching")); + await user.click(screen.getByRole("button", { name: "Open Wallet" })); + await user.click(screen.getByRole("button", { name: "Close" })); + await waitFor(() => screen.queryByText("Automatic Wallet Launching")); expect(screen.getByText("Setup Wallet")).toBeInTheDocument(); expect(mockSetAutoWalletLaunching).not.toHaveBeenCalled(); @@ -277,28 +289,28 @@ test("launch a wallet and open wallet automatically ", async () => { selectors.autoWalletLaunching = jest.fn(() => true); await launchWallet(); - expect(screen.getByText("Setup Wallet")).toBeInTheDocument(); + await waitFor(() => screen.getByText("Setup Wallet")); expect(mockSetAutoWalletLaunching).not.toHaveBeenCalled(); }); test("launch a wallet and cancel loading", async () => { await launchWallet(1000); - await wait(() => screen.getByText("Cancel Loading")); - user.click(screen.getByText("Cancel Loading")); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await waitFor(() => screen.getByText("Cancel Loading")); + await user.click(screen.getByText("Cancel Loading")); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); expect(mockSetAutoWalletLaunching).not.toHaveBeenCalled(); }); test("launch an encrypted wallet", async () => { render(); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); mockStartWallet = daemonActions.startWallet = jest.fn( () => () => Promise.reject(OPENWALLET_INPUT) ); - user.click(screen.getByText(testAvailableWallets[0].value.wallet)); - await wait(() => expect(mockStartWallet).toHaveBeenCalled()); - expect(screen.getByText(/insert your pubkey/i)).toBeInTheDocument(); + await user.click(screen.getByText(testAvailableWallets[0].value.wallet)); + expect(mockStartWallet).toHaveBeenCalled(); + await waitFor(() => screen.getByText(/insert your pubkey/i)); expect(screen.getByText(/decrypt wallet/i)).toBeInTheDocument(); expect( screen.getByText(/this wallet is encrypted/i).textContent @@ -312,45 +324,44 @@ test("launch an encrypted wallet", async () => { const openWalletButton = screen.getByText("Open Wallet"); // invalid passphrase - user.type(publicPassphraseInput, testInvalidPassphrase); - user.click(openWalletButton); - await wait(() => - expect(mockOpenWalletAttempt).toHaveBeenCalledWith( - testInvalidPassphrase, - true - ) + await user.type(publicPassphraseInput, testInvalidPassphrase); + await user.click(openWalletButton); + expect(mockOpenWalletAttempt).toHaveBeenCalledWith( + testInvalidPassphrase, + true + ); + await waitFor(() => + expect( + screen.getByText(/wrong public passphrase/i).textContent + ).toMatchInlineSnapshot('"Wrong public passphrase inserted."') ); - expect( - screen.getByText(/wrong public passphrase/i).textContent - ).toMatchInlineSnapshot('"Wrong public passphrase inserted."'); // invalid passphrase & unknown error const UNKNOWN_ERROR = "UNKNOWN_ERROR"; mockOpenWalletAttempt = wlActions.openWalletAttempt = jest.fn( () => () => Promise.reject(UNKNOWN_ERROR) ); - user.type(publicPassphraseInput, testInvalidPassphrase); - user.click(openWalletButton); - await wait(() => - expect(mockOpenWalletAttempt).toHaveBeenCalledWith( - testInvalidPassphrase, - true - ) + await user.type(publicPassphraseInput, testInvalidPassphrase); + await user.click(openWalletButton); + expect(mockOpenWalletAttempt).toHaveBeenCalledWith( + testInvalidPassphrase, + true ); - expect(screen.getByText(UNKNOWN_ERROR)).toBeInTheDocument(); + await waitFor(() => screen.getByText(UNKNOWN_ERROR)); // submit empty input by pressing enter - user.clear(publicPassphraseInput); - user.type(publicPassphraseInput, "{enter}"); mockOpenWalletAttempt.mockClear(); + await user.clear(publicPassphraseInput); + fireEvent.keyDown(publicPassphraseInput, { key: "enter", keyCode: 13 }); expect(mockOpenWalletAttempt).not.toHaveBeenCalled(); // valid passphrase + submit the form by press enter mockOpenWalletAttempt = wlActions.openWalletAttempt = jest.fn( () => () => Promise.resolve(OPENWALLET_SUCCESS) ); - user.type(publicPassphraseInput, testValidPassphrase + "{enter}"); - await wait(() => + await user.type(publicPassphraseInput, testValidPassphrase); + fireEvent.keyDown(publicPassphraseInput, { key: "enter", keyCode: 13 }); + await waitFor(() => expect(mockOpenWalletAttempt).toHaveBeenCalledWith( testValidPassphrase, true @@ -360,7 +371,7 @@ test("launch an encrypted wallet", async () => { test("ask for passphrase if account discovery is needed", async () => { render(); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); mockStartWallet = daemonActions.startWallet = jest.fn( () => () => Promise.reject(OPENWALLET_INPUTPRIVPASS) @@ -371,8 +382,9 @@ test("ask for passphrase if account discovery is needed", async () => { return Promise.reject(OPENWALLET_INPUTPRIVPASS); } ); - user.click(screen.getByText(testAvailableWallets[0].value.wallet)); - await wait(() => expect(mockStartWallet).toHaveBeenCalled()); + await user.click(screen.getByText(testAvailableWallets[0].value.wallet)); + await waitFor(() => expect(mockStartWallet).toHaveBeenCalled()); + await waitFor(() => screen.getByPlaceholderText(/private passphrase/i)); let privatePassphraseInput = screen.getByPlaceholderText(/private passphrase/i); const testInvalidPassphrase = "invalid-passphrase"; @@ -389,20 +401,19 @@ test("ask for passphrase if account discovery is needed", async () => { ); // type invalid private key - user.type(privatePassphraseInput, testInvalidPassphrase); - user.click(continueButton); - await wait(() => - expect(mockStartRpcRequestFunc).toHaveBeenCalledWith( - testInvalidPassphrase, - undefined - ) + await user.type(privatePassphraseInput, testInvalidPassphrase); + await user.click(continueButton); + expect(mockStartRpcRequestFunc).toHaveBeenCalledWith( + testInvalidPassphrase, + undefined ); + await waitFor(() => screen.getByPlaceholderText(/private passphrase/i)); privatePassphraseInput = screen.getByPlaceholderText(/private passphrase/i); continueButton = screen.getByText("Continue"); // submit empty input by pressing enter - user.clear(privatePassphraseInput); - user.type(privatePassphraseInput, "{enter}"); + await user.clear(privatePassphraseInput); + fireEvent.keyDown(privatePassphraseInput, { key: "enter", keyCode: 13 }); mockStartRpcRequestFunc.mockClear(); expect(mockStartRpcRequestFunc).not.toHaveBeenCalled(); @@ -410,8 +421,9 @@ test("ask for passphrase if account discovery is needed", async () => { mockStartRpcRequestFunc = wlActions.startRpcRequestFunc = jest.fn( () => () => Promise.resolve() ); - user.type(privatePassphraseInput, testValidPassphrase + "{enter}"); - await wait(() => + await user.type(privatePassphraseInput, testValidPassphrase); + fireEvent.keyDown(privatePassphraseInput, { key: "enter", keyCode: 13 }); + await waitFor(() => expect(mockStartRpcRequestFunc).toHaveBeenCalledWith( testValidPassphrase, undefined @@ -421,7 +433,7 @@ test("ask for passphrase if account discovery is needed", async () => { test("launch an encrypted wallet and ask private passphrase too if account discovery is needed", async () => { render(); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); mockStartWallet = daemonActions.startWallet = jest.fn( () => () => Promise.reject(OPENWALLET_INPUT) @@ -430,29 +442,25 @@ test("launch an encrypted wallet and ask private passphrase too if account disco () => () => Promise.reject(OPENWALLET_INPUTPRIVPASS) ); - user.click(screen.getByText(testAvailableWallets[0].value.wallet)); - await wait(() => expect(mockStartWallet).toHaveBeenCalled()); + await user.click(screen.getByText(testAvailableWallets[0].value.wallet)); + expect(mockStartWallet).toHaveBeenCalled(); + await waitFor(() => screen.getByPlaceholderText(/public passphrase/i)); const publicPassphraseInput = screen.getByPlaceholderText(/public passphrase/i); const testValidPassphrase = "valid-passphrase"; - user.type(publicPassphraseInput, testValidPassphrase + "{enter}"); - await wait(() => - expect(mockOpenWalletAttempt).toHaveBeenCalledWith( - testValidPassphrase, - true - ) + await user.type(publicPassphraseInput, testValidPassphrase); + fireEvent.keyDown(publicPassphraseInput, { key: "enter", keyCode: 13 }); + expect(mockOpenWalletAttempt).toHaveBeenCalledWith(testValidPassphrase, true); + await waitFor(() => + expect(screen.getByText(/type passphrase to discover accounts/i)) ); - - expect( - screen.getByText(/type passphrase to discover accounts/i) - ).toBeInTheDocument(); }); test("test isSyncingRPC in SPV mode and receive error from SPV sync", async () => { selectors.isSPV = jest.fn(() => true); wlActions.spvSyncAttempt = jest.fn(() => () => Promise.reject()); render(); - await wait(() => screen.getByText(/welcome to decrediton/i)); + await waitFor(() => screen.getByText(/welcome to decrediton/i)); }); diff --git a/test/unit/components/views/GovernancePage/Blockchain/Blockchain.spec.js b/test/unit/components/views/GovernancePage/Blockchain/Blockchain.spec.js index dae95865bc..574ff47593 100644 --- a/test/unit/components/views/GovernancePage/Blockchain/Blockchain.spec.js +++ b/test/unit/components/views/GovernancePage/Blockchain/Blockchain.spec.js @@ -1,6 +1,6 @@ import Blockchain from "components/views/GovernancePage/Blockchain"; import { render } from "test-utils.js"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; import user from "@testing-library/user-event"; import * as sel from "selectors"; import * as gov from "actions/GovernanceActions"; @@ -26,7 +26,7 @@ beforeEach(() => { selectors.getAvailableVSPs = jest.fn(() => defaultMockAvailableMainnetVsps); }); -const testAgendaCardElements = ( +const testAgendaCardElements = async ( mockAgendaName, mockDescription, mockChoice, @@ -43,11 +43,11 @@ const testAgendaCardElements = ( expect(screen.getByText(expectedTooltipText)).toBeInTheDocument(); expect(screen.getByText(expectedStatusText)).toBeInTheDocument(); - user.click(screen.getByText(mockDescription)); + await user.click(screen.getByText(mockDescription)); expect(mockViewAgendaDetailsHandler).toHaveBeenCalledWith(mockAgendaName); }; -const testAgendaCard = ( +const testAgendaCard = async ( mockChoice, finished, passed, @@ -68,7 +68,7 @@ const testAgendaCard = ( { agendaId: mockAgendaName, choiceId: mockChoice } ]); render(); - testAgendaCardElements( + await testAgendaCardElements( mockAgendaName, mockDescription, mockChoice, @@ -129,7 +129,7 @@ test("test agenda search and sort controls", async () => { const filterControl = screen.getByPlaceholderText("Filter by Name"); - user.type(filterControl, "1"); + await user.type(filterControl, "1"); expect( screen .getAllByText(/test-desc-/i) @@ -137,9 +137,9 @@ test("test agenda search and sort controls", async () => { ).toStrictEqual(["test-desc-1", "test-desc-12"]); const eyeFilterMenu = screen.getByRole("button", { name: "EyeFilterMenu" }); - user.click(eyeFilterMenu); - user.click(screen.getByText("Oldest")); - await wait(() => + await user.click(eyeFilterMenu); + await user.click(screen.getByText("Oldest")); + await waitFor(() => expect( screen .getAllByText(/test-desc-/i) @@ -147,22 +147,22 @@ test("test agenda search and sort controls", async () => { ).toStrictEqual(["test-desc-12", "test-desc-1"]) ); - user.clear(filterControl); - user.type(filterControl, "12"); + await user.clear(filterControl); + await user.type(filterControl, "12"); expect( screen .getAllByText(/test-desc-/i) .map((element) => element.textContent.trim()) ).toStrictEqual(["test-desc-12"]); - user.clear(filterControl); - user.type(filterControl, "4"); + await user.clear(filterControl); + await user.type(filterControl, "4"); expect(screen.queryByText(/test-desc-/i)).not.toBeInTheDocument(); expect( screen.getByText(/no agendas matched your search/i) ).toBeInTheDocument(); - user.clear(filterControl); + await user.clear(filterControl); expect( screen .getAllByText(/test-desc-/i) @@ -170,9 +170,9 @@ test("test agenda search and sort controls", async () => { ).toStrictEqual(["test-desc-3", "test-desc-12", "test-desc-1"]); // Newest first - user.click(eyeFilterMenu); - user.click(screen.getByText("Newest")); - await wait(() => + await user.click(eyeFilterMenu); + await user.click(screen.getByText("Newest")); + await waitFor(() => expect( screen .getAllByText(/test-desc-/i) @@ -183,7 +183,7 @@ test("test agenda search and sort controls", async () => { test("test one outdated vsp alert", async () => { render(); - await wait(() => screen.getByText(/You have unspent tickets/i)); + await waitFor(() => screen.getByText(/You have unspent tickets/i)); expect( screen.getByText(/You have unspent tickets/i).textContent @@ -196,7 +196,7 @@ test("test multiple outdated vsps alert", async () => { defaultMockAvailableMainnetVsps[0].outdated = true; selectors.getAvailableVSPs = jest.fn(() => defaultMockAvailableMainnetVsps); render(); - await wait(() => screen.getByText(/You have unspent tickets/i)); + await waitFor(() => screen.getByText(/You have unspent tickets/i)); expect( screen.getByText(/You have unspent tickets/i).textContent diff --git a/test/unit/components/views/GovernancePage/TreasurySpending/TreasurySpending.spec.js b/test/unit/components/views/GovernancePage/TreasurySpending/TreasurySpending.spec.js index 7bb49a5fc0..64ff33a771 100644 --- a/test/unit/components/views/GovernancePage/TreasurySpending/TreasurySpending.spec.js +++ b/test/unit/components/views/GovernancePage/TreasurySpending/TreasurySpending.spec.js @@ -1,6 +1,6 @@ import TreasurySpendingTab from "components/views/GovernancePage/TreasurySpendingTab"; import { render } from "test-utils.js"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor, fireEvent } from "@testing-library/react"; import user from "@testing-library/user-event"; import { within } from "@testing-library/dom"; import * as sel from "selectors"; @@ -62,31 +62,31 @@ const expectedPiKeyResult = async ( expectPassphraseError, expectedPiKey ) => { - if (!expectPassphraseError) { - await wait(() => + await waitFor(() => { + if (!expectPassphraseError) { expect(mockSetTreasuryPolicy).toHaveBeenCalledWith( undefined, // votingService expectedPiKey, policy - ) - ); - } else { - expect(mockSetTreasuryPolicy).not.toHaveBeenCalled(); - } + ); + } else { + expect(mockSetTreasuryPolicy).not.toHaveBeenCalled(); + } + }); }; const expectedTSpendResult = async (policy, expectPassphraseError) => { - if (!expectPassphraseError) { - await wait(() => + await waitFor(() => { + if (!expectPassphraseError) { expect(mockSetTSpendPolicy).toHaveBeenCalledWith( undefined, // votingService testTSpendHash, policy - ) - ); - } else { - expect(mockSetTSpendPolicy).not.toHaveBeenCalled(); - } + ); + } else { + expect(mockSetTSpendPolicy).not.toHaveBeenCalled(); + } + }); }; const vote = async ( @@ -97,22 +97,22 @@ const vote = async ( expectedPiKey ) => { // vote on yes - const yesRadioBtn = within(container).getByText(policy).parentNode - .previousSibling; + const yesRadioBtn = + within(container).getByText(policy).parentNode.previousSibling; const updatePrefBtn = within(container).getByRole("button", { name: "Update Preference" }); - user.click(yesRadioBtn); + fireEvent.click(yesRadioBtn); expect(yesRadioBtn).toBeTruthy(); - user.click(updatePrefBtn); + fireEvent.click(updatePrefBtn); expect(screen.getByText("Confirm Your Vote").parentNode.textContent).toMatch( `Confirm Your Vote${policy}` ); // cancel first - user.click( + fireEvent.click( screen.getByRole("button", { name: "Cancel" }) @@ -120,21 +120,23 @@ const vote = async ( expect(screen.queryByText("Confirm Your Vote")).not.toBeInTheDocument(); // vote again - user.click(updatePrefBtn); + fireEvent.click(updatePrefBtn); const continueBtn = screen.getByRole("button", { name: "Continue" }); expect(continueBtn.disabled).toBeTruthy(); - user.type(screen.getByLabelText("Private Passphrase"), testPassphrase); + fireEvent.change(screen.getByLabelText("Private Passphrase"), { + target: { value: testPassphrase } + }); expect(continueBtn.disabled).toBeFalsy(); - user.click(continueBtn); + await user.click(continueBtn); - await expectedResult(policy, expectPassphraseError, expectedPiKey); - await wait(() => + await waitFor(() => { within(container).getByRole("button", { name: "Update Preference" - }) - ); + }); + }); + await expectedResult(policy, expectPassphraseError, expectedPiKey); }; const getPiKeyContainer = () => screen.getByText("Pi key:").parentElement; @@ -143,7 +145,18 @@ const queryTSpendLabel = () => screen.queryByText("Tx hash:"); /* PiKey vote tests */ -test("PiKey test: treasury spending tab (not yet voted)", async () => { +test("PiKey test: treasury spending tab (not yet voted, vote no)", async () => { + selectors.treasuryPolicies = jest.fn(() => []); + render(); + const container = getPiKeyContainer(); + expect( + within(container).getByText("abstain").parentNode.previousSibling.checked + ).toBeTruthy(); + + await vote("no", container, expectedPiKeyResult, false, PiKey_mainnet); +}); + +test("PiKey test: treasury spending tab (not yet voted, vote yes)", async () => { selectors.treasuryPolicies = jest.fn(() => []); render(); const container = getPiKeyContainer(); @@ -152,8 +165,17 @@ test("PiKey test: treasury spending tab (not yet voted)", async () => { ).toBeTruthy(); await vote("yes", container, expectedPiKeyResult, false, PiKey_mainnet); +}); + +test("PiKey test: treasury spending tab (not yet voted, vote abstain)", async () => { + selectors.treasuryPolicies = jest.fn(() => []); + render(); + const container = getPiKeyContainer(); + expect( + within(container).getByText("abstain").parentNode.previousSibling.checked + ).toBeTruthy(); + await vote("abstain", container, expectedPiKeyResult, false, PiKey_mainnet); - await vote("no", container, expectedPiKeyResult, false, PiKey_mainnet); }); test("PiKey test: treasury spending tab (already voted yes)", () => { @@ -233,7 +255,7 @@ test("TSpend test: treasury spending tab (received invalid tspend info)", () => expect(queryTSpendLabel()).not.toBeInTheDocument(); }); -test("Tspend test: treasury spending tab (not yet voted)", async () => { +test("Tspend test: treasury spending tab (not yet voted, vote yes)", async () => { render(); const container = getTSpendContainer(); expect( @@ -241,10 +263,28 @@ test("Tspend test: treasury spending tab (not yet voted)", async () => { ).toBeTruthy(); await vote("yes", container, expectedTSpendResult); - await vote("abstain", container, expectedTSpendResult); +}); + +test("Tspend test: treasury spending tab (not yet voted, vote no)", async () => { + render(); + const container = getTSpendContainer(); + expect( + within(container).getByText("abstain").parentNode.previousSibling.checked + ).toBeTruthy(); + await vote("no", container, expectedTSpendResult); }); +test("Tspend test: treasury spending tab (not yet voted, vote abstain)", async () => { + render(); + const container = getTSpendContainer(); + expect( + within(container).getByText("abstain").parentNode.previousSibling.checked + ).toBeTruthy(); + + await vote("abstain", container, expectedTSpendResult); +}); + test("TSpend test: treasury spending tab (already voted yes)", () => { selectors.tspendPolicies = jest.fn(() => [ { key: testTSpendHash, policy: "yes" } diff --git a/test/unit/components/views/HomePage/HomePage.spec.js b/test/unit/components/views/HomePage/HomePage.spec.js index 5bb8545535..b9b084f9fd 100644 --- a/test/unit/components/views/HomePage/HomePage.spec.js +++ b/test/unit/components/views/HomePage/HomePage.spec.js @@ -1,7 +1,6 @@ import HomePage from "components/views/HomePage"; import { render } from "test-utils.js"; -import user from "@testing-library/user-event"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; import { DCR } from "constants"; import * as sel from "selectors"; import * as cla from "actions/ClientActions"; @@ -105,7 +104,7 @@ test("walletService is undefined", () => { }); test("test HomePage with an immature ticket", async () => { - render(); + const { user } = render(); expect( screen.getByText("Current Total Balance").parentNode.textContent @@ -120,8 +119,8 @@ test("test HomePage with an immature ticket", async () => { ); // go to Tickets tab - user.click(screen.getByText("Tickets")); - await wait(() => screen.getByText(/voted ticket/i)); + await user.click(screen.getByText("Tickets")); + await waitFor(() => screen.getByText(/voted ticket/i)); expect( screen.getByText(/active and locked ticket/i).parentNode.textContent ).toMatchInlineSnapshot( @@ -134,8 +133,8 @@ test("test HomePage with an immature ticket", async () => { ); // go to Transactions tab - user.click(screen.getByText("Transactions")); - await wait(() => screen.getByText(/sent/i)); + await user.click(screen.getByText("Transactions")); + await waitFor(() => screen.getByText(/sent/i)); expect( screen.getAllByText(/received/i)[0].parentNode.textContent ).toMatchInlineSnapshot('"3.00000 DCRReceived"'); @@ -159,9 +158,9 @@ test("test HomePage with an immature ticket", async () => { // see all transactions const seeAllLinks = screen.getAllByText(/See all/i); - user.click(seeAllLinks[0]); + await user.click(seeAllLinks[0]); expect(mockGoToTransactionHistory).toHaveBeenCalledTimes(1); - user.click(seeAllLinks[1]); + await user.click(seeAllLinks[1]); expect(mockGoToMyTickets).toHaveBeenCalledTimes(1); }); diff --git a/test/unit/components/views/LNPage/AdvancedTab.spec.js b/test/unit/components/views/LNPage/AdvancedTab.spec.js index 59c7b5ea0e..fe1d701287 100644 --- a/test/unit/components/views/LNPage/AdvancedTab.spec.js +++ b/test/unit/components/views/LNPage/AdvancedTab.spec.js @@ -1,7 +1,6 @@ import { AdvancedTab } from "components/views/LNPage/AdvancedTab"; import { render } from "test-utils.js"; -import user from "@testing-library/user-event"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; import { DCR } from "constants"; import * as sel from "selectors"; import * as lna from "actions/LNActions"; @@ -148,7 +147,7 @@ test("test infos", () => { test("test backup", async () => { expect(process.env.TZ).toBe("UTC"); - render(); + const { user } = render(); expect(screen.getByText(mockLnInfo.identityPubkey)).toBeInTheDocument(); expect( screen.getByText(`SCB backup file location: ${mockLnSCBPath}`) @@ -159,19 +158,23 @@ test("test backup", async () => { const mockFilePath = "mockFilePath"; wallet.showSaveDialog.mockReturnValueOnce({ filePath: mockFilePath }); - user.click(screen.getByText("Backup Now")); - await wait(() => expect(mockExportBackup).toHaveBeenCalledWith(mockFilePath)); + await user.click(screen.getByText("Backup Now")); + await waitFor(() => + expect(mockExportBackup).toHaveBeenCalledWith(mockFilePath) + ); wallet.showOpenDialog.mockReturnValueOnce({ filePaths: [mockFilePath], filePath: mockFilePath }); - user.click(screen.getByText("Verify Backup")); - await wait(() => expect(mockVerifyBackup).toHaveBeenCalledWith(mockFilePath)); + await user.click(screen.getByText("Verify Backup")); + await waitFor(() => + expect(mockVerifyBackup).toHaveBeenCalledWith(mockFilePath) + ); }); -test("test towers", () => { - render(); +test("test towers", async () => { + const { user } = render(); // check first tower expect( @@ -190,7 +193,7 @@ test("test towers", () => { ); // check remove tower button - user.click(screen.getAllByText("Remove tower")[0].nextElementSibling); + await user.click(screen.getAllByText("Remove tower")[0].nextElementSibling); expect(mockRemoveWatchtower).toHaveBeenCalledWith( mockLnTowersList[0].pubkeyHex ); @@ -198,17 +201,17 @@ test("test towers", () => { // check add tower const mockTowerId = "mock-tower-id"; const mockTowerAddress = "mock-tower-address"; - user.type(screen.getByLabelText("Tower ID:"), mockTowerId); - user.type(screen.getByLabelText("Address:"), mockTowerAddress); - user.click(screen.getByText("Add")); + await user.type(screen.getByLabelText("Tower ID:"), mockTowerId); + await user.type(screen.getByLabelText("Address:"), mockTowerAddress); + await user.click(screen.getByText("Add")); expect(mockAddWatchtower).toHaveBeenCalledWith(mockTowerId, mockTowerAddress); }); -test("test query node", () => { +test("test query node", async () => { selectors.lnNodeInfo = jest.fn(() => mockNodeInfo); - render(); + const { user } = render(); - user.type(screen.getByLabelText("Node ID"), mockNodeInfo.node.pubKey); + await user.type(screen.getByLabelText("Node ID"), mockNodeInfo.node.pubKey); expect(mockGetNodeInfo).toHaveBeenCalledWith(mockNodeInfo.node.pubKey); expect( screen.getByText(mockNodeInfo.node.alias).parentElement.textContent diff --git a/test/unit/components/views/LNPage/ChannelDetailsTab.spec.js b/test/unit/components/views/LNPage/ChannelDetailsTab.spec.js index 0effe9ce63..acc1b9030a 100644 --- a/test/unit/components/views/LNPage/ChannelDetailsTab.spec.js +++ b/test/unit/components/views/LNPage/ChannelDetailsTab.spec.js @@ -1,7 +1,6 @@ import ChannelDetailsPage from "components/views/LNPage/ChannelDetailsPage"; import { render } from "test-utils.js"; -import user from "@testing-library/user-event"; -import { screen, wait, fireEvent } from "@testing-library/react"; +import { screen, waitFor, fireEvent } from "@testing-library/react"; import * as sel from "selectors"; import * as lna from "actions/LNActions"; import * as cli from "actions/ClientActions"; @@ -45,7 +44,7 @@ const getConfirmButton = () => screen.getByText("Confirm"); test("open channel details", async () => { mockChannelPoint = mockChannels[0].channelPoint; - render(); + const { user } = render(); expect(screen.getByText("Open").previousSibling.alt).toBe("greenCheck"); expect( @@ -87,14 +86,14 @@ test("open channel details", async () => { ); const cancelChannelBt = getCancelChannelButton(); - user.click(cancelChannelBt); + await user.click(cancelChannelBt); expect( screen.getByText(/Attempt cooperative close of channel/i) ).toBeInTheDocument(); fireEvent.click(getConfirmButton()); - await wait(() => + await waitFor(() => expect(mockCloseChannel).toHaveBeenCalledWith( mockChannels[0].channelPoint, false @@ -105,13 +104,13 @@ test("open channel details", async () => { screen.queryByText(/Attempt cooperative close of channel/i) ).not.toBeInTheDocument(); - user.click(screen.getByTestId("goBackHistory")); + await user.click(screen.getByTestId("goBackHistory")); expect(mockGoBackHistory).toHaveBeenCalled(); }); test("pending channel details", async () => { mockChannelPoint = mockPendingChannels[0].channelPoint; - render(); + const { user } = render(); expect(screen.getByText("Pending").previousSibling.alt).toBe("bluePending"); expect( @@ -143,14 +142,14 @@ test("pending channel details", async () => { // show close button for pending channels too const cancelChannelBt = getCancelChannelButton(); - user.click(cancelChannelBt); + await user.click(cancelChannelBt); expect( screen.getByText(/Attempt forced close of the channel/i) ).toBeInTheDocument(); fireEvent.click(getConfirmButton()); - await wait(() => + await waitFor(() => expect(mockCloseChannel).toHaveBeenCalledWith( mockPendingChannels[0].channelPoint, true @@ -271,7 +270,7 @@ test("test force close pending status", () => { ); }); -test("test wait close pending status", () => { +test("test waitFor close pending status", () => { const mockLimboBalance = 2000000; selectors.lnPendingChannels = jest.fn(() => [ { diff --git a/test/unit/components/views/LNPage/ChannelsTab.spec.js b/test/unit/components/views/LNPage/ChannelsTab.spec.js index 220ca1912e..8f814a1aad 100644 --- a/test/unit/components/views/LNPage/ChannelsTab.spec.js +++ b/test/unit/components/views/LNPage/ChannelsTab.spec.js @@ -1,7 +1,6 @@ import { ChannelsTab } from "components/views/LNPage/ChannelsTab"; import { render } from "test-utils.js"; -import user from "@testing-library/user-event"; -import { screen, wait, fireEvent } from "@testing-library/react"; +import { screen, waitFor, fireEvent } from "@testing-library/react"; import * as sel from "selectors"; import * as lna from "actions/LNActions"; import * as wl from "wallet"; @@ -44,11 +43,11 @@ beforeEach(() => { ); }); -test("test channel list", () => { - render(); +test("test channel list", async () => { + const { user } = render(); // check active card - user.click(screen.getByText(mockChannels[0].channelPoint)); + await user.click(screen.getByText(mockChannels[0].channelPoint)); expect(mockViewChannelDetails).toHaveBeenCalledWith( mockChannels[0].channelPoint ); @@ -58,7 +57,7 @@ test("test channel list", () => { ).toBe("2.00000 DCRcpa-0Open Local:0.7899636 DCR Remote:1.21000 DCRCapacity"); // check pending card - user.click(screen.getByText(mockPendingChannels[0].channelPoint)); + await user.click(screen.getByText(mockPendingChannels[0].channelPoint)); expect(mockViewChannelDetails).toHaveBeenCalledWith( mockPendingChannels[0].channelPoint ); @@ -70,7 +69,7 @@ test("test channel list", () => { ); // check closed card - user.click(screen.getByText(mockClosedChannels[0].channelPoint)); + await user.click(screen.getByText(mockClosedChannels[0].channelPoint)); expect(mockViewChannelDetails).toHaveBeenCalledWith( mockClosedChannels[0].channelPoint ); @@ -84,7 +83,7 @@ test("test channel list", () => { }); test("test filter control", async () => { - render(); + const { user } = render(); // by default, all types are shown expect(screen.getByText("Open")).toBeInTheDocument(); @@ -95,17 +94,19 @@ test("test filter control", async () => { name: "EyeFilterMenu" }); - user.click(filterMenuButton); - user.click(screen.getAllByText("Closed")[0]); + await user.click(filterMenuButton); + await user.click(screen.getAllByText("Closed")[0]); - await wait(() => expect(screen.queryByText("Open")).not.toBeInTheDocument()); + await waitFor(() => + expect(screen.queryByText("Open")).not.toBeInTheDocument() + ); expect(screen.queryByText("Pending")).not.toBeInTheDocument(); expect(screen.getByText("Closed")).toBeInTheDocument(); - user.click(filterMenuButton); - user.click(screen.getAllByText("Pending")[0]); + await user.click(filterMenuButton); + await user.click(screen.getAllByText("Pending")[0]); - await wait(() => + await waitFor(() => expect(screen.queryByText("Closed")).not.toBeInTheDocument() ); expect(screen.queryByText("Open")).not.toBeInTheDocument(); @@ -125,7 +126,9 @@ test("test search control", async () => { target: { value: "not valid channel point" } }); - await wait(() => expect(screen.queryByText("Open")).not.toBeInTheDocument()); + await waitFor(() => + expect(screen.queryByText("Open")).not.toBeInTheDocument() + ); expect(screen.queryByText("Pending")).not.toBeInTheDocument(); expect(screen.queryByText("Closed")).not.toBeInTheDocument(); expect(screen.getByText(/no channel found/i)).toBeInTheDocument(); @@ -133,11 +136,11 @@ test("test search control", async () => { fireEvent.change(searchInput, { target: { value: "cpp" } }); - await wait(() => expect(screen.getByText("Pending")).toBeInTheDocument()); + await waitFor(() => expect(screen.getByText("Pending")).toBeInTheDocument()); fireEvent.change(searchInput, { target: { value: "" } }); - await wait(() => expect(screen.getByText("Open")).toBeInTheDocument()); + await waitFor(() => expect(screen.getByText("Open")).toBeInTheDocument()); expect(screen.getByText("Pending")).toBeInTheDocument(); expect(screen.getByText("Closed")).toBeInTheDocument(); }); @@ -167,32 +170,30 @@ const getSearchClearBt = () => const getAuotPilotToggleSwitch = () => screen.getByTestId("switch"); test("test create form and receintly created modal", async () => { - render(); + const { user } = render(); const mockNode = "mock-node"; const mockAmount = 12; const createChannelBt = getCreateChannelButton(); expect(createChannelBt.disabled).toBe(true); - user.type(getNodeInput(), mockNode); + await user.type(getNodeInput(), mockNode); expect(createChannelBt.disabled).toBe(true); - user.type(getAmountToCommitInput(), `${mockAmount}`); + await user.type(getAmountToCommitInput(), `${mockAmount}`); expect(createChannelBt.disabled).toBe(false); - user.click(createChannelBt); - await wait(() => - expect(mockOpenChannel).toHaveBeenCalledWith( - mockNode, - mockAmount * 100000000, - null //pushAmt is null on mainnet - ) + await user.click(createChannelBt); + expect(mockOpenChannel).toHaveBeenCalledWith( + mockNode, + mockAmount * 100000000, + null //pushAmt is null on mainnet ); // inputs shoud be reseted now - expect(getNodeInput().value).toBe(""); - expect(getAmountToCommitInput().value).toBe(""); + await waitFor(() => expect(getNodeInput().value).toBe("")); + await waitFor(() => expect(getAmountToCommitInput().value).toBe("")); - await wait(() => screen.getByText("Channel Created")); + await waitFor(() => screen.getByText("Channel Created")); expect( screen.getAllByText("Open")[1].parentNode.parentNode.parentNode.textContent @@ -212,14 +213,14 @@ test("test create form and receintly created modal", async () => { ); const cancelChannelBt = getCancelChannelButton(); - user.click(cancelChannelBt); + await user.click(cancelChannelBt); expect( screen.getByText(/Attempt cooperative close of channel/i) ).toBeInTheDocument(); fireEvent.click(getConfirmButton()); - await wait(() => + await waitFor(() => expect(mockCloseChannel).toHaveBeenCalledWith( mockChannels[0].channelPoint, false @@ -230,14 +231,14 @@ test("test create form and receintly created modal", async () => { ).not.toBeInTheDocument(); //close recentlyOpenedChannel modal - user.click(screen.getByTestId("lnchannel-close-button")); + await user.click(screen.getByTestId("lnchannel-close-button")); expect(screen.queryByText("Channel Created")).not.toBeInTheDocument(); }); test("test push amount in testnet mode", async () => { selectors.isTestNet = jest.fn(() => true); selectors.isMainNet = jest.fn(() => false); - render(); + const { user } = render(); const mockNode = "mock-node"; const mockAmount = 12; @@ -245,51 +246,54 @@ test("test push amount in testnet mode", async () => { const createChannelBt = getCreateChannelButton(); expect(createChannelBt.disabled).toBe(true); - user.type(getNodeInput(), mockNode); + await user.type(getNodeInput(), mockNode); expect(createChannelBt.disabled).toBe(true); - user.type(getAmountToCommitInput(), `${mockAmount}`); + await user.type(getAmountToCommitInput(), `${mockAmount}`); expect(createChannelBt.disabled).toBe(false); - user.type(getPushAmountToCommitInput(), `${mockPushAmount}`); + await user.type(getPushAmountToCommitInput(), `${mockPushAmount}`); expect(createChannelBt.disabled).toBe(false); - user.click(createChannelBt); - await wait(() => - expect(mockOpenChannel).toHaveBeenCalledWith( - mockNode, - mockAmount * 100000000, - mockPushAmount * 100000000 - ) + await user.click(createChannelBt); + expect(mockOpenChannel).toHaveBeenCalledWith( + mockNode, + mockAmount * 100000000, + mockPushAmount * 100000000 ); // inputs shoud be reseted now - expect(getNodeInput().value).toBe(""); - expect(getAmountToCommitInput().value).toBe(""); - expect(getPushAmountToCommitInput().value).toBe(""); + await waitFor(() => { + expect(getNodeInput().value).toBe(""); + expect(getAmountToCommitInput().value).toBe(""); + expect(getPushAmountToCommitInput().value).toBe(""); + }); - await wait(() => screen.getByText("Channel Created")); + await waitFor(() => screen.getByText("Channel Created")); }); test("test failing channel create form", async () => { mockOpenChannel = lnActions.openChannel = jest.fn(() => () => { - return Promise.reject(); + return new Promise((_, reject) => { + setTimeout(() => { + reject(); + }, 10); + }); }); - render(); + const { user } = render(); const mockNode = "mock-node"; const mockAmount = 12; const createChannelBt = getCreateChannelButton(); expect(createChannelBt.disabled).toBe(true); - user.type(getNodeInput(), mockNode); + await user.type(getNodeInput(), mockNode); expect(createChannelBt.disabled).toBe(true); - user.type(getAmountToCommitInput(), `${mockAmount}`); + await user.type(getAmountToCommitInput(), `${mockAmount}`); expect(createChannelBt.disabled).toBe(false); - user.click(createChannelBt); - expect(createChannelBt.disabled).toBe(true); + await user.click(createChannelBt); + await waitFor(() => expect(createChannelBt.disabled).toBe(true)); expect(mockOpenChannel).toHaveBeenCalled(); - - await wait(() => expect(createChannelBt.disabled).toBe(false)); + await waitFor(() => expect(createChannelBt.disabled).toBe(false)); // inputs shoud NOT be reseted expect(getNodeInput().value).toBe(mockNode); @@ -299,25 +303,25 @@ test("test failing channel create form", async () => { }); test("test paste and clear button", async () => { - render(); + const { user } = render(); const mockPastedNodePubKey = "mockPastedNodePubKey"; wallet.readFromClipboard.mockImplementation(() => mockPastedNodePubKey); - user.click(getPasteButton()); - await wait(() => expect(getNodeInput().value).toBe(mockPastedNodePubKey)); + await user.click(getPasteButton()); + await waitFor(() => expect(getNodeInput().value).toBe(mockPastedNodePubKey)); - user.click(getClearButton()); - await wait(() => expect(getNodeInput().value).toBe("")); + await user.click(getClearButton()); + await waitFor(() => expect(getNodeInput().value).toBe("")); }); -test("test recent node list", () => { - render(); +test("test recent node list", async () => { + const { user } = render(); expect(screen.getByText("Recent Nodes").nextSibling.textContent).toBe( "mock-alias-1mock-alias-0mock-alias-2" ); - user.click(screen.getByText("mock-alias-1")); + await user.click(screen.getByText("mock-alias-1")); expect(getNodeInput().value).toBe(mockPendingChannels[0].remotePubkey); }); @@ -335,66 +339,65 @@ test("test empty recent node and channel list", () => { }); test("test search for node modal", async () => { - render(); + const { user } = render(); - user.click(getSearchButton()); + await user.click(getSearchButton()); expect(getSearchForNodeModalTitle()).toBeInTheDocument(); // simple close - user.click(screen.getByTestId("closeSearchForNodesModalBt")); + await user.click(screen.getByTestId("closeSearchForNodesModalBt")); expect(querySearchForNodeModalTitle()).not.toBeInTheDocument(); // reopen - user.click(getSearchButton()); + await user.click(getSearchButton()); // test recent nodes expect(screen.getAllByText("Recent Nodes")[1].nextSibling.textContent).toBe( "mock-alias-1mock...ub-0mock-alias-0mock...ey-0mock-alias-2mock...ey-0" ); - user.click(screen.getByText("mock...ub-0").parentElement.nextSibling); + await user.click(screen.getByText("mock...ub-0").parentElement.nextSibling); expect(querySearchForNodeModalTitle()).not.toBeInTheDocument(); expect(getNodeInput().value).toBe(mockPendingChannels[0].remotePubkey); // reopen - user.click(getSearchButton()); + await user.click(getSearchButton()); // test paste button const mockPastedAlias = "channel"; wallet.readFromClipboard.mockImplementation(() => mockPastedAlias); - user.click(getSearchPasteBt()); - await wait(() => expect(getSearchInput().value).toBe(mockPastedAlias)); + await user.click(getSearchPasteBt()); + await waitFor(() => expect(getSearchInput().value).toBe(mockPastedAlias)); expect(getSearchResultsTitle().parentElement.textContent).toBe( "Search Results (2)mock-alias-1mock...ub-0mock-alias-2mock...ey-0" ); - user.click(getSearchClearBt()); - await wait(() => expect(getSearchInput().value).toBe("")); + await user.click(getSearchClearBt()); + await waitFor(() => expect(getSearchInput().value).toBe("")); // type alias - user.type(getSearchInput(), "mock-alias-0"); // alias of the first open channel's node + await user.type(getSearchInput(), "mock-alias-0"); // alias of the first open channel's node expect(getSearchResultsTitle().parentElement.textContent).toBe( "Search Results (1)mock-alias-0mock...ey-0" ); - user.type(getSearchInput(), "notvalidaliasorpubkey"); + await user.type(getSearchInput(), "notvalidaliasorpubkey"); expect(getSearchResultsTitle().parentElement.textContent).toBe( "Search Results (0)No matching nodes found" ); }); -test("test automatic channel creation", () => { - render(); +test("test automatic channel creation", async () => { + const { user } = render(); - user.click(getAuotPilotToggleSwitch()); + await user.click(getAuotPilotToggleSwitch()); expect(mockModifyAutopilotStatus).toHaveBeenCalledWith(true); }); -const testPubkey = (pubkey, isValid, nodeInput, createChannelBt, message) => { - fireEvent.change(nodeInput, { - target: { - value: pubkey - } - }); +const testPubkey = async (pubkey, isValid, message) => { + const { user } = render(); + const createChannelBt = getCreateChannelButton(); + await user.type(getAmountToCommitInput(), `${mockAmount}`); + await user.type(getNodeInput(), pubkey); if (isValid) { expect(createChannelBt.disabled).toBe(false); expect( @@ -408,280 +411,281 @@ const testPubkey = (pubkey, isValid, nodeInput, createChannelBt, message) => { } }; -test("test node validation", () => { - render(); +const mockValidPubKey = + "038fde001dbe4d6286ab168cfd1e9711ad0cbb8fc4e3c2312f2b42063b72af8e71"; +// last digit changed to invalid hex char `x` +const mockInvalidPubKey = + "038fde001dbe4d6286ab168cfd1e9711ad0cbb8fc4e3c2312f2b42063b72af8e7x"; +// last digit changed to invalid hex char `x` +const mockInvalidHexPubKey = + "038fde001dbe4d6286ab168cfd1e9711ad0cbb8fc4e3c2312f2b42063b72af8e71xxx"; +const mockAmount = 12; + +test("test node validation (valid pubkey))", async () => { + await testPubkey(mockValidPubKey, true); +}); - const mockAmount = 12; - const createChannelBt = getCreateChannelButton(); - const nodeInput = getNodeInput(); - const mockValidPubKey = - "038fde001dbe4d6286ab168cfd1e9711ad0cbb8fc4e3c2312f2b42063b72af8e71"; - // last digit changed to invalid hex char `x` - const mockInvalidPubKey = - "038fde001dbe4d6286ab168cfd1e9711ad0cbb8fc4e3c2312f2b42063b72af8e7x"; - // last digit changed to invalid hex char `x` - const mockInvalidHexPubKey = - "038fde001dbe4d6286ab168cfd1e9711ad0cbb8fc4e3c2312f2b42063b72af8e71xxx"; - - user.type(getAmountToCommitInput(), `${mockAmount}`); - - testPubkey(mockValidPubKey, true, nodeInput, createChannelBt); - testPubkey(mockInvalidPubKey, false, nodeInput, createChannelBt); - testPubkey(mockInvalidHexPubKey, false, nodeInput, createChannelBt); - - testPubkey( +test("test node validation (invalid pubkey))", async () => { + await testPubkey(mockInvalidPubKey, false); +}); + +test("test node validation (invalid hex pubkey))", async () => { + await testPubkey(mockInvalidHexPubKey, false); +}); + +test("test node validation (More than one @ in the node address))", async () => { + await testPubkey( `${mockValidPubKey}@address@address`, false, - nodeInput, - createChannelBt, "More than one @ in the node address" ); +}); - testPubkey( +test("test node validation (More than one : in the node address)", async () => { + await testPubkey( `${mockValidPubKey}@address:port:port`, false, - nodeInput, - createChannelBt, "More than one : in the node address" ); +}); +test("test node validation", async () => { // mock valid node with domain - testPubkey(`${mockValidPubKey}@domain`, true, nodeInput, createChannelBt); + await testPubkey(`${mockValidPubKey}@domain`, true); +}); +test("test node validation", async () => { // mock valid node with ip and port - testPubkey( - `${mockValidPubKey}@127.0.0.1:88`, - true, - nodeInput, - createChannelBt - ); + await testPubkey(`${mockValidPubKey}@127.0.0.1:88`, true); +}); - testPubkey(mockValidPubKey, true, nodeInput, createChannelBt); +test("test node validation", async () => { + await testPubkey(mockValidPubKey, true); +}); +test("test node validation (uncompressed ok)", async () => { // // test public keys from // https://github.com/decred/dcrd/blob/master/dcrec/secp256k1/pubkey_test.go // - - // uncompressed ok - testPubkey( + await testPubkey( "04" + "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" + "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3", - true, - nodeInput, - createChannelBt + true ); - // uncompressed x changed (not on curve) - testPubkey( +}); + +test("test node validation (uncompressed x changed (not on curve))", async () => { + await testPubkey( "04" + "15db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" + "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3", - false, - nodeInput, - createChannelBt + false ); - // uncompressed y changed (not on curve) - testPubkey( +}); + +test("test node validation (uncompressed y changed (not on curve))", async () => { + await testPubkey( "04" + "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" + "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a4", - false, - nodeInput, - createChannelBt + false ); - // uncompressed claims compressed - testPubkey( +}); + +test("test node validation (uncompressed claims compressed)", async () => { + await testPubkey( "03" + "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" + "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3", - false, - nodeInput, - createChannelBt + false ); - // uncompressed as hybrid ok (ybit = 0) - testPubkey( +}); + +test("test node validation (uncompressed as hybrid ok (ybit = 0))", async () => { + await testPubkey( "06" + "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" + "4d1f1522047b33068bbb9b07d1e9f40564749b062b3fc0666479bc08a94be98c", - true, - nodeInput, - createChannelBt + true ); - // uncompressed as hybrid ok (ybit = 1) - testPubkey( +}); + +test("test node validation (uncompressed as hybrid ok (ybit = 1))", async () => { + await testPubkey( "07" + "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" + "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3", - true, - nodeInput, - createChannelBt + true ); - // uncompressed as hybrid wrong oddness - testPubkey( +}); + +test("test node validation (uncompressed as hybrid wrong oddness)", async () => { + await testPubkey( "06" + "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" + "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3", - false, - nodeInput, - createChannelBt + false ); - // compressed ok (ybit = 0) - testPubkey( +}); + +test("test node validation (compressed ok (ybit = 0))", async () => { + await testPubkey( "02" + "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d", - true, - nodeInput, - createChannelBt + true ); - // compressed ok (ybit = 1) - testPubkey( +}); + +test("test node validation (compressed ok (ybit = 1))", async () => { + await testPubkey( "03" + "2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e", - true, - nodeInput, - createChannelBt + true ); - // compressed claims uncompressed (ybit = 0) - testPubkey( +}); + +test("test node validation (compressed claims uncompressed (ybit = 0))", async () => { + await testPubkey( "04" + "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d", - false, - nodeInput, - createChannelBt + false ); - // compressed claims uncompressed (ybit = 1) - testPubkey( +}); + +test("test node validation (compressed claims uncompressed (ybit = 1))", async () => { + await testPubkey( "04" + "2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e", - false, - nodeInput, - createChannelBt + false ); - // compressed claims hybrid (ybit = 0) - testPubkey( +}); + +test("test node validation (compressed claims hybrid (ybit = 0))", async () => { + await testPubkey( "06" + "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d", - false, - nodeInput, - createChannelBt + false ); - // compressed claims hybrid (ybit = 1) - testPubkey( +}); + +test("test node validation (compressed claims hybrid (ybit = 1))", async () => { + await testPubkey( "07" + "2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e", - false, - nodeInput, - createChannelBt + false ); - // compressed with invalid x coord (ybit = 0) - testPubkey( +}); + +test("test node validation (compressed with invalid x coord (ybit = 0))", async () => { + await testPubkey( "03" + "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4c", - false, - nodeInput, - createChannelBt + false ); - // compressed with invalid x coord (ybit = 1) - testPubkey( +}); + +test("test node validation (compressed with invalid x coord (ybit = 1))", async () => { + await testPubkey( "03" + "2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448d", - false, - nodeInput, - createChannelBt + false ); - // wrong length - testPubkey("05", false, nodeInput, createChannelBt); - // uncompressed x == p - testPubkey( +}); + +test("test node validation (wrong length)", async () => { + await testPubkey("05", false); +}); + +test("test node validation (uncompressed x == p)", async () => { + await testPubkey( "04" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f" + "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3", - false, - nodeInput, - createChannelBt + false ); - // uncompressed x > p (p + 1 -- aka 1) - testPubkey( +}); + +test("test node validation (uncompressed x > p (p + 1 -- aka 1))", async () => { + await testPubkey( "04" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30" + "bde70df51939b94c9c24979fa7dd04ebd9b3572da7802290438af2a681895441", - false, - nodeInput, - createChannelBt + false ); - // uncompressed y == p - testPubkey( +}); + +test("test node validation (uncompressed y == p)", async () => { + await testPubkey( "04" + "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", - false, - nodeInput, - createChannelBt + false ); - // uncompressed y > p (p + 1 -- aka 1) - testPubkey( +}); + +test("test node validation (uncompressed y > p (p + 1 -- aka 1))", async () => { + await testPubkey( "04" + "1fe1e5ef3fceb5c135ab7741333ce5a6e80d68167653f6b2b24bcbcfaaaff507" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", - false, - nodeInput, - createChannelBt + false ); - // compressed x == p (ybit = 0) - testPubkey( +}); + +test("test node validation (compressed x == p (ybit = 0))", async () => { + await testPubkey( "02" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", - false, - nodeInput, - createChannelBt + false ); - // compressed x == p (ybit = 1) - testPubkey( +}); + +test("test node validation (compressed x == p (ybit = 1))", async () => { + await testPubkey( "03" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", - false, - nodeInput, - createChannelBt + false ); - // compressed x > p (p + 2 -- aka 2) (ybit = 0) - testPubkey( +}); + +test("test node validation (compressed x > p (p + 2 -- aka 2) (ybit = 0))", async () => { + await testPubkey( "02" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc31", - false, - nodeInput, - createChannelBt + false ); - // compressed x > p (p + 1 -- aka 1) (ybit = 1) - testPubkey( +}); + +test("test node validation (compressed x > p (p + 1 -- aka 1) (ybit = 1))", async () => { + await testPubkey( "03" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", - false, - nodeInput, - createChannelBt + false ); - // hybrid x == p (ybit = 1) - testPubkey( +}); + +test("test node validation (hybrid x == p (ybit = 1))", async () => { + await testPubkey( "07" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f" + "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3", - false, - nodeInput, - createChannelBt + false ); - // hybrid x > p (p + 1 -- aka 1) (ybit = 0) - testPubkey( +}); + +test("test node validation (hybrid x > p (p + 1 -- aka 1) (ybit = 0))", async () => { + await testPubkey( "06" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30" + "bde70df51939b94c9c24979fa7dd04ebd9b3572da7802290438af2a681895441", - false, - nodeInput, - createChannelBt + false ); - // hybrid y == p (ybit = 0 when mod p) - testPubkey( +}); + +test("test node validation (hybrid y == p (ybit = 0 when mod p))", async () => { + await testPubkey( "06" + "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", - false, - nodeInput, - createChannelBt + false ); - // hybrid y > p (p + 1 -- aka 1) (ybit = 1 when mod p) - testPubkey( +}); + +test("test node validation (hybrid y > p (p + 1 -- aka 1) (ybit = 1 when mod p))", async () => { + await testPubkey( "07" + "1fe1e5ef3fceb5c135ab7741333ce5a6e80d68167653f6b2b24bcbcfaaaff507" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", - false, - nodeInput, - createChannelBt + false ); }); diff --git a/test/unit/components/views/LNPage/ConnectPage.spec.js b/test/unit/components/views/LNPage/ConnectPage.spec.js index b2ddddec3d..13a74ecade 100644 --- a/test/unit/components/views/LNPage/ConnectPage.spec.js +++ b/test/unit/components/views/LNPage/ConnectPage.spec.js @@ -1,7 +1,6 @@ import { ConnectPage } from "components/views/LNPage/ConnectPage"; import { render } from "test-utils.js"; -import user from "@testing-library/user-event"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor, fireEvent } from "@testing-library/react"; import * as sel from "selectors"; import * as ca from "actions/ControlActions"; import * as lna from "actions/LNActions"; @@ -107,25 +106,25 @@ const getCanceButton = () => screen.getByRole("button", { name: "Cancel" }); const getAuotPilotToggleSwitch = () => screen.getByTestId("switch"); // go through the warning cards and click on understand button -const goToCreateWalletView = () => { +const goToCreateWalletView = async (user) => { const nextButton = screen.getByRole("button", { name: "Next" }); for (let i = 0; i < 5; i++) { - user.click(nextButton); + await user.click(nextButton); } - user.click(getUnderstandButton()); + await user.click(getUnderstandButton()); }; -test("ln wallet exists", () => { +test("ln wallet exists", async () => { selectors.lnWalletExists = jest.fn(() => true); - render(); + const { user } = render(); expect(queryUnderstandButton()).not.toBeInTheDocument(); expect(queryCreateNewWalletAccountToggle()).not.toBeInTheDocument(); expect(queryUseExistingWalletAccountToggle()).not.toBeInTheDocument(); expect(querySCBBackupFileInput()).not.toBeInTheDocument(); - user.click(getSubmitButton()); - user.type(getPrivatePasshpraseInput(), mockPrivatePassphrase); - user.click(getContinueButton()); + await user.click(getSubmitButton()); + await user.type(getPrivatePasshpraseInput(), mockPrivatePassphrase); + await user.click(getContinueButton()); expect(mockStartDcrlnd).toHaveBeenCalledWith( mockPrivatePassphrase, false /*autopilot not enabled*/, @@ -134,27 +133,27 @@ test("ln wallet exists", () => { ); }); -test("test create new new account", () => { - render(); - goToCreateWalletView(); +test("test create new new account", async () => { + const { user } = render(); + await goToCreateWalletView(user); expect(screen.getByText("Lightning Transactions")).toBeInTheDocument(); expect( screen.getByText("Start, unlock and connect to the dcrlnd wallet.") ).toBeInTheDocument(); - user.type(getSCBBackupFileInput(), mockFilePath); + await user.type(getSCBBackupFileInput(), mockFilePath); - user.click(getSubmitButton()); + await user.click(getSubmitButton()); expect(screen.getByText("Unlock LN Wallet")).toBeInTheDocument(); //click on cancel first - user.click(getCanceButton()); + await user.click(getCanceButton()); expect(screen.queryByText("Unlock LN Wallet")).not.toBeInTheDocument(); - user.click(getSubmitButton()); - user.type(getPrivatePasshpraseInput(), mockPrivatePassphrase); - user.click(getContinueButton()); + await user.click(getSubmitButton()); + await user.type(getPrivatePasshpraseInput(), mockPrivatePassphrase); + await user.click(getContinueButton()); expect(mockStartDcrlnd).toHaveBeenCalledWith( mockPrivatePassphrase, false /*autopilot not enabled*/, @@ -164,28 +163,37 @@ test("test create new new account", () => { }); test("test text toggles", async () => { - render(); - goToCreateWalletView(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + + await goToCreateWalletView(user); expect(screen.queryByText(/attention:/i)).not.toBeInTheDocument(); - user.click(getUseExistingWalletAccountToggle()); - await wait(() => screen.getByText(/attention:/i)); + await user.click(getUseExistingWalletAccountToggle()); + await waitFor(() => screen.getByText(/attention:/i)); // go back - user.click(getCreateNewWalletAccountToggle()); - await wait(() => + await user.click(getCreateNewWalletAccountToggle()); + await waitFor(() => expect(screen.queryByText(/attention:/i)).not.toBeInTheDocument() ); }); -test("test automatic channel creation", () => { - render(); - goToCreateWalletView(); +test("test automatic channel creation", async () => { + const { user } = render(); + await goToCreateWalletView(user); - user.click(getAuotPilotToggleSwitch()); - user.click(getSubmitButton()); - user.type(getPrivatePasshpraseInput(), mockPrivatePassphrase); - user.click(getContinueButton()); + await user.click(getAuotPilotToggleSwitch()); + await user.click(getSubmitButton()); + await user.type(getPrivatePasshpraseInput(), mockPrivatePassphrase); + await user.click(getContinueButton()); expect(mockStartDcrlnd).toHaveBeenCalledWith( mockPrivatePassphrase, true /*autopilot enabled*/, @@ -195,19 +203,28 @@ test("test automatic channel creation", () => { }); test("test use existing account", async () => { - render(); - goToCreateWalletView(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + + await goToCreateWalletView(user); expect(screen.queryByText(/attention:/i)).not.toBeInTheDocument(); - user.click(getUseExistingWalletAccountToggle()); - await wait(() => screen.getByText(/attention:/i)); + fireEvent.click(getUseExistingWalletAccountToggle()); + await waitFor(() => screen.getByText(/attention:/i)); - user.click(screen.getByText(mockDefaultAccount.name)); - user.click(screen.getByText(mockLnAccount.name)); + await user.click(screen.getByText(mockDefaultAccount.name)); + await user.click(screen.getByText(mockLnAccount.name)); - user.click(getSubmitButton()); - user.type(getPrivatePasshpraseInput(), mockPrivatePassphrase); - user.click(getContinueButton()); + fireEvent.click(getSubmitButton()); + await user.type(getPrivatePasshpraseInput(), mockPrivatePassphrase); + fireEvent.click(getContinueButton()); expect(mockStartDcrlnd).toHaveBeenCalledWith( mockPrivatePassphrase, false /*autopilot not enabled*/, @@ -216,10 +233,10 @@ test("test use existing account", async () => { ); }); -test("test disabled submit button", () => { +test("test disabled submit button", async () => { selectors.getRunningIndicator = jest.fn(() => true); - render(); - goToCreateWalletView(); + const { user } = render(); + await goToCreateWalletView(user); expect(getSubmitButton().disabled).toBe(true); expect( @@ -228,7 +245,7 @@ test("test disabled submit button", () => { ) ).toBeInTheDocument(); - user.click(getSubmitButton()); + await user.click(getSubmitButton()); expect(mockStartDcrlnd).not.toHaveBeenCalled(); }); @@ -245,7 +262,7 @@ const tabShouldBeActive = (tab) => const tabShouldBeChecked = (tab) => expect(tab.firstElementChild.firstElementChild.className).toMatch("visited"); -test("test warning view", () => { +test("test warning view", async () => { render(); const understandButton = getUnderstandButton(); @@ -288,208 +305,233 @@ test("test warning view", () => { tabShouldBeUnchecked(tab6); // clicking on previousButton in vain - user.click(previousButton); - tabShouldBeActive(tab1); - tabShouldBeUnchecked(tab1); - tabShouldBeInactive(tab2); - tabShouldBeUnchecked(tab2); - tabShouldBeInactive(tab3); - tabShouldBeUnchecked(tab3); - tabShouldBeInactive(tab4); - tabShouldBeUnchecked(tab4); - tabShouldBeInactive(tab5); - tabShouldBeUnchecked(tab5); - tabShouldBeInactive(tab6); - tabShouldBeUnchecked(tab6); + fireEvent.click(previousButton); + await waitFor(() => { + tabShouldBeActive(tab1); + tabShouldBeUnchecked(tab1); + tabShouldBeInactive(tab2); + tabShouldBeUnchecked(tab2); + tabShouldBeInactive(tab3); + tabShouldBeUnchecked(tab3); + tabShouldBeInactive(tab4); + tabShouldBeUnchecked(tab4); + tabShouldBeInactive(tab5); + tabShouldBeUnchecked(tab5); + tabShouldBeInactive(tab6); + tabShouldBeUnchecked(tab6); + }); // clicking on previousArrowButton in vain - user.click(previousArrowButton); - tabShouldBeActive(tab1); - tabShouldBeUnchecked(tab1); - tabShouldBeInactive(tab2); - tabShouldBeUnchecked(tab2); - tabShouldBeInactive(tab3); - tabShouldBeUnchecked(tab3); - tabShouldBeInactive(tab4); - tabShouldBeUnchecked(tab4); - tabShouldBeInactive(tab5); - tabShouldBeUnchecked(tab5); - tabShouldBeInactive(tab6); - tabShouldBeUnchecked(tab6); + fireEvent.click(previousArrowButton); + + await waitFor(() => { + tabShouldBeActive(tab1); + tabShouldBeUnchecked(tab1); + tabShouldBeInactive(tab2); + tabShouldBeUnchecked(tab2); + tabShouldBeInactive(tab3); + tabShouldBeUnchecked(tab3); + tabShouldBeInactive(tab4); + tabShouldBeUnchecked(tab4); + tabShouldBeInactive(tab5); + tabShouldBeUnchecked(tab5); + tabShouldBeInactive(tab6); + tabShouldBeUnchecked(tab6); + }); // move on to the second tab - user.click(nextButton); - tabShouldBeInactive(tab1); - tabShouldBeChecked(tab1); - tabShouldBeActive(tab2); - tabShouldBeUnchecked(tab2); - tabShouldBeInactive(tab3); - tabShouldBeUnchecked(tab3); - tabShouldBeInactive(tab4); - tabShouldBeUnchecked(tab4); - tabShouldBeInactive(tab5); - tabShouldBeUnchecked(tab5); - tabShouldBeInactive(tab6); - tabShouldBeUnchecked(tab6); - expect(previousButton.className).not.toMatch("disabled"); - expect(previousArrowButton.className).not.toMatch("disabled"); - expect(understandButton.disabled).toBe(true); + fireEvent.click(nextButton); + await waitFor(() => { + tabShouldBeInactive(tab1); + tabShouldBeChecked(tab1); + tabShouldBeActive(tab2); + tabShouldBeUnchecked(tab2); + tabShouldBeInactive(tab3); + tabShouldBeUnchecked(tab3); + tabShouldBeInactive(tab4); + tabShouldBeUnchecked(tab4); + tabShouldBeInactive(tab5); + tabShouldBeUnchecked(tab5); + tabShouldBeInactive(tab6); + tabShouldBeUnchecked(tab6); + expect(previousButton.className).not.toMatch("disabled"); + expect(previousArrowButton.className).not.toMatch("disabled"); + expect(understandButton.disabled).toBe(true); + }); // move on to the third tab click on the next arrow - user.click(nextArrowButton); - tabShouldBeInactive(tab1); - tabShouldBeChecked(tab1); - tabShouldBeInactive(tab2); - tabShouldBeChecked(tab2); - tabShouldBeActive(tab3); - tabShouldBeUnchecked(tab3); - tabShouldBeInactive(tab4); - tabShouldBeUnchecked(tab4); - tabShouldBeInactive(tab5); - tabShouldBeUnchecked(tab5); - tabShouldBeInactive(tab6); - tabShouldBeUnchecked(tab6); - expect(previousButton.className).not.toMatch("disabled"); - expect(previousArrowButton.className).not.toMatch("disabled"); - expect(understandButton.disabled).toBe(true); + fireEvent.click(nextArrowButton); + await waitFor(() => { + tabShouldBeInactive(tab1); + tabShouldBeChecked(tab1); + tabShouldBeInactive(tab2); + tabShouldBeChecked(tab2); + tabShouldBeActive(tab3); + tabShouldBeUnchecked(tab3); + tabShouldBeInactive(tab4); + tabShouldBeUnchecked(tab4); + tabShouldBeInactive(tab5); + tabShouldBeUnchecked(tab5); + tabShouldBeInactive(tab6); + tabShouldBeUnchecked(tab6); + expect(previousButton.className).not.toMatch("disabled"); + expect(previousArrowButton.className).not.toMatch("disabled"); + expect(understandButton.disabled).toBe(true); + }); // move on to the fourth tab - user.click(nextArrowButton); - tabShouldBeInactive(tab1); - tabShouldBeChecked(tab1); - tabShouldBeInactive(tab2); - tabShouldBeChecked(tab2); - tabShouldBeInactive(tab3); - tabShouldBeChecked(tab3); - tabShouldBeActive(tab4); - tabShouldBeUnchecked(tab4); - tabShouldBeInactive(tab5); - tabShouldBeUnchecked(tab5); - tabShouldBeInactive(tab6); - tabShouldBeUnchecked(tab6); - expect(previousButton.className).not.toMatch("disabled"); - expect(previousArrowButton.className).not.toMatch("disabled"); - expect(understandButton.disabled).toBe(true); + fireEvent.click(nextArrowButton); + await waitFor(() => { + tabShouldBeInactive(tab1); + tabShouldBeChecked(tab1); + tabShouldBeInactive(tab2); + tabShouldBeChecked(tab2); + tabShouldBeInactive(tab3); + tabShouldBeChecked(tab3); + tabShouldBeActive(tab4); + tabShouldBeUnchecked(tab4); + tabShouldBeInactive(tab5); + tabShouldBeUnchecked(tab5); + tabShouldBeInactive(tab6); + tabShouldBeUnchecked(tab6); + expect(previousButton.className).not.toMatch("disabled"); + expect(previousArrowButton.className).not.toMatch("disabled"); + expect(understandButton.disabled).toBe(true); + }); // move on to the fifth tab - user.click(nextButton); - tabShouldBeInactive(tab1); - tabShouldBeChecked(tab1); - tabShouldBeInactive(tab2); - tabShouldBeChecked(tab2); - tabShouldBeInactive(tab3); - tabShouldBeChecked(tab3); - tabShouldBeInactive(tab4); - tabShouldBeChecked(tab4); - tabShouldBeActive(tab5); - tabShouldBeUnchecked(tab5); - tabShouldBeInactive(tab6); - tabShouldBeUnchecked(tab6); - expect(previousButton.className).not.toMatch("disabled"); - expect(previousArrowButton.className).not.toMatch("disabled"); - expect(understandButton.disabled).toBe(true); + fireEvent.click(nextButton); + await waitFor(() => { + tabShouldBeInactive(tab1); + tabShouldBeChecked(tab1); + tabShouldBeInactive(tab2); + tabShouldBeChecked(tab2); + tabShouldBeInactive(tab3); + tabShouldBeChecked(tab3); + tabShouldBeInactive(tab4); + tabShouldBeChecked(tab4); + tabShouldBeActive(tab5); + tabShouldBeUnchecked(tab5); + tabShouldBeInactive(tab6); + tabShouldBeUnchecked(tab6); + expect(previousButton.className).not.toMatch("disabled"); + expect(previousArrowButton.className).not.toMatch("disabled"); + expect(understandButton.disabled).toBe(true); + }); // move on to the final tab - user.click(nextButton); - tabShouldBeInactive(tab1); - tabShouldBeChecked(tab1); - tabShouldBeInactive(tab2); - tabShouldBeChecked(tab2); - tabShouldBeInactive(tab3); - tabShouldBeChecked(tab3); - tabShouldBeInactive(tab4); - tabShouldBeChecked(tab4); - tabShouldBeInactive(tab5); - tabShouldBeChecked(tab5); - tabShouldBeActive(tab6); - tabShouldBeChecked(tab6); - expect(previousButton.className).not.toMatch("disabled"); - expect(previousArrowButton.className).not.toMatch("disabled"); - expect(understandButton.disabled).toBe(false); - expect(nextButton.className).toMatch("disabled"); - expect(nextArrowButton.className).toMatch("disabled"); + fireEvent.click(nextButton); + await waitFor(() => { + tabShouldBeInactive(tab1); + tabShouldBeChecked(tab1); + tabShouldBeInactive(tab2); + tabShouldBeChecked(tab2); + tabShouldBeInactive(tab3); + tabShouldBeChecked(tab3); + tabShouldBeInactive(tab4); + tabShouldBeChecked(tab4); + tabShouldBeInactive(tab5); + tabShouldBeChecked(tab5); + tabShouldBeActive(tab6); + tabShouldBeChecked(tab6); + expect(previousButton.className).not.toMatch("disabled"); + expect(previousArrowButton.className).not.toMatch("disabled"); + expect(understandButton.disabled).toBe(false); + expect(nextButton.className).toMatch("disabled"); + expect(nextArrowButton.className).toMatch("disabled"); + }); // clicking on nextButton in vain - user.click(nextButton); - tabShouldBeInactive(tab1); - tabShouldBeChecked(tab1); - tabShouldBeInactive(tab2); - tabShouldBeChecked(tab2); - tabShouldBeInactive(tab3); - tabShouldBeChecked(tab3); - tabShouldBeInactive(tab4); - tabShouldBeChecked(tab4); - tabShouldBeInactive(tab5); - tabShouldBeChecked(tab5); - tabShouldBeActive(tab6); - tabShouldBeChecked(tab6); - expect(previousButton.className).not.toMatch("disabled"); - expect(previousArrowButton.className).not.toMatch("disabled"); - expect(understandButton.disabled).toBe(false); - expect(nextButton.className).toMatch("disabled"); - expect(nextArrowButton.className).toMatch("disabled"); + fireEvent.click(nextButton); + await waitFor(() => { + tabShouldBeInactive(tab1); + tabShouldBeChecked(tab1); + tabShouldBeInactive(tab2); + tabShouldBeChecked(tab2); + tabShouldBeInactive(tab3); + tabShouldBeChecked(tab3); + tabShouldBeInactive(tab4); + tabShouldBeChecked(tab4); + tabShouldBeInactive(tab5); + tabShouldBeChecked(tab5); + tabShouldBeActive(tab6); + tabShouldBeChecked(tab6); + expect(previousButton.className).not.toMatch("disabled"); + expect(previousArrowButton.className).not.toMatch("disabled"); + expect(understandButton.disabled).toBe(false); + expect(nextButton.className).toMatch("disabled"); + expect(nextArrowButton.className).toMatch("disabled"); + }); // clicking on nextArrowButton in vain - user.click(nextButton); - tabShouldBeInactive(tab1); - tabShouldBeChecked(tab1); - tabShouldBeInactive(tab2); - tabShouldBeChecked(tab2); - tabShouldBeInactive(tab3); - tabShouldBeChecked(tab3); - tabShouldBeInactive(tab4); - tabShouldBeChecked(tab4); - tabShouldBeInactive(tab5); - tabShouldBeChecked(tab5); - tabShouldBeActive(tab6); - tabShouldBeChecked(tab6); - expect(previousButton.className).not.toMatch("disabled"); - expect(previousArrowButton.className).not.toMatch("disabled"); - expect(understandButton.disabled).toBe(false); - expect(nextButton.className).toMatch("disabled"); - expect(nextArrowButton.className).toMatch("disabled"); + fireEvent.click(nextButton); + await waitFor(() => { + tabShouldBeInactive(tab1); + tabShouldBeChecked(tab1); + tabShouldBeInactive(tab2); + tabShouldBeChecked(tab2); + tabShouldBeInactive(tab3); + tabShouldBeChecked(tab3); + tabShouldBeInactive(tab4); + tabShouldBeChecked(tab4); + tabShouldBeInactive(tab5); + tabShouldBeChecked(tab5); + tabShouldBeActive(tab6); + tabShouldBeChecked(tab6); + expect(previousButton.className).not.toMatch("disabled"); + expect(previousArrowButton.className).not.toMatch("disabled"); + expect(understandButton.disabled).toBe(false); + expect(nextButton.className).toMatch("disabled"); + expect(nextArrowButton.className).toMatch("disabled"); + }); // move back to the fifth tab - user.click(previousButton); - tabShouldBeInactive(tab1); - tabShouldBeChecked(tab1); - tabShouldBeInactive(tab2); - tabShouldBeChecked(tab2); - tabShouldBeInactive(tab3); - tabShouldBeChecked(tab3); - tabShouldBeInactive(tab4); - tabShouldBeChecked(tab4); - tabShouldBeActive(tab5); - tabShouldBeChecked(tab5); - tabShouldBeInactive(tab6); - tabShouldBeChecked(tab6); - expect(previousButton.className).not.toMatch("disabled"); - expect(previousArrowButton.className).not.toMatch("disabled"); - expect(understandButton.disabled).toBe(false); - expect(nextButton.className).not.toMatch("disabled"); - expect(nextArrowButton.className).not.toMatch("disabled"); + fireEvent.click(previousButton); + await waitFor(() => { + tabShouldBeInactive(tab1); + tabShouldBeChecked(tab1); + tabShouldBeInactive(tab2); + tabShouldBeChecked(tab2); + tabShouldBeInactive(tab3); + tabShouldBeChecked(tab3); + tabShouldBeInactive(tab4); + tabShouldBeChecked(tab4); + tabShouldBeActive(tab5); + tabShouldBeChecked(tab5); + tabShouldBeInactive(tab6); + tabShouldBeChecked(tab6); + expect(previousButton.className).not.toMatch("disabled"); + expect(previousArrowButton.className).not.toMatch("disabled"); + expect(understandButton.disabled).toBe(false); + expect(nextButton.className).not.toMatch("disabled"); + expect(nextArrowButton.className).not.toMatch("disabled"); + }); // move back to the fourth tab clicking on the arrow button - user.click(previousArrowButton); - tabShouldBeInactive(tab1); - tabShouldBeChecked(tab1); - tabShouldBeInactive(tab2); - tabShouldBeChecked(tab2); - tabShouldBeInactive(tab3); - tabShouldBeChecked(tab3); - tabShouldBeActive(tab4); - tabShouldBeChecked(tab4); - tabShouldBeInactive(tab5); - tabShouldBeChecked(tab5); - tabShouldBeInactive(tab6); - tabShouldBeChecked(tab6); - expect(previousButton.className).not.toMatch("disabled"); - expect(previousArrowButton.className).not.toMatch("disabled"); - expect(understandButton.disabled).toBe(false); - expect(nextButton.className).not.toMatch("disabled"); - expect(nextArrowButton.className).not.toMatch("disabled"); - - expect(screen.getByText(/before you continue/i)).toBeInTheDocument(); - user.click(understandButton); - expect(screen.queryByText(/before you continue/i)).not.toBeInTheDocument(); - expect(screen.getByText(/create new wallet account/i)).toBeInTheDocument(); + fireEvent.click(previousArrowButton); + await waitFor(() => { + tabShouldBeInactive(tab1); + tabShouldBeChecked(tab1); + tabShouldBeInactive(tab2); + tabShouldBeChecked(tab2); + tabShouldBeInactive(tab3); + tabShouldBeChecked(tab3); + tabShouldBeActive(tab4); + tabShouldBeChecked(tab4); + tabShouldBeInactive(tab5); + tabShouldBeChecked(tab5); + tabShouldBeInactive(tab6); + tabShouldBeChecked(tab6); + expect(previousButton.className).not.toMatch("disabled"); + expect(previousArrowButton.className).not.toMatch("disabled"); + expect(understandButton.disabled).toBe(false); + expect(nextButton.className).not.toMatch("disabled"); + expect(nextArrowButton.className).not.toMatch("disabled"); + expect(screen.getByText(/before you continue/i)).toBeInTheDocument(); + }); + + fireEvent.click(understandButton); + await waitFor(() => { + expect(screen.queryByText(/before you continue/i)).not.toBeInTheDocument(); + expect(screen.getByText(/create new wallet account/i)).toBeInTheDocument(); + }); }); diff --git a/test/unit/components/views/LNPage/OverviewTab.spec.js b/test/unit/components/views/LNPage/OverviewTab.spec.js index 5ece1dd200..efbef74816 100644 --- a/test/unit/components/views/LNPage/OverviewTab.spec.js +++ b/test/unit/components/views/LNPage/OverviewTab.spec.js @@ -1,7 +1,6 @@ import { OverviewTab } from "components/views/LNPage/OverviewTab"; import { render } from "test-utils.js"; -import user from "@testing-library/user-event"; -import { screen, wait, act } from "@testing-library/react"; +import { fireEvent, screen, waitFor } from "@testing-library/react"; import { DCR } from "constants"; import * as sel from "selectors"; import * as lna from "actions/LNActions"; @@ -95,25 +94,26 @@ test("test account overview and net stats", () => { }); test("test recent activity list", async () => { - render(); + const { user } = render(); // channel closed expect(screen.getByText("Channel Closed").parentElement.textContent).toBe( "Channel Closed 0.47381162 DCRcpc-0" ); - user.click(screen.getByText("Channel Closed")); + await user.click(screen.getByText("Channel Closed")); expect(screen.getByText("Channel Created")).toBeInTheDocument(); - user.click(screen.getByTestId("lnchannel-close-button")); + await user.click(screen.getByTestId("lnchannel-close-button")); // channel funding expect(screen.getByText("Channel Funding").parentElement.textContent).toBe( "Channel Funding 0.00000 DCRcpa-0" ); - user.click(screen.getByText("Channel Funding")); + await user.click(screen.getByText("Channel Funding")); expect(screen.getByText("Channel Created")).toBeInTheDocument(); - user.click(screen.getByRole("button", { name: "Close Channel" })); - act(() => user.click(screen.getByText("Confirm"))); - await wait(() => + await user.click(screen.getByRole("button", { name: "Close Channel" })); + fireEvent.click(screen.getByText("Confirm")); + + await waitFor(() => expect(mockCloseChannel).toHaveBeenCalledWith( mockChannels[0].channelPoint, false @@ -124,28 +124,28 @@ test("test recent activity list", async () => { expect(screen.getByText("Sent Payment").parentElement.textContent).toBe( "Sent Payment 0.20000 DCRmock-payment-hash-0" ); - user.click(screen.getByText("Sent Payment")); + await user.click(screen.getByText("Sent Payment")); expect(screen.getByText("Lightning Payment")).toBeInTheDocument(); - user.click(screen.getByTestId("lnpayment-close-button")); + await user.click(screen.getByTestId("lnpayment-close-button")); // invoice expect(screen.getAllByText("Invoice for")[0].parentElement.textContent).toBe( "Invoice for 0.00001 DCRmock-rhash-hex-21" ); - user.click(screen.getAllByText("Invoice for")[1]); + await user.click(screen.getAllByText("Invoice for")[1]); expect(screen.getByText("Lightning Payment Request")).toBeInTheDocument(); - user.click(screen.getByRole("button", { name: "Cancel Invoice" })); + await user.click(screen.getByRole("button", { name: "Cancel Invoice" })); expect(mockCancelInvoice).toHaveBeenCalledWith(mockInvoices[0].rHash); - await wait(() => + await waitFor(() => expect( screen.queryByText("Lightning Payment Request") ).not.toBeInTheDocument() ); // open a second invoice and close with the close button - user.click(screen.getAllByText("Invoice for")[0]); + await user.click(screen.getAllByText("Invoice for")[0]); expect(screen.getByText("Lightning Payment Request")).toBeInTheDocument(); - user.click(screen.getByTestId("lninvoice-close-button")); + await user.click(screen.getByTestId("lninvoice-close-button")); expect( screen.queryByText("Lightning Payment Request") ).not.toBeInTheDocument(); @@ -174,18 +174,18 @@ test("test no activities yet", () => { }); test("test search for nodes button", async () => { - render(); + const { user } = render(); const searchForNodesButton = screen.getByTestId("searchForNodesButton"); - user.click(searchForNodesButton); + await user.click(searchForNodesButton); // click one of recent nodes expect(screen.getByText("Recent Nodes").nextSibling.textContent).toBe( "mock-alias-1mock...ub-0mock-alias-0mock...ey-0mock-alias-2mock...ey-0" ); - user.click(screen.getByText("mock...ub-0").parentElement.nextSibling); + await user.click(screen.getByText("mock...ub-0").parentElement.nextSibling); expect(screen.queryByText("Search For Nodes")).not.toBeInTheDocument(); - await wait(() => + await waitFor(() => expect(mockGoToChannelsTab).toHaveBeenCalledWith( mockPendingChannels[0].remotePubkey ) diff --git a/test/unit/components/views/LNPage/ReceiveTab.spec.js b/test/unit/components/views/LNPage/ReceiveTab.spec.js index f3227c1af2..29c86988a8 100644 --- a/test/unit/components/views/LNPage/ReceiveTab.spec.js +++ b/test/unit/components/views/LNPage/ReceiveTab.spec.js @@ -1,7 +1,6 @@ import { ReceiveTab } from "components/views/LNPage/ReceiveTab"; import { render } from "test-utils.js"; -import user from "@testing-library/user-event"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; import { DCR } from "constants"; import * as sel from "selectors"; import * as lna from "actions/LNActions"; @@ -34,7 +33,7 @@ beforeEach(() => { }); test("test invoice list and modal ", async () => { - render(); + const { user } = render(); expect( screen @@ -47,45 +46,45 @@ test("test invoice list and modal ", async () => { ]); // click on the first (open) invoice and check modal - user.click(screen.getByText("Not Paid Yet")); + await user.click(screen.getByText("Not Paid Yet")); expect(screen.getAllByText("Not Paid Yet").length).toBe(2); expect(screen.getByText(mockInvoices[0].paymentRequest)).toBeInTheDocument(); - user.click(screen.getByRole("button", { name: "Cancel Invoice" })); + await user.click(screen.getByRole("button", { name: "Cancel Invoice" })); expect(mockCancelInvoice).toHaveBeenCalledWith(mockInvoices[0].rHash); //modal has been closed - await wait(() => + await waitFor(() => expect( screen.queryByText("Lightning Payment Request") ).not.toBeInTheDocument() ); // click on the second (settled) invoice and check modal - user.click(screen.getByText("Received")); + await user.click(screen.getByText("Received")); expect(screen.getAllByText("Received").length).toBe(2); expect(screen.getByText(mockInvoices[1].paymentRequest)).toBeInTheDocument(); expect(screen.getByRole("button", { name: "Cancel Invoice" }).disabled).toBe( true ); - user.click(screen.getByTestId("lninvoice-close-button")); + await user.click(screen.getByTestId("lninvoice-close-button")); expect( screen.queryByText("Lightning Payment Request") ).not.toBeInTheDocument(); // click on the second (canceled) invoice and check modal - user.click(screen.getByText("Canceled")); + await user.click(screen.getByText("Canceled")); expect(screen.getAllByText("Canceled").length).toBe(2); expect(screen.getByText(mockInvoices[2].paymentRequest)).toBeInTheDocument(); expect(screen.getByRole("button", { name: "Cancel Invoice" }).disabled).toBe( true ); - user.click(screen.getByTestId("lninvoice-close-button")); + await user.click(screen.getByTestId("lninvoice-close-button")); expect( screen.queryByText("Lightning Payment Request") ).not.toBeInTheDocument(); }); test("test add invoice form ", async () => { - render(); + const { user } = render(); const mockTooLargeAmount = "10"; const mockValidAmount = "0.0001"; @@ -95,30 +94,32 @@ test("test add invoice form ", async () => { name: "Create Invoice" }); expect(createInvoiceButton.disabled).toBe(false); - user.type(amountInput, mockTooLargeAmount); + await user.type(amountInput, mockTooLargeAmount); expect( screen.getByText(/cannot request more than total receive capacity/i) ).toBeInTheDocument(); - user.clear(amountInput); - user.type(amountInput, mockValidAmount); + await user.clear(amountInput); + await user.type(amountInput, mockValidAmount); expect( screen.queryByText(/cannot request more than total receive capacity/i) ).not.toBeInTheDocument(); expect(createInvoiceButton.disabled).toBe(false); const descInput = screen.getByLabelText("Description"); - user.type(descInput, mockDescText); - user.click(createInvoiceButton); + await user.type(descInput, mockDescText); + await user.click(createInvoiceButton); expect(mockAddInvoice).toHaveBeenCalledWith( mockDescText, mockValidAmount * 100000000 ); - await wait(() => expect(descInput.value).toBe("")); - expect(amountInput.value).toBe(""); + await waitFor(() => { + expect(descInput.value).toBe(""); + expect(amountInput.value).toBe(""); + }); }); test("0 invoice amount is allowed", async () => { - render(); + const { user } = render(); const mockZeroAmount = "0.000"; const mockDescText = "mock-desc-text"; @@ -128,21 +129,21 @@ test("0 invoice amount is allowed", async () => { }); expect(createInvoiceButton.disabled).toBe(false); - user.type(amountInput, mockZeroAmount); + await user.type(amountInput, mockZeroAmount); expect(createInvoiceButton.disabled).toBe(false); - user.clear(amountInput); + await user.clear(amountInput); expect(createInvoiceButton.disabled).toBe(false); const descInput = screen.getByLabelText("Description"); - user.type(descInput, mockDescText); - user.click(createInvoiceButton); + await user.type(descInput, mockDescText); + await user.click(createInvoiceButton); expect(mockAddInvoice).toHaveBeenCalledWith(mockDescText, 0); - await wait(() => expect(descInput.value).toBe("")); + await waitFor(() => expect(descInput.value).toBe("")); }); test("test filter control", async () => { - render(); + const { user } = render(); expect( screen @@ -158,10 +159,10 @@ test("test filter control", async () => { name: "EyeFilterMenu" })[1]; - user.click(filterMenuButton); - user.click(screen.getAllByText("Canceled")[0]); + await user.click(filterMenuButton); + await user.click(screen.getAllByText("Canceled")[0]); - await wait(() => + await waitFor(() => expect( screen .getAllByText(/Invoice for/i) @@ -169,17 +170,17 @@ test("test filter control", async () => { ).toStrictEqual([`Invoice for 0.00001 DCR${mockInvoices[2].rHashHex}`]) ); - user.click(filterMenuButton); - user.click(screen.getAllByText("Expired")[0]); + await user.click(filterMenuButton); + await user.click(screen.getAllByText("Expired")[0]); - await wait(() => + await waitFor(() => expect(screen.queryByText(/Invoice for/i)).not.toBeInTheDocument() ); expect(screen.getByText(/no invoices found/i)).toBeInTheDocument(); }); test("test sort control", async () => { - render(); + const { user } = render(); expect( screen @@ -195,10 +196,10 @@ test("test sort control", async () => { name: "EyeFilterMenu" })[0]; - user.click(sortMenuButton); - user.click(screen.getByText("Oldest")); + await user.click(sortMenuButton); + await user.click(screen.getByText("Oldest")); - await wait(() => + await waitFor(() => expect( screen .getAllByText(/Invoice for/i) @@ -212,7 +213,7 @@ test("test sort control", async () => { }); test("test search control", async () => { - render(); + const { user } = render(); expect( screen @@ -225,9 +226,9 @@ test("test search control", async () => { ]); const searchInput = screen.getByPlaceholderText("Filter by Payment Hash"); - user.type(searchInput, "mock-rhash-2"); + await user.type(searchInput, "mock-rhash-2"); - await wait(() => + await waitFor(() => expect( screen .getAllByText(/Invoice for/i) @@ -238,9 +239,9 @@ test("test search control", async () => { ]) ); - user.type(searchInput, "mock-rhash-22"); + await user.type(searchInput, "mock-rhash-22"); - await wait(() => + await waitFor(() => expect( screen .getAllByText(/Invoice for/i) @@ -248,9 +249,9 @@ test("test search control", async () => { ).toStrictEqual([`Invoice for 0.00001 DCR${mockInvoices[2].rHashHex}`]) ); - user.type(searchInput, "mock-rhash-22-12"); + await user.type(searchInput, "mock-rhash-22-12"); - await wait(() => + await waitFor(() => expect(screen.queryByText(/Invoice for/i)).not.toBeInTheDocument() ); expect(screen.getByText(/no invoices found/i)).toBeInTheDocument(); diff --git a/test/unit/components/views/LNPage/SendTab.spec.js b/test/unit/components/views/LNPage/SendTab.spec.js index f93fd9dbd6..e614ad5602 100644 --- a/test/unit/components/views/LNPage/SendTab.spec.js +++ b/test/unit/components/views/LNPage/SendTab.spec.js @@ -1,7 +1,6 @@ import { SendTab } from "components/views/LNPage/SendTab"; import { render } from "test-utils.js"; -import user from "@testing-library/user-event"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; import { DCR } from "constants"; import * as sel from "selectors"; import * as lna from "actions/LNActions"; @@ -99,14 +98,12 @@ const getClearButton = () => screen.getByRole("button", { name: "Clear Address" }); test("test send form with valid lightning request", async () => { - render(); + const { user } = render(); const reqCodeInput = getReqCodeInput(); - user.type(reqCodeInput, mockReqCode); - await wait(() => - expect(mockDecodePayRequest).toHaveBeenCalledWith(mockReqCode) - ); - expect(screen.getByText("Valid Lightning Request")).toBeInTheDocument(); + await user.type(reqCodeInput, mockReqCode); + expect(mockDecodePayRequest).toHaveBeenCalledWith(mockReqCode); + await waitFor(() => screen.getByText("Valid Lightning Request")); expect(screen.getByText("Amount").parentNode.textContent).toMatch( "Amount0.01000 DCR" ); @@ -126,7 +123,7 @@ test("test send form with valid lightning request", async () => { expect(screen.queryByText("CLTV Expiry:")).not.toBeInTheDocument(); // open details const details = screen.getByText("Details"); - user.click(details); + await user.click(details); expect(screen.getByText("CLTV Expiry:").parentNode.textContent).toMatch( `CLTV Expiry:${mockValidDecodedPayRequest.cltvExpiry}` @@ -139,10 +136,10 @@ test("test send form with valid lightning request", async () => { ); // close details - user.click(details); + await user.click(details); expect(screen.queryByText("CLTV Expiry:")).not.toBeInTheDocument(); - user.click(getSendButton()); + await user.click(getSendButton()); expect(mockSendPayment).toHaveBeenCalledWith(mockReqCode, 0); }); @@ -150,14 +147,12 @@ test("test send form with expired lightning request (with empty fallbackAddr)", mockDecodePayRequest = lnActions.decodePayRequest = jest.fn( () => () => Promise.resolve(mockExpiredDecodedPayRequest) ); - render(); + const { user } = render(); const reqCodeInput = getReqCodeInput(); - user.type(reqCodeInput, mockReqCode); - await wait(() => - expect(mockDecodePayRequest).toHaveBeenCalledWith(mockReqCode) - ); - expect(screen.getByText("Invoice expired")).toBeInTheDocument(); + await user.type(reqCodeInput, mockReqCode); + expect(mockDecodePayRequest).toHaveBeenCalledWith(mockReqCode); + await waitFor(() => screen.getByText("Invoice expired")); expect(screen.getByText("Expiration Time").parentNode.textContent).toMatch( "Expiration TimeExpired 1 hour ago" ); @@ -165,14 +160,14 @@ test("test send form with expired lightning request (with empty fallbackAddr)", expect(screen.queryByText("CLTV Expiry:")).not.toBeInTheDocument(); // open details const details = screen.getByText("Details"); - user.click(details); + await user.click(details); expect(screen.getByText("Fallback Address:").parentNode.textContent).toMatch( "Fallback Address:(empty fallback address)" ); // close details - user.click(details); + await user.click(details); expect(screen.queryByText("CLTV Expiry:")).not.toBeInTheDocument(); expect(querySendButton()).not.toBeInTheDocument(); @@ -183,31 +178,29 @@ test("test send form with invalid lightning request", async () => { mockDecodePayRequest = lnActions.decodePayRequest = jest.fn( () => () => Promise.reject(mockErrorResp) ); - render(); + const { user } = render(); const reqCodeInput = getReqCodeInput(); - user.type(reqCodeInput, mockReqCode); - await wait(() => - expect(mockDecodePayRequest).toHaveBeenCalledWith(mockReqCode) - ); - expect(screen.getByText(mockErrorResp)).toBeInTheDocument(); + await user.type(reqCodeInput, mockReqCode); + expect(mockDecodePayRequest).toHaveBeenCalledWith(mockReqCode); + await waitFor(() => screen.getByText(mockErrorResp)); }); test("test paste and clear button", async () => { - render(); + const { user } = render(); const mockPastedPayReq = "mockPastedPayReq"; wallet.readFromClipboard.mockImplementation(() => mockPastedPayReq); - user.click(getPasteButton()); - await wait(() => expect(getReqCodeInput().value).toBe(mockPastedPayReq)); + await user.click(getPasteButton()); + await waitFor(() => expect(getReqCodeInput().value).toBe(mockPastedPayReq)); - user.click(getClearButton()); - await wait(() => expect(getReqCodeInput().value).toBe("")); + await user.click(getClearButton()); + await waitFor(() => expect(getReqCodeInput().value).toBe("")); }); test("test payment list and modal ", async () => { - render(); + const { user } = render(); expect( screen @@ -220,33 +213,33 @@ test("test payment list and modal ", async () => { ]); // click on the first (outstanding) payment and check modal - user.click(screen.getByText("Pending")); + await user.click(screen.getByText("Pending")); expect(screen.getAllByText("Pending").length).toBe(2); //modal has been closed - user.click(screen.getByTestId("lnpayment-close-button")); - await wait(() => + await user.click(screen.getByTestId("lnpayment-close-button")); + await waitFor(() => expect(screen.queryByText("Lightning Payment")).not.toBeInTheDocument() ); // click on the second (failed) payment and check modal - user.click(screen.getByText("Failed")); + await user.click(screen.getByText("Failed")); expect(screen.getAllByText("Failed").length).toBe(2); expect( screen.getByText(mockFailedPayment[0].decoded.paymentHash) ).toBeInTheDocument(); - user.click(screen.getByTestId("lnpayment-close-button")); + await user.click(screen.getByTestId("lnpayment-close-button")); expect(screen.queryByText("Lightning Payment")).not.toBeInTheDocument(); // click on the second (confirmed) payment and check modal - user.click(screen.getByText("Confirmed")); + await user.click(screen.getByText("Confirmed")); expect(screen.getAllByText("Confirmed").length).toBe(2); expect(screen.getByText(mockPayments[0].paymentHash)).toBeInTheDocument(); - user.click(screen.getByTestId("lnpayment-close-button")); + await user.click(screen.getByTestId("lnpayment-close-button")); expect(screen.queryByText("Lightning Payment")).not.toBeInTheDocument(); }); test("test sort control", async () => { - render(); + const { user } = render(); expect( screen @@ -262,10 +255,10 @@ test("test sort control", async () => { name: "EyeFilterMenu" })[0]; - user.click(sortMenuButton); - user.click(screen.getByText("Oldest")); + await user.click(sortMenuButton); + await user.click(screen.getByText("Oldest")); - await wait(() => + await waitFor(() => expect( screen .getAllByText(/Sent Payment/i) @@ -279,7 +272,7 @@ test("test sort control", async () => { }); test("test search control", async () => { - render(); + const { user } = render(); expect( screen @@ -292,9 +285,9 @@ test("test search control", async () => { ]); const searchInput = screen.getByPlaceholderText("Filter by Payment Hash"); - user.type(searchInput, "payment-hash-0"); + await user.type(searchInput, "payment-hash-0"); - await wait(() => + await waitFor(() => expect( screen .getAllByText(/Sent Payment/i) @@ -305,9 +298,9 @@ test("test search control", async () => { ]) ); - user.type(searchInput, "mock-hash-22-12"); + await user.type(searchInput, "mock-hash-22-12"); - await wait(() => + await waitFor(() => expect(screen.queryByText(/Sent Payment/i)).not.toBeInTheDocument() ); expect(screen.getByText(/no payment found/i)).toBeInTheDocument(); diff --git a/test/unit/components/views/PrivacyPage/PrivacyTab.spec.js b/test/unit/components/views/PrivacyPage/PrivacyTab.spec.js index 991840ceaf..1462b1d41b 100644 --- a/test/unit/components/views/PrivacyPage/PrivacyTab.spec.js +++ b/test/unit/components/views/PrivacyPage/PrivacyTab.spec.js @@ -1,12 +1,12 @@ import PrivacyPage from "components/views/PrivacyPage"; import PrivacyTab from "components/views/PrivacyPage/PrivacyTab"; import { render } from "test-utils.js"; -import user from "@testing-library/user-event"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; +import { act } from "react-dom/test-utils"; import { MIXED_ACCOUNT, CHANGE_ACCOUNT, DCR } from "constants"; import * as sel from "selectors"; -import * as act from "actions/AccountMixerActions"; +import * as actM from "actions/AccountMixerActions"; import * as wl from "wallet"; import * as ca from "actions/ControlActions"; import * as sa from "actions/SnackbarActions"; @@ -14,7 +14,7 @@ import * as sa from "actions/SnackbarActions"; const selectors = sel; const wallet = wl; const controlActions = ca; -const accountMixerActions = act; +const accountMixerActions = actM; const snackbarActions = sa; const mockDefaultAccount = { @@ -66,6 +66,12 @@ const mockSpendingAccounts = [ mockMixedAccount, mockUnMixedAccount ]; + +const mockVisibleAccounts = [ + mockDefaultAccount, + mockMixedAccount, + mockUnMixedAccount +]; const mockCsppServer = "mockCsppServer.decred.org"; const mockCsppPort = "1234"; const mockMixedAccountBranch = 0; @@ -83,6 +89,7 @@ let mockDispatchSingleMessage; beforeEach(() => { selectors.currencyDisplay = jest.fn(() => DCR); selectors.defaultSpendingAccount = jest.fn(() => mockMixedAccount); + selectors.visibleAccounts = jest.fn(() => mockVisibleAccounts); selectors.getPrivacyEnabled = jest.fn(() => true); selectors.isWatchingOnly = jest.fn(() => false); selectors.getMixedAccountName = jest.fn(() => mockMixedAccount.name); @@ -91,7 +98,6 @@ beforeEach(() => { selectors.walletService = jest.fn(() => { return {}; }); - selectors.nextAddressAccount = jest.fn(() => mockUnMixedAccount); selectors.nextAddress = jest.fn(() => mockNextAddress); selectors.getRunningIndicator = jest.fn(() => false); @@ -156,24 +162,24 @@ const getPrivacyCheckbox = () => screen.getByTestId("privacyCheckbox"); const getSendToSelfButton = () => screen.getByRole("button", { name: "Send to Self" }); -test("create default mixer accounts on `Privacy Configuration` view", () => { +test("create default mixer accounts on `Privacy Configuration` view", async () => { selectors.getMixedAccount = jest.fn(() => null); selectors.getChangeAccount = jest.fn(() => null); selectors.balances = jest.fn(() => []); - render(); - user.click(screen.getByText("Privacy")); + const { user } = render(); + await user.click(screen.getByText("Privacy")); expect(screen.getByText("Privacy Configuration")).toBeInTheDocument(); const testPassphrase = "test-passphrase"; - user.click(getCreateDefaultAccountsButton()); - user.type(getPrivatePassphraseInput(), testPassphrase); + await user.click(getCreateDefaultAccountsButton()); + await user.type(getPrivatePassphraseInput(), testPassphrase); // cancel first - user.click(getCancelButton()); + await user.click(getCancelButton()); - user.click(getCreateDefaultAccountsButton()); - user.type(getPrivatePassphraseInput(), testPassphrase); - user.click(getContinueButton()); + await user.click(getCreateDefaultAccountsButton()); + await user.type(getPrivatePassphraseInput(), testPassphrase); + await user.click(getContinueButton()); expect(mockCreateNeededAccounts).toHaveBeenCalledWith( testPassphrase, @@ -182,10 +188,10 @@ test("create default mixer accounts on `Privacy Configuration` view", () => { ); }); -test("create needed mixer accounts on `Privacy Configuration` view", () => { +test("create needed mixer accounts on `Privacy Configuration` view", async () => { selectors.getMixedAccount = jest.fn(() => null); selectors.getChangeAccount = jest.fn(() => null); - render(); + const { user } = render(); expect( screen.getByText( /It looks like you already have one of the default accounts/i @@ -198,20 +204,23 @@ test("create needed mixer accounts on `Privacy Configuration` view", () => { const testPassphrase = "test-passphrase"; const testMixedAccountName = "test-mixed-account-name"; const testUnMixedAccountName = "test-unmixed-account-name"; - user.click(getCreateNeededAccountsButton()); - user.type(getPrivatePassphraseInput(), testPassphrase); + await user.click(getCreateNeededAccountsButton()); + await user.type(getPrivatePassphraseInput(), testPassphrase); // cancel first - user.click(getCancelButton()); + await user.click(getCancelButton()); - user.click(getCreateNeededAccountsButton()); - user.type(getPrivatePassphraseInput(), testPassphrase); - user.type(screen.getByLabelText("Mixed Account Name"), testMixedAccountName); - user.type( + await user.click(getCreateNeededAccountsButton()); + await user.type(getPrivatePassphraseInput(), testPassphrase); + await user.type( + screen.getByLabelText("Mixed Account Name"), + testMixedAccountName + ); + await user.type( screen.getByLabelText("Unmixed Account Name"), testUnMixedAccountName ); - user.click(getContinueButton()); + await user.click(getContinueButton()); expect(mockCreateNeededAccounts).toHaveBeenCalledWith( testPassphrase, @@ -232,8 +241,17 @@ test("test insufficient unmixed account balance error message", async () => { (acc) => acc.value == acctId ) ); - render(); - await wait(() => + render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockUnMixedAccount.value + } + } + } + }); + + await waitFor(() => expect(screen.getByText("Unmixed Balance").parentNode.className).toMatch( /balanceError/i ) @@ -245,8 +263,17 @@ test("test insufficient unmixed account balance error message", async () => { }); test("start coin mixer", async () => { - render(); - await wait(() => + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + + await waitFor(() => expect( screen.getByText("Unmixed Balance").parentNode.className ).not.toMatch(/balanceError/i) @@ -265,15 +292,15 @@ test("start coin mixer", async () => { ).toMatchInlineSnapshot('"249.79547928 DCRMixed Balance"'); const testPassphrase = "test-passphrase"; - user.click(startMixerButton); + await user.click(startMixerButton); // cancel first - user.click(getCancelButton()); + await user.click(getCancelButton()); - user.click(startMixerButton); + await user.click(startMixerButton); - user.type(getPrivatePassphraseInput(), testPassphrase); - user.click(getContinueButton()); + await user.type(getPrivatePassphraseInput(), testPassphrase); + await user.click(getContinueButton()); expect(mockRunAccountMixer).toHaveBeenCalledWith({ changeAccount: mockUnMixedAccount.accountNumber, @@ -284,116 +311,174 @@ test("start coin mixer", async () => { }); }); -test("stop coin mixer", () => { +test("stop coin mixer", async () => { selectors.getAccountMixerRunning = jest.fn(() => true); - render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); expect(screen.getByText("Mixer is running")).toBeInTheDocument(); - user.click(getStopMixerButton()); + await user.click(getStopMixerButton()); expect(mockStopAccountMixer).toHaveBeenCalled(); }); test("mixer is disabled (Autobuyer running)", () => { selectors.getRunningIndicator = jest.fn(() => true); - render(); + render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); expect(getSendToSelfButton().disabled).toBe(true); expect(getStartMixerButton().disabled).toBe(true); }); -test("allow sending from unmixed accounts", () => { - render(); +test("allow sending from unmixed accounts", async () => { + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); const checkbox = getPrivacyCheckbox(); expect(checkbox.checked).toBe(false); - user.click(checkbox); + await user.click(checkbox); expect(screen.getByText("Sending from Unmixed Accounts")).toBeInTheDocument(); expect(getEnableSendingFromUnmixedAccountButton().disabled).toBe(true); // cancel first - user.click(getCancelButton()); + await user.click(getCancelButton()); - user.click(checkbox); + await user.click(checkbox); // type random text, enable button should stay disabled - user.type(getConfirmInput(), "random text"); + await user.type(getConfirmInput(), "random text"); expect(getEnableSendingFromUnmixedAccountButton().disabled).toBe(true); - user.clear(getConfirmInput()); - user.type(getConfirmInput(), "I understand the risks"); - user.click(getEnableSendingFromUnmixedAccountButton()); + await user.clear(getConfirmInput()); + await user.type(getConfirmInput(), "I understand the risks"); + await user.click(getEnableSendingFromUnmixedAccountButton()); expect(mockToggleAllowSendFromUnmixed).toHaveBeenCalledWith(true); }); -test("sending from unmixed accounts is allowed already", () => { +test("sending from unmixed accounts is allowed already", async () => { selectors.getAllowSendFromUnmixed = jest.fn(() => true); - render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); const checkbox = getPrivacyCheckbox(); expect(checkbox.checked).toBe(true); - user.click(checkbox); + await user.click(checkbox); expect(mockToggleAllowSendFromUnmixed).toHaveBeenCalledWith(false); }); test("Send to Unmixed Account form", async () => { - render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + const sendToSelfBtn = getSendToSelfButton(); const amountInput = screen.getByLabelText("Amount:"); const testAmount = "12"; expect(sendToSelfBtn.disabled).toBe(true); - user.type(amountInput, testAmount); + await user.type(amountInput, testAmount); - await wait(() => expect(mockConstructTransactionAttempt).toHaveBeenCalled()); + await waitFor(() => + expect(mockConstructTransactionAttempt).toHaveBeenCalled() + ); - user.click( + await user.click( screen.getByText("Send all funds from selected account").nextElementSibling ); expect(screen.getByText("Amount:").nextElementSibling.textContent).toBe( "19.00000 DCR" ); - user.click(screen.getByText("Cancel sending all funds").nextElementSibling); + await user.click( + screen.getByText("Cancel sending all funds").nextElementSibling + ); expect(screen.getByLabelText("Amount:").value).toBe(""); }); test("check logs", async () => { + jest.useFakeTimers(); mockGetPrivacyLogs = wallet.getPrivacyLogs = jest.fn(() => Promise.resolve(mockLogLine) ); - render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); const logsLabel = screen.getByText("Logs"); - user.click(logsLabel); + await user.click(logsLabel); + + act(() => { + jest.advanceTimersByTime(2001); + }); - await wait(() => expect(mockGetPrivacyLogs).toHaveBeenCalledTimes(2)); - await wait(() => screen.getByText(mockLogLine)); + await waitFor(() => expect(mockGetPrivacyLogs).toHaveBeenCalledTimes(2)); + await waitFor(() => screen.getByText(mockLogLine)); - user.click(logsLabel); - await wait(() => + await user.click(logsLabel); + await waitFor(() => expect(screen.queryByText(mockLogLine)).not.toBeInTheDocument() ); }); -test("privacy configuration have to be disabled in watching only (already have mixed or unmixed account)", () => { +test("privacy configuration have to be disabled in watching only (already have mixed or unmixed account)", async () => { selectors.getMixedAccount = jest.fn(() => null); selectors.getChangeAccount = jest.fn(() => null); selectors.isWatchingOnly = jest.fn(() => true); - render(); - user.click(getCreateNeededAccountsButton()); + const { user } = render(); + await user.click(getCreateNeededAccountsButton()); expect(mockDispatchSingleMessage).toHaveBeenCalledTimes(1); }); -test("privacy configuration have to be disabled in watching only ", () => { +test("privacy configuration have to be disabled in watching only ", async () => { selectors.getMixedAccount = jest.fn(() => null); selectors.getChangeAccount = jest.fn(() => null); selectors.isWatchingOnly = jest.fn(() => true); selectors.balances = jest.fn(() => []); - render(); - user.click(getCreateDefaultAccountsButton()); + const { user } = render(); + await user.click(getCreateDefaultAccountsButton()); expect(mockDispatchSingleMessage).toHaveBeenCalledTimes(1); }); diff --git a/test/unit/components/views/PrivacyPage/SecurityTab.spec.js b/test/unit/components/views/PrivacyPage/SecurityTab.spec.js index 2baf282465..2e2c081b1d 100644 --- a/test/unit/components/views/PrivacyPage/SecurityTab.spec.js +++ b/test/unit/components/views/PrivacyPage/SecurityTab.spec.js @@ -1,9 +1,7 @@ import PrivacyPage from "components/views/PrivacyPage"; import SecurityTab from "components/views/PrivacyPage/SecurityTab"; import { render } from "test-utils.js"; -import user from "@testing-library/user-event"; -import { fireEvent } from "@testing-library/react"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; import * as sel from "selectors"; import * as ca from "actions/ControlActions"; import * as trza from "actions/TrezorActions"; @@ -19,6 +17,11 @@ let mockSignMessageAttemptTrezor; let mockGetMessageVerificationServiceAttempt; let mockVerifyMessageAttempt; +const testAddress = "TsfDLrRkk9ciUuwfp2b8PawwnukYD7yAjGd"; +const testSignature = "test-signature"; +const testMessage = "secret message"; +const testPassphrase = "testpassphrase"; + beforeEach(() => { selectors.walletService = jest.fn(() => { return {}; @@ -58,18 +61,16 @@ const getVerifyMessageButton = () => screen.getByRole("button", { name: "Verify Message" }); test("type invalid address to validate", async () => { - render(); - user.click(screen.getByText("Security Center")); + const { user } = render(); + await user.click(screen.getByText("Security Center")); const validateAddressInput = getValidateAddressInput(); - fireEvent.change(validateAddressInput, { - target: { value: "random text" } - }); + await user.type(validateAddressInput, "random text"); - await wait(() => screen.getByText("Invalid address")); + await waitFor(() => screen.getByText("Invalid address")); - user.clear(validateAddressInput); - await wait(() => + await user.clear(validateAddressInput); + await waitFor(() => expect(screen.queryByText("Invalid address")).not.toBeInTheDocument() ); }); @@ -93,14 +94,12 @@ test("type valid, not owned address to validate", async () => { sigsRequired: 0 }) ); - render(); + const { user } = render(); const validateAddressInput = getValidateAddressInput(); - fireEvent.change(validateAddressInput, { - target: { value: "TsfDLrRkk9ciUuwfp2b8PawwnukYD7yAjGd" } - }); - await wait(() => screen.getByText("Address Valid, Not Owned")); + await user.type(validateAddressInput, testAddress); + await waitFor(() => screen.getByText("Address Valid, Not Owned")); }); test("type valid, owned address to validate", async () => { @@ -122,14 +121,12 @@ test("type valid, owned address to validate", async () => { sigsRequired: 0 }) ); - render(); + const { user } = render(); const validateAddressInput = getValidateAddressInput(); - fireEvent.change(validateAddressInput, { - target: { value: "TsfDLrRkk9ciUuwfp2b8PawwnukYD7yAjGd" } - }); - await wait(() => screen.getByText("Owned address")); + await user.type(validateAddressInput, testAddress); + await waitFor(() => screen.getByText("Owned address")); expect( screen.getByText(/Account Number/i).parentElement.textContent ).toMatchInlineSnapshot('"Account Number4Branch1Index57"'); @@ -156,31 +153,24 @@ test("test signing message", async () => { sigsRequired: 0 }) ); - render(); - const testAddress = "TsfDLrRkk9ciUuwfp2b8PawwnukYD7yAjGd"; - const testMessage = "secret message"; + const { user } = render(); const signMessageButton = getSignMessageButton(); - const testPassphrase = "testpassphrase"; expect(signMessageButton.disabled).toBe(true); - fireEvent.change(getSignMessageAddressInput(), { - target: { value: testAddress } - }); - fireEvent.change(getSignMessageMsgInput(), { - target: { value: testMessage } - }); + await user.type(getSignMessageAddressInput(), testAddress); + await user.type(getSignMessageMsgInput(), testMessage); - await wait(() => expect(signMessageButton.disabled).toBe(false)); + await waitFor(() => expect(signMessageButton.disabled).toBe(false)); - user.click(signMessageButton); + await user.click(signMessageButton); // cancel first - user.click(getCancelButton()); + await user.click(getCancelButton()); - user.click(signMessageButton); - user.type(getPrivatePassphraseInput(), testPassphrase); + await user.click(signMessageButton); + await user.type(getPrivatePassphraseInput(), testPassphrase); - user.click(getContinueButton()); + await user.click(getContinueButton()); expect(mockSignMessageAttempt).toHaveBeenCalledWith( testAddress, @@ -211,22 +201,16 @@ test("test signing message on a trezor-backed wallet", async () => { sigsRequired: 0 }) ); - render(); - const testAddress = "TsfDLrRkk9ciUuwfp2b8PawwnukYD7yAjGd"; - const testMessage = "secret message"; + const { user } = render(); const signMessageButton = getSignMessageButton(); expect(signMessageButton.disabled).toBe(true); - fireEvent.change(getSignMessageAddressInput(), { - target: { value: testAddress } - }); - fireEvent.change(getSignMessageMsgInput(), { - target: { value: testMessage } - }); + await user.type(getSignMessageAddressInput(), testAddress); + await user.type(getSignMessageMsgInput(), testMessage); - await wait(() => expect(signMessageButton.disabled).toBe(false)); + await waitFor(() => expect(signMessageButton.disabled).toBe(false)); - user.click(signMessageButton); + await user.click(signMessageButton); expect(mockSignMessageAttemptTrezor).toHaveBeenCalledWith( testAddress, @@ -253,20 +237,14 @@ test("test signing message using address not owning", async () => { sigsRequired: 0 }) ); - render(); - const testAddress = "TsfDLrRkk9ciUuwfp2b8PawwnukYD7yAjGd"; - const testMessage = "secret message"; + const { user } = render(); const signMessageButton = getSignMessageButton(); expect(signMessageButton.disabled).toBe(true); - fireEvent.change(getSignMessageAddressInput(), { - target: { value: testAddress } - }); - fireEvent.change(getSignMessageMsgInput(), { - target: { value: testMessage } - }); + await user.type(getSignMessageAddressInput(), testAddress); + await user.type(getSignMessageMsgInput(), testMessage); - await wait(() => + await waitFor(() => screen.getByText("Please enter a valid address owned by you") ); }); @@ -295,29 +273,20 @@ test("test verify message", async () => { sigsRequired: 0 }) ); - render(); - user.click(getVerifyMessageToggleBt()); - const testAddress = "TsfDLrRkk9ciUuwfp2b8PawwnukYD7yAjGd"; - const testSignature = "test-signature"; - const testMessage = "secret message"; + const { user } = render(); + await user.click(getVerifyMessageToggleBt()); const verifyMessageButton = getVerifyMessageButton(); expect(verifyMessageButton.disabled).toBe(true); - await wait(() => + await waitFor(() => expect(mockGetMessageVerificationServiceAttempt).toHaveBeenCalled() ); - fireEvent.change(getSignMessageAddressInput(), { - target: { value: testAddress } - }); - fireEvent.change(getSignMessageSignatureInput(), { - target: { value: testSignature } - }); - fireEvent.change(getSignMessageMsgInput(), { - target: { value: testMessage } - }); - await wait(() => expect(verifyMessageButton.disabled).toBe(false)); + await user.type(getSignMessageAddressInput(), testAddress); + await user.type(getSignMessageSignatureInput(), testSignature); + await user.type(getSignMessageMsgInput(), testMessage); + await waitFor(() => expect(verifyMessageButton.disabled).toBe(false)); - user.click(verifyMessageButton); + await user.click(verifyMessageButton); expect(mockVerifyMessageAttempt).toHaveBeenCalledWith( testAddress, testMessage, @@ -351,29 +320,20 @@ test("test verify invalid message", async () => { sigsRequired: 0 }) ); - render(); - user.click(getVerifyMessageToggleBt()); - const testAddress = "TsfDLrRkk9ciUuwfp2b8PawwnukYD7yAjGd"; - const testSignature = "test-signature"; - const testMessage = "secret message"; + const { user } = render(); + await user.click(getVerifyMessageToggleBt()); const verifyMessageButton = getVerifyMessageButton(); expect(verifyMessageButton.disabled).toBe(true); - await wait(() => + await waitFor(() => expect(mockGetMessageVerificationServiceAttempt).toHaveBeenCalled() ); - fireEvent.change(getSignMessageAddressInput(), { - target: { value: testAddress } - }); - fireEvent.change(getSignMessageSignatureInput(), { - target: { value: testSignature } - }); - fireEvent.change(getSignMessageMsgInput(), { - target: { value: testMessage } - }); - await wait(() => expect(verifyMessageButton.disabled).toBe(false)); + await user.type(getSignMessageAddressInput(), testAddress); + await user.type(getSignMessageSignatureInput(), testSignature); + await user.type(getSignMessageMsgInput(), testMessage); + await waitFor(() => expect(verifyMessageButton.disabled).toBe(false)); - user.click(verifyMessageButton); + await user.click(verifyMessageButton); expect(mockVerifyMessageAttempt).toHaveBeenCalledWith( testAddress, testMessage, @@ -383,6 +343,6 @@ test("test verify invalid message", async () => { expect(screen.getByText("Invalid Signature")).toBeInTheDocument(); // go back to Sign Message - user.click(getSignMessageToggleBt()); + await user.click(getSignMessageToggleBt()); expect(getSignMessageButton()).toBeInTheDocument(); }); diff --git a/test/unit/components/views/SettingsPage/SettingsPage.spec.js b/test/unit/components/views/SettingsPage/SettingsPage.spec.js index 3a700b9312..239ae661f3 100644 --- a/test/unit/components/views/SettingsPage/SettingsPage.spec.js +++ b/test/unit/components/views/SettingsPage/SettingsPage.spec.js @@ -1,7 +1,7 @@ import SettingsPage from "components/views/SettingsPage"; import { render } from "test-utils.js"; -import user from "@testing-library/user-event"; -import { screen, fireEvent, wait } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { screen, fireEvent, waitFor } from "@testing-library/react"; import * as sel from "selectors"; import * as ca from "actions/ControlActions"; @@ -169,16 +169,18 @@ test("show error when there is no walletService", () => { }); test("test close wallet button (there is no ongoing process) ", async () => { + const user = userEvent.setup(); render(, { initialState: { settings: testSettings } }); - const changeFn = () => { - user.click(screen.getByRole("button", { name: "Close Wallet" })); + const changeFn = async () => { + await user.click(screen.getByRole("button", { name: "Close Wallet" })); }; - changeFn(); + await changeFn(); await testConfirmModal( + user, changeFn, "Confirmation Required", `Are you sure you want to close ${testWalletName} and return to the launcher?` @@ -199,23 +201,26 @@ const testCloseWalletButtonUnpaidTicketFee = async ( ] }; }); + const user = userEvent.setup(); render(, { initialState: { settings: testSettings } }); - const changeFn = () => { - user.click(screen.getByRole("button", { name: "Close Wallet" })); + const changeFn = async () => { + await user.click(screen.getByRole("button", { name: "Close Wallet" })); }; - changeFn(); + await changeFn(); if (expectDefaultModal) { await testConfirmModal( + user, changeFn, "Confirmation Required", `Are you sure you want to close ${testWalletName} and return to the launcher?` ); } else { await testConfirmModal( + user, changeFn, "VSP Tickets Fee Error", /You have outstanding tickets that are not properly registered with a VSP/i, @@ -239,17 +244,19 @@ test("test close wallet button (account mixer is running) ", async () => { mockGetAccountMixerRunning = selectors.getAccountMixerRunning = jest.fn( () => true ); + const user = userEvent.setup(); render(, { initialState: { settings: testSettings } }); expect(mockGetAccountMixerRunning).toHaveBeenCalled(); - const changeFn = () => { - user.click(screen.getByRole("button", { name: "Close Wallet" })); + const changeFn = async () => { + await user.click(screen.getByRole("button", { name: "Close Wallet" })); }; - changeFn(); + await changeFn(); await testConfirmModal( + user, changeFn, "Account mixer is running", "Account mixer is currently running. Ongoing mixes will be cancelled and no more Decred will be mixed if you proceed.", @@ -260,17 +267,19 @@ test("test close wallet button (account mixer is running) ", async () => { test("test close wallet button (still finalizing ticket purchases) ", async () => { mockPurchaseTicketsRequestAttempt = selectors.purchaseTicketsRequestAttempt = jest.fn(() => true); + const user = userEvent.setup(); render(, { initialState: { settings: testSettings } }); expect(mockPurchaseTicketsRequestAttempt).toHaveBeenCalled(); - const changeFn = () => { - user.click(screen.getByRole("button", { name: "Close Wallet" })); + const changeFn = async () => { + await user.click(screen.getByRole("button", { name: "Close Wallet" })); }; - changeFn(); + await changeFn(); await testConfirmModal( + user, changeFn, "Purchasing Tickets", "Decrediton is still finalizing ticket purchases. Tickets may not be registered with the VSP if you proceed now, which can result in missed votes.", @@ -280,17 +289,19 @@ test("test close wallet button (still finalizing ticket purchases) ", async () = test("test close wallet button (auto ticket buyer still running) ", async () => { selectors.getTicketAutoBuyerRunning = jest.fn(() => true); + const user = userEvent.setup(); render(, { initialState: { settings: testSettings } }); expect(mockPurchaseTicketsRequestAttempt).toHaveBeenCalled(); - const changeFn = () => { - user.click(screen.getByRole("button", { name: "Close Wallet" })); + const changeFn = async () => { + await user.click(screen.getByRole("button", { name: "Close Wallet" })); }; - changeFn(); + await changeFn(); await testConfirmModal( + user, changeFn, "Auto Ticket Buyer Still Running", "If you proceed, it will be closed and no more tickets will be purchased.", @@ -327,22 +338,23 @@ const getOptionByNameAndType = (name, type) => { }; const testConfirmModal = async ( + user, changeFn, confirmHeaderText, confirmContent, confirmButtonLabel = "Confirm" ) => { - // wait for the confirm modal and cancel - await wait(() => screen.getByText(confirmHeaderText)); + // waitFor for the confirm modal and cancel + await waitFor(() => screen.getByText(confirmHeaderText)); if (confirmContent) { expect(screen.getByText(confirmContent)).toBeInTheDocument(); } - user.click(screen.getByText("Cancel")); + await user.click(screen.getByText("Cancel")); expect(screen.queryByText(confirmHeaderText)).not.toBeInTheDocument(); - // wait for the confirm modal and confirm - changeFn(); - await wait(() => screen.getByText(confirmHeaderText)); - user.click(screen.getByRole("button", { name: confirmButtonLabel })); + // waitFor for the confirm modal and confirm + await changeFn(); + await waitFor(() => screen.getByText(confirmHeaderText)); + await user.click(screen.getByRole("button", { name: confirmButtonLabel })); }; const testComboxBoxInput = async ( @@ -353,6 +365,7 @@ const testComboxBoxInput = async ( needsConfirm, tabLabel = null ) => { + const user = userEvent.setup(); render(, { initialState: { settings: testSettings @@ -360,22 +373,22 @@ const testComboxBoxInput = async ( }); if (tabLabel) { // go to the specified tab - user.click(screen.getAllByText(tabLabel)[0]); + await user.click(screen.getAllByText(tabLabel)[0]); } const inputControl = screen.getByLabelText(labelName); const inputValueSpan = getOptionByNameAndType(oldValue, "singleValue"); expect(inputValueSpan.textContent).toMatch(oldValue); - const changeFn = () => { - user.click(inputControl); - user.click(getOptionByNameAndType(option, "option")); + const changeFn = async () => { + await user.click(inputControl); + await user.click(getOptionByNameAndType(option, "option")); }; - changeFn(); + await changeFn(); if (needsConfirm) { - await testConfirmModal(changeFn, "Reset required"); + await testConfirmModal(user, changeFn, "Reset required"); } - await wait(() => + await waitFor(() => expect(mockSaveSettings).toHaveBeenCalledWith({ ...testCurrentSettings, ...expectedChange @@ -434,14 +447,16 @@ const testTextFieldInput = async ( expectedChange, needsConfirm ) => { + const user = userEvent.setup(); render(, { initialState: { settings: testSettings } }); const inputControl = screen.getByLabelText(labelName); expect(inputControl.value).toMatch(oldValue); - user.clear(inputControl); - user.type(inputControl, newValue); + fireEvent.change(inputControl, { + target: { value: newValue } + }); // press enter fireEvent.keyDown(inputControl, { key: "enter", @@ -449,10 +464,10 @@ const testTextFieldInput = async ( }); if (needsConfirm) { - testConfirmModal("Save", "Reset required"); + testConfirmModal(user, "Save", "Reset required"); } - await wait(() => + await waitFor(() => expect(mockSaveSettings).toHaveBeenCalledWith({ ...testCurrentSettings, ...expectedChange @@ -478,13 +493,14 @@ test.each([ ] ])("change '%s' TextInput from '%s' to '%s' expeced %s", testTextFieldInput); -const testRadioButtonGroupInput = (configKey, options, defaultValue) => { +const testRadioButtonGroupInput = async (configKey, options, defaultValue) => { + const user = userEvent.setup(); render(, { initialState: { settings: testSettings } }); - user.click(screen.getByText("General")); + await user.click(screen.getByText("General")); options.forEach((option) => expect(screen.getByLabelText(option.label).checked).toBe( @@ -496,7 +512,7 @@ const testRadioButtonGroupInput = (configKey, options, defaultValue) => { const otherOption = options.filter( (option) => option.value != defaultValue )[0]; - user.click(screen.getByLabelText(otherOption.label)); + await user.click(screen.getByLabelText(otherOption.label)); options.forEach((option) => expect(screen.getByLabelText(option.label).checked).toBe( option.value == otherOption.value @@ -511,7 +527,7 @@ const testRadioButtonGroupInput = (configKey, options, defaultValue) => { const defaultOption = options.filter( (option) => option.value == defaultValue )[0]; - user.click(screen.getByLabelText(defaultOption.label)); + await user.click(screen.getByLabelText(defaultOption.label)); options.forEach((option) => expect(screen.getByLabelText(option.label).checked).toBe( option.value == defaultOption.value @@ -530,13 +546,14 @@ test.each([ ] ])("test '%s' RadioButtonGroup", testRadioButtonGroupInput); -const testCheckBoxInputOnPrivacy = (label, configKey) => { +const testCheckBoxInputOnPrivacy = async (label, configKey) => { + const user = userEvent.setup(); render(, { initialState: { settings: testSettings } }); - user.click(screen.getByText("Privacy and Security")); + await user.click(screen.getByText("Privacy and Security")); const checkbox = screen.getByLabelText(label); const defaultCheckedValue = @@ -544,7 +561,7 @@ const testCheckBoxInputOnPrivacy = (label, configKey) => { expect(checkbox.checked).toBe(defaultCheckedValue); - user.click(checkbox); + await user.click(checkbox); expect(checkbox.checked).toBe(!defaultCheckedValue); const expectedChange = { ...testCurrentSettings }; @@ -568,20 +585,21 @@ test.each([ ["Decred Block Explorer", EXTERNALREQUEST_DCRDATA] ])("test '%s' Checkbox", testCheckBoxInputOnPrivacy); -test("test launcer CheckBox", () => { +test("test launcer CheckBox", async () => { + const user = userEvent.setup(); render(, { initialState: { settings: testSettings } }); - user.click(screen.getByText("General")); + await user.click(screen.getByText("General")); const checkbox = screen.getByLabelText( "Launch wallet immediately after loading completes" ); expect(checkbox.checked).toBe(testDefaultAutoWalletLaunching); - user.click(checkbox); + await user.click(checkbox); expect(checkbox.checked).toBe(!testDefaultAutoWalletLaunching); const expectedChange = { @@ -598,94 +616,98 @@ const getFieldRequiredErrorCount = () => { return inputErrors ? inputErrors.length : 0; }; -const testPassphraseInputRequiedErrorMsg = (label) => { +const testPassphraseInputRequiedErrorMsg = async (user, label) => { const testString = "test-string"; const inputErrorsCount = getFieldRequiredErrorCount(); const input = screen.getByLabelText(label); - user.type(input, testString); + await user.type(input, testString); expect(input.value).toMatch(testString); - user.clear(input); + await user.clear(input); expect(getFieldRequiredErrorCount()).toBe(inputErrorsCount + 1); // type again, error message should disappear - user.type(input, testString); + await user.type(input, testString); expect(getFieldRequiredErrorCount()).toBe(inputErrorsCount); // clean up - user.clear(input); + await user.clear(input); expect(getFieldRequiredErrorCount()).toBe(inputErrorsCount + 1); }; -test("test update private passphrase", () => { +test("test update private passphrase", async () => { + const user = userEvent.setup(); render(, { initialState: { settings: testSettings } }); - user.click(screen.getByText("Privacy and Security")); + await user.click(screen.getByText("Privacy and Security")); const updateButton = screen.getByRole("button", { name: "Update Private Passphrase" }); const modalHeaderText = "Change your passphrase"; // click and cancel - user.click(updateButton); + await user.click(updateButton); expect(screen.getByText(modalHeaderText)).toBeInTheDocument(); - user.click(screen.getByText("Cancel")); + await user.click(screen.getByText("Cancel")); expect(screen.queryByText(modalHeaderText)).not.toBeInTheDocument(); - user.click(updateButton); + await user.click(updateButton); expect(screen.getByText(modalHeaderText)).toBeInTheDocument(); const continueButton = screen.getByText("Continue"); expect(continueButton.disabled).toBe(true); // test 'This Field is required' error message - testPassphraseInputRequiedErrorMsg("Private Passphrase"); - testPassphraseInputRequiedErrorMsg("New Private Passphrase"); - testPassphraseInputRequiedErrorMsg("Confirm"); + await testPassphraseInputRequiedErrorMsg(user, "Private Passphrase"); + await testPassphraseInputRequiedErrorMsg(user, "New Private Passphrase"); + await testPassphraseInputRequiedErrorMsg(user, "Confirm"); expect(continueButton.disabled).toBe(true); // fill input fields const testPassphrase = "test-passphrase"; const testNewPassphrase = "test-new-passphrase"; const testConfirmPassphrase = "test-confirm-passphrase"; - user.type(screen.getByLabelText(/^private passphrase/i), testPassphrase); - user.type( + await user.type( + screen.getByLabelText(/^private passphrase/i), + testPassphrase + ); + await user.type( screen.getByLabelText(/^new private passphrase/i), testNewPassphrase ); expect(continueButton.disabled).toBe(true); - user.type(screen.getByLabelText(/^confirm/i), testConfirmPassphrase); + await user.type(screen.getByLabelText(/^confirm/i), testConfirmPassphrase); expect(screen.getByText("Passwords does not match.")).toBeInTheDocument(); // fix confirm passphrase - user.clear(screen.getByLabelText(/^confirm/i)); - user.type(screen.getByLabelText(/^confirm/i), testNewPassphrase); + await user.clear(screen.getByLabelText(/^confirm/i)); + await user.type(screen.getByLabelText(/^confirm/i), testNewPassphrase); expect( screen.queryByText("Passwords does not match.") ).not.toBeInTheDocument(); expect(continueButton.disabled).toBe(false); // clear confirm and new passphrases. should get an error message - user.clear(screen.getByLabelText(/^confirm/i)); - user.clear(screen.getByLabelText(/^new private passphrase/i)); + await user.clear(screen.getByLabelText(/^confirm/i)); + await user.clear(screen.getByLabelText(/^new private passphrase/i)); expect(screen.getByText("Fill all fields.")).toBeInTheDocument(); expect(continueButton.disabled).toBe(true); //refill inputs - user.type( + await user.type( screen.getByLabelText(/^new private passphrase/i), testNewPassphrase ); - user.type(screen.getByLabelText(/^confirm/i), testNewPassphrase); + await user.type(screen.getByLabelText(/^confirm/i), testNewPassphrase); expect(screen.queryByText("Fill all fields.")).not.toBeInTheDocument(); expect(continueButton.disabled).toBe(false); expect( screen.queryByLabelText(/^DEX App Passsword/i) ).not.toBeInTheDocument(); - user.click(screen.getByText("Continue")); + await user.click(screen.getByText("Continue")); expect(mockChangePassphrase).toHaveBeenCalledWith( testPassphrase, testNewPassphrase, @@ -694,7 +716,8 @@ test("test update private passphrase", () => { ); }); -test("test update private passphrase, DEX is active", () => { +test("test update private passphrase, DEX is active", async () => { + const user = userEvent.setup(); render(, { initialState: { settings: testSettings, @@ -706,26 +729,26 @@ test("test update private passphrase, DEX is active", () => { } } }); - user.click(screen.getByText("Privacy and Security")); + await user.click(screen.getByText("Privacy and Security")); const updateButton = screen.getByRole("button", { name: "Update Private Passphrase" }); const modalHeaderText = "Change your passphrase"; // click and cancel - user.click(updateButton); + await user.click(updateButton); expect(screen.getByText(modalHeaderText)).toBeInTheDocument(); - user.click(screen.getByText("Cancel")); + await user.click(screen.getByText("Cancel")); expect(screen.queryByText(modalHeaderText)).not.toBeInTheDocument(); - user.click(updateButton); + await user.click(updateButton); expect(screen.getByText(modalHeaderText)).toBeInTheDocument(); const continueButton = screen.getByText("Continue"); expect(continueButton.disabled).toBe(true); // test 'This Field is required' error message - testPassphraseInputRequiedErrorMsg("Private Passphrase"); - testPassphraseInputRequiedErrorMsg("New Private Passphrase"); - testPassphraseInputRequiedErrorMsg("Confirm"); + await testPassphraseInputRequiedErrorMsg(user, "Private Passphrase"); + await testPassphraseInputRequiedErrorMsg(user, "New Private Passphrase"); + await testPassphraseInputRequiedErrorMsg(user, "Confirm"); expect(continueButton.disabled).toBe(true); // fill input fields @@ -733,40 +756,46 @@ test("test update private passphrase, DEX is active", () => { const testNewPassphrase = "test-new-passphrase"; const testDEXAppPasspword = "test-dex-app-password"; const testConfirmPassphrase = "test-confirm-passphrase"; - user.type(screen.getByLabelText(/^private passphrase/i), testPassphrase); - user.type( + await user.type( + screen.getByLabelText(/^private passphrase/i), + testPassphrase + ); + await user.type( screen.getByLabelText(/^new private passphrase/i), testNewPassphrase ); expect(continueButton.disabled).toBe(true); - user.type(screen.getByLabelText(/^confirm/i), testConfirmPassphrase); + await user.type(screen.getByLabelText(/^confirm/i), testConfirmPassphrase); expect(screen.getByText("Passwords does not match.")).toBeInTheDocument(); // fix confirm passphrase - user.clear(screen.getByLabelText(/^confirm/i)); - user.type(screen.getByLabelText(/^confirm/i), testNewPassphrase); + await user.clear(screen.getByLabelText(/^confirm/i)); + await user.type(screen.getByLabelText(/^confirm/i), testNewPassphrase); expect( screen.queryByText("Passwords does not match.") ).not.toBeInTheDocument(); expect(continueButton.disabled).toBe(false); // clear confirm and new passphrases. should get an error message - user.clear(screen.getByLabelText(/^confirm/i)); - user.clear(screen.getByLabelText(/^new private passphrase/i)); + await user.clear(screen.getByLabelText(/^confirm/i)); + await user.clear(screen.getByLabelText(/^new private passphrase/i)); expect(screen.getByText("Fill all fields.")).toBeInTheDocument(); expect(continueButton.disabled).toBe(true); //refill inputs - user.type( + await user.type( screen.getByLabelText(/^new private passphrase/i), testNewPassphrase ); - user.type(screen.getByLabelText(/^confirm/i), testNewPassphrase); + await user.type(screen.getByLabelText(/^confirm/i), testNewPassphrase); expect(screen.queryByText("Fill all fields.")).not.toBeInTheDocument(); expect(continueButton.disabled).toBe(false); - user.type(screen.getByLabelText(/^DEX App Passsword/i), testDEXAppPasspword); - user.click(screen.getByText("Continue")); + await user.type( + screen.getByLabelText(/^DEX App Passsword/i), + testDEXAppPasspword + ); + await user.click(screen.getByText("Continue")); expect(mockChangePassphrase).toHaveBeenCalledWith( testPassphrase, testNewPassphrase, @@ -775,7 +804,8 @@ test("test update private passphrase, DEX is active", () => { ); }); -test("test update private passphrase, DEX is active, but dex account is null", () => { +test("test update private passphrase, DEX is active, but dex account is null", async () => { + const user = userEvent.setup(); render(, { initialState: { settings: testSettings, @@ -784,17 +814,18 @@ test("test update private passphrase, DEX is active, but dex account is null", ( } } }); - user.click(screen.getByText("Privacy and Security")); + await user.click(screen.getByText("Privacy and Security")); const updateButton = screen.getByRole("button", { name: "Update Private Passphrase" }); - user.click(updateButton); + await user.click(updateButton); expect( screen.queryByLabelText(/^DEX App Passsword/i) ).not.toBeInTheDocument(); }); -test("test update private passphrase, DEX is active, but dex account is empty string", () => { +test("test update private passphrase, DEX is active, but dex account is empty string", async () => { + const user = userEvent.setup(); render(, { initialState: { settings: testSettings, @@ -806,17 +837,18 @@ test("test update private passphrase, DEX is active, but dex account is empty st } } }); - user.click(screen.getByText("Privacy and Security")); + await user.click(screen.getByText("Privacy and Security")); const updateButton = screen.getByRole("button", { name: "Update Private Passphrase" }); - user.click(updateButton); + await user.click(updateButton); expect( screen.queryByLabelText(/^DEX App Passsword/i) ).not.toBeInTheDocument(); }); -test("update private passphrase is disabled", () => { +test("update private passphrase is disabled", async () => { + const user = userEvent.setup(); mockIsChangePassPhraseDisabled = selectors.isChangePassPhraseDisabled = jest.fn(() => true); render(, { @@ -824,9 +856,11 @@ test("update private passphrase is disabled", () => { settings: testSettings } }); - user.click(screen.getByText("Privacy and Security")); + await user.click(screen.getByText("Privacy and Security")); expect(mockIsChangePassPhraseDisabled).toHaveBeenCalled(); - user.click(screen.getByRole("button", { name: "Update Private Passphrase" })); + await user.click( + screen.getByRole("button", { name: "Update Private Passphrase" }) + ); expect(screen.queryByText("Change your passphrase")).not.toBeInTheDocument(); }); @@ -847,6 +881,7 @@ test("renders settings with trezor is NOT enabled", () => { }); test("test proxy settings", async () => { + const user = userEvent.setup(); render(, { initialState: { settings: testSettings } }); // set proxy type @@ -860,25 +895,25 @@ test("test proxy settings", async () => { }) ).not.toBeInTheDocument(); expect(inputValueSpan.textContent).toMatch(oldValue); - const changeFn = () => { - user.click(inputControl); - user.click(getOptionByNameAndType(option, "option")); + const changeFn = async () => { + await user.click(inputControl); + await user.click(getOptionByNameAndType(option, "option")); }; - changeFn(); + await changeFn(); // set proxy location const proxyLocationInputControl = screen.getByLabelText("Proxy Location"); expect(proxyLocationInputControl.value).toMatch(testDefaultProxyLocation); - user.clear(proxyLocationInputControl); - user.type(proxyLocationInputControl, testProxyLocation); + await user.clear(proxyLocationInputControl); + await user.type(proxyLocationInputControl, testProxyLocation); // press enter fireEvent.keyDown(proxyLocationInputControl, { key: "enter", keyCode: 13 }); - user.click(screen.getByRole("button", { name: "Save proxy settings" })); - await wait(() => screen.getByText("Reset required")); - user.click(screen.getByRole("button", { name: "Confirm" })); + await user.click(screen.getByRole("button", { name: "Save proxy settings" })); + await waitFor(() => screen.getByText("Reset required")); + await user.click(screen.getByRole("button", { name: "Confirm" })); - await wait(() => + await waitFor(() => expect(mockSaveSettings).toHaveBeenCalledWith({ ...testCurrentSettings, ...{ proxyType: PROXYTYPE_PAC, proxyLocation: testProxyLocation } diff --git a/test/unit/components/views/TicketsPage/MyTicketsTab/MyTicketsTab.spec.js b/test/unit/components/views/TicketsPage/MyTicketsTab/MyTicketsTab.spec.js index 580558bec5..c2e63abda1 100644 --- a/test/unit/components/views/TicketsPage/MyTicketsTab/MyTicketsTab.spec.js +++ b/test/unit/components/views/TicketsPage/MyTicketsTab/MyTicketsTab.spec.js @@ -1,7 +1,6 @@ import TicketsPage from "components/views/TicketsPage/"; import { render } from "test-utils.js"; -import { screen, wait } from "@testing-library/react"; -import user from "@testing-library/user-event"; +import { screen, waitFor } from "@testing-library/react"; export const GETNEXTADDRESS_SUCCESS = "GETNEXTADDRESS_SUCCESS"; import * as sel from "selectors"; import * as ta from "actions/TransactionActions"; @@ -136,7 +135,8 @@ const incAllTestTxs = (mockGetTransactionsResponse, ts) => { return mockGetTransactionsResponse; }; -const viewAllTxs = ( +const viewAllTxs = async ( + user, mockGetTransactionsResponse, chunkCount, blockHeightIndexes @@ -148,7 +148,7 @@ const viewAllTxs = ( chunkCount ); while (queryLoadingMoreLabel()) { - user.click(getLoadingMoreLabel()); + await user.click(getLoadingMoreLabel()); mockGetTransactionsResponse = incAllTestTxs(mockGetTransactionsResponse); blockHeightIndexes.currentBlockHeight = blockHeightIndexes.currentBlockHeight - step; @@ -205,21 +205,26 @@ test("test tickets list", async () => { mockGetTransactionsResponse, 1587545280 ); - render(, { + const { user } = render(, { initialState: cloneDeep(initialState), currentSettings: { network: "testnet" } }); - user.click(screen.getByText("Ticket History")); + await user.click(screen.getByText("Ticket History")); - user.click(screen.getByText("Cancel listing tickets").nextSibling); + await user.click(screen.getByText("Cancel listing tickets").nextSibling); expect(mockToggleGetTransactions).toHaveBeenCalledTimes(1); // Scroll down to the bottom. // The data should be fetched by getTransactions request expect(getHistoryPageContent().childElementCount).toBe(0); - viewAllTxs(mockGetTransactionsResponse, chunkCount, blockHeightIndexes); + await viewAllTxs( + user, + mockGetTransactionsResponse, + chunkCount, + blockHeightIndexes + ); expect(getHistoryPageContent().childElementCount).toBe( Object.keys(allTestTxs).length ); @@ -236,17 +241,22 @@ test("test tickets list", async () => { const txTypeFilterButton = screen.getAllByRole("button", { name: "EyeFilterMenu" })[1]; - user.click(txTypeFilterButton); - user.click(getTxTypeFilterMenuItem("live")); + await user.click(txTypeFilterButton); + await user.click(getTxTypeFilterMenuItem("live")); jest.advanceTimersByTime(101); - await wait(() => + await waitFor(() => expect(getHistoryPageContent().childElementCount).not.toBe( Object.keys(allTestTxs).length ) ); - viewAllTxs(mockGetTransactionsResponse, chunkCount, blockHeightIndexes); + await viewAllTxs( + user, + mockGetTransactionsResponse, + chunkCount, + blockHeightIndexes + ); let expectedVisibleItems = countTxsByType(allTestTxs, ["live"]); expect(screen.getAllByText("Live").length).toBe(expectedVisibleItems); @@ -256,17 +266,22 @@ test("test tickets list", async () => { // show just unmined blockHeightIndexes.currentBlockHeight = 1000; - user.click(txTypeFilterButton); - user.click(getTxTypeFilterMenuItem("unmined")); + await user.click(txTypeFilterButton); + await user.click(getTxTypeFilterMenuItem("unmined")); jest.advanceTimersByTime(101); - await wait(() => + await waitFor(() => expect(getHistoryPageContent().childElementCount).not.toBe( Object.keys(allTestTxs).length ) ); - viewAllTxs(mockGetTransactionsResponse, chunkCount, blockHeightIndexes); + await viewAllTxs( + user, + mockGetTransactionsResponse, + chunkCount, + blockHeightIndexes + ); expectedVisibleItems = countTxsByType(allTestTxs, ["unmined"]); expect(screen.getAllByText("Unmined").length).toBe(expectedVisibleItems); @@ -276,17 +291,22 @@ test("test tickets list", async () => { // show all tickets again blockHeightIndexes.currentBlockHeight = 1000; - user.click(txTypeFilterButton); - user.click(getTxTypeFilterMenuItem("All")); + await user.click(txTypeFilterButton); + await user.click(getTxTypeFilterMenuItem("All")); jest.advanceTimersByTime(101); - await wait(() => + await waitFor(() => expect(getHistoryPageContent().childElementCount).not.toBe( Object.keys(allTestTxs).length ) ); - viewAllTxs(mockGetTransactionsResponse, chunkCount, blockHeightIndexes); + await viewAllTxs( + user, + mockGetTransactionsResponse, + chunkCount, + blockHeightIndexes + ); expect(mockGetTransactions).toHaveBeenCalledTimes(0); expect(getHistoryPageContent().childElementCount).toBe( Object.keys(allTestTxs).length @@ -295,12 +315,14 @@ test("test tickets list", async () => { // filter by hash id blockHeightIndexes.currentBlockHeight = 1000; - user.type( + await user.type( screen.getByPlaceholderText("Filter by Hash"), Object.keys(allTestTxs)[3] ); jest.advanceTimersByTime(101); - await wait(() => expect(getHistoryPageContent().childElementCount).toBe(1)); + await waitFor(() => + expect(getHistoryPageContent().childElementCount).toBe(1) + ); }); test("test ticket sorting", async () => { @@ -309,22 +331,22 @@ test("test ticket sorting", async () => { const mockChangeTransactionsFilter = (transactionActions.changeTicketsFilter = jest.fn(() => () => {})); - render(, { + const { user } = render(, { initialState: cloneDeep(initialState), currentSettings: { network: "testnet" } }); - user.click(screen.getByText("Ticket History")); + await user.click(screen.getByText("Ticket History")); // change sorting, show the oldest first const txSortButton = screen.getAllByRole("button", { name: "EyeFilterMenu" })[0]; - user.click(txSortButton); - user.click(screen.getByText("Oldest")); + await user.click(txSortButton); + await user.click(screen.getByText("Oldest")); jest.advanceTimersByTime(101); - await wait(() => + await waitFor(() => expect(mockChangeTransactionsFilter).toHaveBeenCalledWith({ ...initialState.grpc.ticketsFilter, listDirection: "asc" @@ -332,10 +354,10 @@ test("test ticket sorting", async () => { ); // change back sorting, show the newest first - user.click(txSortButton); - user.click(screen.getByText("Newest")); + await user.click(txSortButton); + await user.click(screen.getByText("Newest")); jest.advanceTimersByTime(101); - await wait(() => + await waitFor(() => expect(mockChangeTransactionsFilter).toHaveBeenCalledWith({ ...initialState.grpc.ticketsFilter, listDirection: "desc" @@ -343,7 +365,7 @@ test("test ticket sorting", async () => { ); }); -test("test no tickets", () => { +test("test no tickets", async () => { const mockGetTransactionsResponse = { type: transactionActions.GETTRANSACTIONS_COMPLETE, getStakeTxsAux: { noMoreTransactions: true }, @@ -360,13 +382,13 @@ test("test no tickets", () => { ...cloneDeep(initialState) }; initialStateMod.grpc.getStakeTxsAux.noMoreTransactions = true; - render(, { + const { user } = render(, { initialState: initialStateMod, currentSettings: { network: "testnet" } }); - user.click(screen.getByText("Ticket History")); - user.click(getLoadingMoreLabel()); + await user.click(screen.getByText("Ticket History")); + await user.click(getLoadingMoreLabel()); expect(screen.getByText("No Tickets Found")); }); diff --git a/test/unit/components/views/TicketsPage/MyVSPTickets/MyVSPTicketsTab.spec.js b/test/unit/components/views/TicketsPage/MyVSPTickets/MyVSPTicketsTab.spec.js index 5f214c3113..d462dd85ed 100644 --- a/test/unit/components/views/TicketsPage/MyVSPTickets/MyVSPTicketsTab.spec.js +++ b/test/unit/components/views/TicketsPage/MyVSPTickets/MyVSPTicketsTab.spec.js @@ -1,7 +1,6 @@ import TicketsPage from "components/views/TicketsPage/"; import { render } from "test-utils.js"; -import { screen, wait } from "@testing-library/react"; -import user from "@testing-library/user-event"; +import { screen, waitFor } from "@testing-library/react"; export const GETNEXTADDRESS_SUCCESS = "GETNEXTADDRESS_SUCCESS"; import * as sel from "selectors"; import * as ta from "actions/TransactionActions"; @@ -188,10 +187,10 @@ const incAllTestTxs = (mockGetTransactionsResponse, ts) => { return mockGetTransactionsResponse; }; -const viewAllTxs = (mockGetTransactionsResponse, chunkCount) => { +const viewAllTxs = async (user, mockGetTransactionsResponse, chunkCount) => { let i = 1; while (queryLoadingMoreLabel()) { - user.click(getLoadingMoreLabel()); + await user.click(getLoadingMoreLabel()); mockGetTransactionsResponse = incAllTestTxs(mockGetTransactionsResponse); if (i++ == chunkCount) { mockGetTransactionsResponse.noMoreLiveTickets = true; @@ -288,20 +287,22 @@ test("test vsp ticket status list", async () => { mockGetTransactionsResponse, 1587545280 ); - render(, { + const { user } = render(, { initialState: cloneDeep(initialState), currentSettings: { network: "testnet" } }); - user.click(screen.getByText("Ticket Status")); + await user.click(screen.getByText("Ticket Status")); // Scroll down to the bottom. // The data should be fetched by getTransactions request expect(getHistoryPageContent().childElementCount).toBe(0); - viewAllTxs(mockGetTransactionsResponse, chunkCount); + await viewAllTxs(user, mockGetTransactionsResponse, chunkCount); - await wait(() => expect(getHistoryPageContent().childElementCount).toBe(17)); + await waitFor(() => + expect(getHistoryPageContent().childElementCount).toBe(17) + ); expect(mockGetTransactions).toHaveBeenCalledTimes( Object.keys(allTestTxs).length / Object.keys(mockStakeTickets).length ); @@ -313,11 +314,13 @@ test("test vsp ticket status list", async () => { const txTypeFilterButton = screen.getByRole("button", { name: "EyeFilterMenu" }); - user.click(txTypeFilterButton); - user.click(getTxTypeFilterMenuItem("Unpaid Fee")); + await user.click(txTypeFilterButton); + await user.click(getTxTypeFilterMenuItem("Unpaid Fee")); jest.advanceTimersByTime(101); - await wait(() => expect(getHistoryPageContent().childElementCount).toBe(3)); + await waitFor(() => + expect(getHistoryPageContent().childElementCount).toBe(3) + ); expect(screen.getAllByText("Processing").length).toBe(3); expect(mockGetTransactions).toHaveBeenCalledTimes(0); @@ -325,11 +328,13 @@ test("test vsp ticket status list", async () => { // show just paid fee ticket mockGetTransactions.mockClear(); - user.click(txTypeFilterButton); - user.click(getTxTypeFilterMenuItem("Paid Fee")); + await user.click(txTypeFilterButton); + await user.click(getTxTypeFilterMenuItem("Paid Fee")); jest.advanceTimersByTime(101); - await wait(() => expect(getHistoryPageContent().childElementCount).toBe(3)); + await waitFor(() => + expect(getHistoryPageContent().childElementCount).toBe(3) + ); expect(screen.getAllByText("Paid").length).toBe(3); expect(mockGetTransactions).toHaveBeenCalledTimes(0); @@ -337,11 +342,13 @@ test("test vsp ticket status list", async () => { // show just paid fee ticket mockGetTransactions.mockClear(); - user.click(txTypeFilterButton); - user.click(getTxTypeFilterMenuItem("Fee Error")); + await user.click(txTypeFilterButton); + await user.click(getTxTypeFilterMenuItem("Fee Error")); jest.advanceTimersByTime(101); - await wait(() => expect(getHistoryPageContent().childElementCount).toBe(3)); + await waitFor(() => + expect(getHistoryPageContent().childElementCount).toBe(3) + ); expect(screen.getAllByText("Error").length).toBe(3); expect(mockGetTransactions).toHaveBeenCalledTimes(0); @@ -349,11 +356,11 @@ test("test vsp ticket status list", async () => { // show just confirmed fee ticket mockGetTransactions.mockClear(); - user.click(txTypeFilterButton); - user.click(getTxTypeFilterMenuItem("Confirmed Fee")); + await user.click(txTypeFilterButton); + await user.click(getTxTypeFilterMenuItem("Confirmed Fee")); jest.advanceTimersByTime(101); - await wait(() => + await waitFor(() => expect(getHistoryPageContent().childElementCount).not.toBe(3) ); @@ -365,22 +372,22 @@ test("test vsp ticket status list", async () => { const syncFaildVSPTicketsButton = screen.getByRole("button", { name: "Sync Failed VSP Tickets" }); - user.click(syncFaildVSPTicketsButton); + await user.click(syncFaildVSPTicketsButton); // cancel first - user.click(screen.getByRole("button", { name: "Cancel" })); + await user.click(screen.getByRole("button", { name: "Cancel" })); // continue now - user.click(syncFaildVSPTicketsButton); + await user.click(syncFaildVSPTicketsButton); expect(screen.getByText("Confirmation Required")).toBeInTheDocument(); - user.type(screen.getByLabelText("Private Passphrase"), testPassphrase); - user.click(screen.getByText("Select VSP...")); - user.click(screen.getByText(mockAvailableVsps[1].host)); - await wait(() => + await user.type(screen.getByLabelText("Private Passphrase"), testPassphrase); + await user.click(screen.getByText("Select VSP...")); + await user.click(screen.getByText(mockAvailableVsps[1].host)); + await waitFor(() => expect(screen.queryByText("Loading")).not.toBeInTheDocument() ); - user.click(screen.getByRole("button", { name: "Continue" })); + await user.click(screen.getByRole("button", { name: "Continue" })); expect(mockSyncVSPTicketsRequest).toHaveBeenCalledWith({ account: mockMixedAccountValue, passphrase: testPassphrase, @@ -389,7 +396,7 @@ test("test vsp ticket status list", async () => { }); }); -test("test no tickets", () => { +test("test no tickets", async () => { const mockGetTransactionsResponse = { type: transactionActions.GETTRANSACTIONS_COMPLETE, getStakeTxsAux: { noMoreTransactions: true }, @@ -406,12 +413,12 @@ test("test no tickets", () => { ...cloneDeep(initialState) }; initialStateMod.grpc.getStakeTxsAux.noMoreTransactions = true; - render(, { + const { user } = render(, { initialState: initialStateMod, currentSettings: { network: "testnet" } }); - user.click(screen.getByText("Ticket Status")); + await user.click(screen.getByText("Ticket Status")); expect(screen.getByText("No Tickets Found")); }); diff --git a/test/unit/components/views/TicketsPage/PurchaseTab/PurchasePage.spec.js b/test/unit/components/views/TicketsPage/PurchaseTab/PurchasePage.spec.js index fb01c88b21..b9766111bf 100644 --- a/test/unit/components/views/TicketsPage/PurchaseTab/PurchasePage.spec.js +++ b/test/unit/components/views/TicketsPage/PurchaseTab/PurchasePage.spec.js @@ -1,8 +1,8 @@ import Purchase from "components/views/TicketsPage/PurchaseTab/PurchaseTab.jsx"; import TicketAutoBuyer from "components/views/TicketsPage/PurchaseTab/TicketAutoBuyer/"; import { render } from "test-utils.js"; -import user from "@testing-library/user-event"; -import { fireEvent, screen, wait } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { fireEvent, screen, waitFor } from "@testing-library/react"; import * as sel from "selectors"; import * as ca from "actions/ControlActions"; import * as wal from "wallet"; @@ -109,43 +109,47 @@ beforeEach(() => { }); test("render PurchasePage", async () => { + const user = userEvent.setup(); render(, initialState); // check PrivacyInfo - user.click(screen.getByText("You are purchasing mixed tickets")); + await user.click(screen.getByText("You are purchasing mixed tickets")); expect( screen.getByText(/Purchasing mixed tickets can take some time/i) ).toBeInTheDocument(); // set stakepool - user.click(screen.getByText("Select VSP...")); - user.click(screen.getByText(mockAvailableVsps[1].host)); - await wait(() => - expect(screen.queryByText("Loading")).not.toBeInTheDocument() - ); + await user.click(screen.getByText("Select VSP...")); + await user.click(screen.getByText(mockAvailableVsps[1].host)); + await waitFor(() => { + expect(screen.queryByText("Loading")).not.toBeInTheDocument(); + expect(screen.getByLabelText("Always use this VSP")).toBeInTheDocument(); + }); + await user.click(screen.getByLabelText("Always use this VSP")); // check Always use this VSP checkbox - user.click(screen.getByLabelText("Always use this VSP")); expect(mockSetRememberedVspHost).toHaveBeenCalledWith({ host: mockAvailableVsps[1].host, pubkey: mockVspInfo.data.pubkey }); - // check summary - expect(screen.getByText("VSP Fee:").nextElementSibling.textContent).toMatch( - `${mockVspInfo.data.feepercentage} %` - ); - expect(screen.getByText(/Total:/).textContent).toMatch( - `${mockTicketPrice / 100000000} ${DCR}` - ); - expect(screen.getByText(/Remaining:/).textContent).toMatch( - `${(mockMixedAccount.spendable - mockTicketPrice) / 100000000} ${DCR}` - ); + await waitFor(() => { + // check summary + expect(screen.getByText("VSP Fee:").nextElementSibling.textContent).toMatch( + `${mockVspInfo.data.feepercentage} %` + ); + expect(screen.getByText(/Total:/).textContent).toMatch( + `${mockTicketPrice / 100000000} ${DCR}` + ); + expect(screen.getByText(/Remaining:/).textContent).toMatch( + `${(mockMixedAccount.spendable - mockTicketPrice) / 100000000} ${DCR}` + ); + }); const purchaseButton = screen.getByText("Purchase"); - user.click(purchaseButton); - user.type(screen.getByLabelText("Private Passphrase"), mockPassphrase); - user.click(screen.getByText("Continue")); + await user.click(purchaseButton); + await user.type(screen.getByLabelText("Private Passphrase"), mockPassphrase); + await user.click(screen.getByText("Continue")); expect(mockPurchaseTicketsAttempt).toHaveBeenCalledWith( mockPassphrase, mockMixedAccount, @@ -160,14 +164,14 @@ test("render PurchasePage", async () => { const inputTag = screen.getByLabelText("Amount"); const moreButton = screen.getByRole("button", { name: "more" }); - user.click(moreButton); + await user.click(moreButton); expect(inputTag.value).toBe("2"); const lessButton = screen.getByRole("button", { name: "less" }); - user.click(lessButton); + await user.click(lessButton); expect(inputTag.value).toBe("1"); // remain 1 - user.click(lessButton); + await user.click(lessButton); expect(inputTag.value).toBe("1"); /* test arrow key */ @@ -176,17 +180,17 @@ test("render PurchasePage", async () => { fireEvent.keyDown(inputTag, { keyCode: 40 }); expect(inputTag.value).toBe("1"); - user.clear(inputTag); + await user.clear(inputTag); expect(inputTag.value).toBe(""); // "" => 1 - user.click(moreButton); + await user.click(moreButton); expect(inputTag.value).toBe("1"); // not enough funds mockPurchaseTicketsAttempt.mockReset(); - user.type(inputTag, "100000000"); - user.click(purchaseButton); + await user.type(inputTag, "100000000"); + await user.click(purchaseButton); expect(mockPurchaseTicketsAttempt).not.toHaveBeenCalled(); }); @@ -202,65 +206,66 @@ const getSettingsModalTitle = () => const getFillAllFieldsErrorMsg = () => screen.getByText("Fill all fields."); test("test autobuyer", async () => { + const user = userEvent.setup(); render(, initialState); const settingsButton = getSettingsButton(); const toggleSwitch = getToggleSwitch(); - user.click(toggleSwitch); - await wait(() => getSettingsModalTitle()); + await user.click(toggleSwitch); + await waitFor(() => getSettingsModalTitle()); - user.click(getSaveButton()); - await wait(() => screen.getByText("Fill all fields.")); + await user.click(getSaveButton()); + await waitFor(() => screen.getByText("Fill all fields.")); const mockBalanceToMaintain = 14; - user.type( + await user.type( screen.getByLabelText(/Balance to Maintain/i), `${mockBalanceToMaintain}` ); - user.click(getSaveButton()); + await user.click(getSaveButton()); expect(getFillAllFieldsErrorMsg()).toBeInTheDocument(); // set account - user.click(screen.getByText("Select account")); - user.click(screen.getByText(mockMixedAccount.name)); + await user.click(screen.getByText("Select account")); + await user.click(screen.getByText(mockMixedAccount.name)); selectors.buyerAccount = jest.fn(() => mockMixedAccount); - user.click(getSaveButton()); + await user.click(getSaveButton()); expect(screen.getByText("Fill all fields.")).toBeInTheDocument(); const validMaxFeeInput = "10"; - user.type(getMaxFeeInput(), validMaxFeeInput); - user.click(getSaveButton()); - await wait(() => + await user.type(getMaxFeeInput(), validMaxFeeInput); + await user.click(getSaveButton()); + await waitFor(() => expect( screen.queryByText("Automatic ticket purchases") ).not.toBeInTheDocument() ); // check settings - user.click(settingsButton); + await user.click(settingsButton); expect(screen.getByLabelText(/Balance to Maintain/i).value).toBe( `${mockBalanceToMaintain}` ); expect(screen.getByText(mockMixedAccount.name)).toBeInTheDocument(); expect(getMaxFeeInput().value).toBe(validMaxFeeInput); - user.click(screen.getByRole("button", { name: "Cancel" })); - await wait(() => + await user.click(screen.getByRole("button", { name: "Cancel" })); + await waitFor(() => expect( screen.queryByText("Automatic ticket purchases") ).not.toBeInTheDocument() ); // clicking again on switch should open the confirmation modal - user.click(toggleSwitch); - await wait(() => screen.getByText(/start ticket buyer confirmation/i)); + await user.click(toggleSwitch); + await waitFor(() => screen.getByText(/start ticket buyer confirmation/i)); expect(screen.getByText(mockAvailableVsps[1].host)).toBeInTheDocument(); expect(screen.getByText(`${mockBalanceToMaintain}.00`)).toBeInTheDocument(); // cancel first - user.click(screen.getByText("Cancel")); + await user.click(screen.getByText("Cancel")); // try again - user.click(getToggleSwitch()); - await wait(() => screen.getByText(/start ticket buyer confirmation/i)); - user.type(screen.getByLabelText("Private Passphrase"), mockPassphrase); - user.click(screen.getByText("Continue")); + await user.click(getToggleSwitch()); + await waitFor(() => screen.getByText(/start ticket buyer confirmation/i)); + await user.type(screen.getByLabelText("Private Passphrase"), mockPassphrase); + await user.click(screen.getByText("Continue")); expect(mockStartTicketBuyerAttempt).toHaveBeenCalledWith( mockPassphrase, mockMixedAccount, @@ -273,25 +278,27 @@ test("test autobuyer", async () => { ); }); -test("test autobuyer (autobuyer is runnning)", () => { +test("test autobuyer (autobuyer is runnning)", async () => { + const user = userEvent.setup(); mockGetTicketAutoBuyerRunning = selectors.getTicketAutoBuyerRunning = jest.fn( () => true ); render(, initialState); expect(mockGetTicketAutoBuyerRunning).toHaveBeenCalled(); expect(screen.getByText(/turn off auto buyer/i)).toBeInTheDocument(); - user.click(getToggleSwitch()); + await user.click(getToggleSwitch()); expect(mockTicketBuyerCancel).toHaveBeenCalled(); }); -test("test when VSP listing is not enabled ", () => { +test("test when VSP listing is not enabled ", async () => { + const user = userEvent.setup(); render(); expect( screen.getByText( /VSP listing from external API endpoint is currently disabled/i ) ).toBeInTheDocument(); - user.click(screen.getByRole("button", { name: "Enable VSP Listing" })); + await user.click(screen.getByRole("button", { name: "Enable VSP Listing" })); expect(mockAddAllowedExternalRequest).toHaveBeenCalledWith( EXTERNALREQUEST_STAKEPOOL_LISTING ); @@ -309,6 +316,7 @@ test("test `end of a ticket interval` state", () => { }); test("remembered vsp have been set", async () => { + const user = userEvent.setup(); const testRememberedVSP = { host: mockAvailableVsps[1].host, label: mockAvailableVsps[1].label @@ -316,7 +324,7 @@ test("remembered vsp have been set", async () => { selectors.getRememberedVspHost = jest.fn(() => testRememberedVSP); render(, initialState); - await wait(() => + await waitFor(() => expect(screen.queryByText("Loading")).not.toBeInTheDocument() ); expect(screen.getByText(testRememberedVSP.host)).toBeInTheDocument(); @@ -325,7 +333,7 @@ test("remembered vsp have been set", async () => { expect(mockSetRememberedVspHost).not.toHaveBeenCalled(); // uncheck Always use this VSP checkbox - user.click(screen.getByLabelText("Always use this VSP")); + await user.click(screen.getByLabelText("Always use this VSP")); expect(mockSetRememberedVspHost).toHaveBeenCalledWith(null); }); @@ -337,7 +345,7 @@ test("an outdated remembered vsp has been set, should be cleared", async () => { selectors.getRememberedVspHost = jest.fn(() => testRememberedVSP); render(, initialState); - await wait(() => + await waitFor(() => expect(screen.queryByText("Loading")).not.toBeInTheDocument() ); @@ -350,16 +358,17 @@ test("an outdated remembered vsp has been set, should be cleared", async () => { }); test("outdated vsp could not be selected", async () => { + const user = userEvent.setup(); render(, initialState); - await wait(() => + await waitFor(() => expect(screen.queryByText("Loading")).not.toBeInTheDocument() ); - user.click(screen.getByText("Select VSP...")); + await user.click(screen.getByText("Select VSP...")); expect(screen.getByText("Out of date")).toBeInTheDocument(); // there is (only) one out of date tooltip - user.click(screen.getByText(mockAvailableVsps[2].host)); + await user.click(screen.getByText(mockAvailableVsps[2].host)); // click on outdated vsp is not allowed: expect(screen.getByText("Select VSP...")).toBeInTheDocument(); }); diff --git a/test/unit/components/views/TicketsPage/PurchaseTab/StakeInfo.spec.js b/test/unit/components/views/TicketsPage/PurchaseTab/StakeInfo.spec.js index 49eaddcfb5..55597be82c 100644 --- a/test/unit/components/views/TicketsPage/PurchaseTab/StakeInfo.spec.js +++ b/test/unit/components/views/TicketsPage/PurchaseTab/StakeInfo.spec.js @@ -1,6 +1,5 @@ import StakeInfo from "components/views/TicketsPage/PurchaseTab/StakeInfo/StakeInfo"; import { render } from "test-utils.js"; -import user from "@testing-library/user-event"; import { screen } from "@testing-library/react"; import * as sel from "selectors"; import { DCR, UNIT_DIVISOR } from "constants"; @@ -29,8 +28,8 @@ selectors.isSPV = jest.fn(() => mockIsSPV); selectors.lastVotedTicket = jest.fn(() => mockLastVotedTicket); selectors.currencyDisplay = jest.fn(() => mockCurrencyDisplay); -test("test StakeInfo (SPV enabled)", () => { - render(); +test("test StakeInfo (SPV enabled)", async () => { + const { user } = render(); expect(screen.getByText(/staking overview/i)).toBeInTheDocument(); const unspentTickets = screen.getByText(/unspent tickets/i); @@ -50,7 +49,7 @@ test("test StakeInfo (SPV enabled)", () => { `${(mockTotalSubsidy / UNIT_DIVISOR).toFixed(2)} ${mockCurrencyDisplay}` ); - user.click(totalRewardEarned); + await user.click(totalRewardEarned); expect( screen.getByText(/own mempool tickets/i).nextElementSibling.textContent diff --git a/test/unit/components/views/TicketsPage/PurchaseTab/TicketsPage.spec.js b/test/unit/components/views/TicketsPage/PurchaseTab/TicketsPage.spec.js index cb8a76fe6f..c1bdd21745 100644 --- a/test/unit/components/views/TicketsPage/PurchaseTab/TicketsPage.spec.js +++ b/test/unit/components/views/TicketsPage/PurchaseTab/TicketsPage.spec.js @@ -1,7 +1,7 @@ import TicketsPage from "components/views/TicketsPage/TicketsPage"; import { render } from "test-utils.js"; import user from "@testing-library/user-event"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; import * as sel from "selectors"; import { wallet } from "wallet-preload-shim"; import { DCR } from "constants"; @@ -117,7 +117,7 @@ test("render TicketsPage - show staking warning", async () => { tabShouldBeUnchecked(tab3); // clicking on previousButton in vain - user.click(previousButton); + await user.click(previousButton); tabShouldBeActive(tab1); tabShouldBeUnchecked(tab1); tabShouldBeInactive(tab2); @@ -126,7 +126,7 @@ test("render TicketsPage - show staking warning", async () => { tabShouldBeUnchecked(tab3); // clicking on previousArrowButton in vain - user.click(previousArrowButton); + await user.click(previousArrowButton); tabShouldBeActive(tab1); tabShouldBeUnchecked(tab1); tabShouldBeInactive(tab2); @@ -135,7 +135,7 @@ test("render TicketsPage - show staking warning", async () => { tabShouldBeUnchecked(tab3); // move on to the second tab - user.click(nextButton); + await user.click(nextButton); tabShouldBeInactive(tab1); tabShouldBeChecked(tab1); tabShouldBeActive(tab2); @@ -147,7 +147,7 @@ test("render TicketsPage - show staking warning", async () => { expect(understandButton.disabled).toBe(true); // move on to the third tab click on the next arrow - user.click(nextArrowButton); + await user.click(nextArrowButton); tabShouldBeInactive(tab1); tabShouldBeChecked(tab1); tabShouldBeInactive(tab2); @@ -161,7 +161,7 @@ test("render TicketsPage - show staking warning", async () => { expect(nextButton.className).toMatch("disabled"); expect(nextArrowButton.className).toMatch("disabled"); // clicking on nextButton in vain - user.click(nextButton); + await user.click(nextButton); tabShouldBeInactive(tab1); tabShouldBeChecked(tab1); tabShouldBeInactive(tab2); @@ -175,7 +175,7 @@ test("render TicketsPage - show staking warning", async () => { expect(nextArrowButton.className).toMatch("disabled"); // clicking on nextArrowButton in vain - user.click(nextButton); + await user.click(nextButton); tabShouldBeInactive(tab1); tabShouldBeChecked(tab1); tabShouldBeInactive(tab2); @@ -189,13 +189,13 @@ test("render TicketsPage - show staking warning", async () => { expect(nextArrowButton.className).toMatch("disabled"); expect(screen.getByText(/before you continue/i)).toBeInTheDocument(); - user.click(understandButton); + await user.click(understandButton); expect(mockWalletCfgSet).toHaveBeenCalledWith( cfgConstants.SHOW_STAKING_WARNING, false ); - await wait(() => + await waitFor(() => expect(screen.queryByText(/before you continue/i)).not.toBeInTheDocument() ); expect(screen.getByText(/Staking Overview/i)).toBeInTheDocument(); diff --git a/test/unit/components/views/TransactionPage/TransactionPage.spec.js b/test/unit/components/views/TransactionPage/TransactionPage.spec.js index 745126a9cb..8c37494c28 100644 --- a/test/unit/components/views/TransactionPage/TransactionPage.spec.js +++ b/test/unit/components/views/TransactionPage/TransactionPage.spec.js @@ -1,7 +1,6 @@ import TransactionPage from "components/views/TransactionPage"; import { render } from "test-utils.js"; -import user from "@testing-library/user-event"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; import { DCR, TestNetParams } from "constants"; import * as sel from "selectors"; import * as clia from "actions/ClientActions"; @@ -162,7 +161,7 @@ test("regular sent pending tx from default account to an external address", asyn "263f64a32f2f86ffda747242cfc620b0c42689f5c600ef2be22351f53bcd5b0d"; const rawTx = mockNormalizedRegularTransactions[mockTxHash].rawTx; - render(, { + const { user } = render(, { initialState: { grpc: { regularTransactions: mockNormalizedRegularTransactions, @@ -172,7 +171,7 @@ test("regular sent pending tx from default account to an external address", asyn } }); - await wait(() => getIODetails()); + await waitFor(() => getIODetails()); expect(getHeaderTitleIconClassName()).toMatch("out"); expect(getTitleText()).toMatch("-8.00000 DCR"); @@ -185,10 +184,10 @@ test("regular sent pending tx from default account to an external address", asyn ); expect(getTransactionFeeText()).toMatch("Transaction fee:0.0000253 DCR"); - user.click(getAbandonTransactionButton()); + await user.click(getAbandonTransactionButton()); expect(mockAbandonTransactionAttempt).toHaveBeenCalledWith(mockTxHash); - user.click(getRebroadcastTransaction()); + await user.click(getRebroadcastTransaction()); expect(mockPublishUnminedTransactionsAttempt).toHaveBeenCalled(); expect(getWalletInputsText()).toMatch("Wallet Inputsdefault17.9385434 DCR"); @@ -204,7 +203,7 @@ test("regular sent pending tx from default account to an external address", asyn expect(screen.getByText(rawTx)).toBeInTheDocument(); expect(queryHeight()).not.toBeInTheDocument(); // not mined - user.click(screen.getByText("Back")); + await user.click(screen.getByText("Back")); expect(mockGoBackHistory).toHaveBeenCalled(); }); @@ -214,7 +213,7 @@ test("regular received mined tx to the default account", async () => { const rawTx = mockNormalizedRegularTransactions[mockTxHash].rawTx; selectors.currentBlockHeight = jest.fn(() => 712214); - render(, { + const { user } = render(, { initialState: { grpc: { regularTransactions: mockNormalizedRegularTransactions, @@ -224,7 +223,7 @@ test("regular received mined tx to the default account", async () => { } }); - await wait(() => getIODetails()); + await waitFor(() => getIODetails()); expect(getHeaderTitleIconClassName()).toMatch("in"); expect(getTitleText()).toMatch("100.00000 DCR"); @@ -253,7 +252,7 @@ test("regular received mined tx to the default account", async () => { expect(screen.getByText(rawTx)).toBeInTheDocument(); expect(getHeightText()).toMatch("Height706945"); - user.click(screen.getByText("Back")); + await user.click(screen.getByText("Back")); expect(mockGoBackHistory).toHaveBeenCalled(); }); @@ -263,7 +262,7 @@ test("regular self transfer tx to unmixed account", async () => { const rawTx = mockNormalizedRegularTransactions[mockTxHash].rawTx; selectors.currentBlockHeight = jest.fn(() => 712836); - render(, { + const { user } = render(, { initialState: { grpc: { regularTransactions: mockNormalizedRegularTransactions, @@ -273,7 +272,7 @@ test("regular self transfer tx to unmixed account", async () => { } }); - await wait(() => getIODetails()); + await waitFor(() => getIODetails()); expect(getHeaderTitleIconClassName()).toMatch("self"); expect(getTitleText()).toMatch("-0.0000253 DCR"); @@ -301,7 +300,7 @@ test("regular self transfer tx to unmixed account", async () => { expect(screen.getByText(rawTx)).toBeInTheDocument(); expect(getHeightText()).toMatch("Height712832"); - user.click(screen.getByText("Back")); + await user.click(screen.getByText("Back")); expect(mockGoBackHistory).toHaveBeenCalled(); }); @@ -311,7 +310,7 @@ test("self coins from unmixed to mixed account", async () => { const rawTx = mockNormalizedRegularTransactions[mockTxHash].rawTx; selectors.currentBlockHeight = jest.fn(() => 712883); - render(, { + const { user } = render(, { initialState: { grpc: { regularTransactions: mockNormalizedRegularTransactions, @@ -321,7 +320,7 @@ test("self coins from unmixed to mixed account", async () => { } }); - await wait(() => getIODetails()); + await waitFor(() => getIODetails()); expect(getHeaderTitleIconClassName()).toMatch("mixed"); expect(getTitleText()).toMatch("-0.0000361 DCR"); @@ -351,7 +350,7 @@ test("self coins from unmixed to mixed account", async () => { expect(screen.getByText(rawTx)).toBeInTheDocument(); expect(getHeightText()).toMatch("Height712872"); - user.click(screen.getByText("Back")); + await user.click(screen.getByText("Back")); expect(mockGoBackHistory).toHaveBeenCalled(); }); @@ -364,7 +363,7 @@ test("voted ticket", async () => { mockNormalizedStakeTransactions[mockTxHash]; selectors.currentBlockHeight = jest.fn(() => 924414); - render(, { + const { user } = render(, { initialState: { grpc: { regularTransactions: {}, @@ -385,7 +384,7 @@ test("voted ticket", async () => { } }); - await wait(() => getIODetails()); + await waitFor(() => getIODetails()); expect(getHeaderTitleIconClassName()).toMatch("ticket"); expect(getTitleText()).toMatch("Ticket, Voted"); @@ -415,7 +414,7 @@ test("voted ticket", async () => { expect(screen.getByText(rawTx)).toBeInTheDocument(); expect(getHeightText()).toMatch("Height919842"); - user.click(screen.getByText("Back")); + await user.click(screen.getByText("Back")); expect(mockGoBackHistory).toHaveBeenCalled(); expect(getVSPHostText()).toMatch("VSP host:mockVspHost-votedticket"); @@ -430,7 +429,7 @@ test("vote tx", async () => { mockNormalizedStakeTransactions[mockTxHash]; selectors.currentBlockHeight = jest.fn(() => 934699); - render(, { + const { user } = render(, { initialState: { grpc: { regularTransactions: {}, @@ -451,7 +450,7 @@ test("vote tx", async () => { } }); - await wait(() => getIODetails()); + await waitFor(() => getIODetails()); expect(getHeaderTitleIconClassName()).toMatch("vote"); expect(getTitleText()).toMatch("Vote"); @@ -481,7 +480,7 @@ test("vote tx", async () => { expect(screen.getByText(rawTx)).toBeInTheDocument(); expect(getHeightText()).toMatch("Height932737"); - user.click(screen.getByText("Back")); + await user.click(screen.getByText("Back")); expect(mockGoBackHistory).toHaveBeenCalled(); expect(screen.getByText("Agenda Choices:").parentNode.textContent).toMatch( @@ -529,7 +528,7 @@ test("vote tx (votes don't align with what the wallet currently has set)", async } }); - await wait(() => getIODetails()); + await waitFor(() => getIODetails()); expect(screen.getByText("Agenda Choices:").parentNode.textContent).toMatch( "Agenda Choices:Change maximum treasury expenditure policy as defined in DCP0007reverttreasurypolicyabstainEnable explicit version upgrades as defined in DCP0008explicitverupgradesabstainEnable automatic ticket revocations as defined in DCP0009autorevocationsabstainChange block reward subsidy split to 10/80/10 as defined in DCP0010changesubsidysplitabstainThis doesn't align with what the wallet currently has set (yes)" @@ -544,7 +543,7 @@ test("missed ticket", async () => { mockStakeTransactionMap[mockTxHash] = mockNormalizedStakeTransactions[mockTxHash]; selectors.currentBlockHeight = jest.fn(() => 930680); - render(, { + const { user } = render(, { initialState: { grpc: { regularTransactions: {}, @@ -562,7 +561,7 @@ test("missed ticket", async () => { } }); - await wait(() => getIODetails()); + await waitFor(() => getIODetails()); expect(getHeaderTitleIconClassName()).toMatch("missed"); expect(getTitleText()).toMatch("Missed"); @@ -593,7 +592,7 @@ test("missed ticket", async () => { expect(getHeightText()).toMatch("Height558424"); expect(getVSPHostText()).toMatch("VSP host:mockVspHost-missed"); - user.click(screen.getByText("Back")); + await user.click(screen.getByText("Back")); expect(mockGoBackHistory).toHaveBeenCalled(); }); @@ -606,7 +605,7 @@ test("revocation", async () => { mockNormalizedStakeTransactions[mockTxHash]; selectors.currentBlockHeight = jest.fn(() => 712217); - render(, { + const { user } = render(, { initialState: { grpc: { regularTransactions: {}, @@ -624,7 +623,7 @@ test("revocation", async () => { } }); - await wait(() => getIODetails()); + await waitFor(() => getIODetails()); expect(getHeaderTitleIconClassName()).toMatch("revocation"); expect(getTitleText()).toMatch("Revocation"); @@ -652,7 +651,7 @@ test("revocation", async () => { expect(screen.getByText(rawTx)).toBeInTheDocument(); expect(getHeightText()).toMatch("Height697812"); - user.click(screen.getByText("Back")); + await user.click(screen.getByText("Back")); expect(mockGoBackHistory).toHaveBeenCalled(); }); @@ -665,7 +664,7 @@ test("revocation", async () => { mockNormalizedStakeTransactions[mockTxHash]; selectors.currentBlockHeight = jest.fn(() => 712217); - render(, { + const { user } = render(, { initialState: { grpc: { regularTransactions: {}, @@ -683,7 +682,7 @@ test("revocation", async () => { } }); - await wait(() => getIODetails()); + await waitFor(() => getIODetails()); expect(getHeaderTitleIconClassName()).toMatch("revocation"); expect(getTitleText()).toMatch("Revocation"); @@ -711,7 +710,7 @@ test("revocation", async () => { expect(screen.getByText(rawTx)).toBeInTheDocument(); expect(getHeightText()).toMatch("Height697812"); - user.click(screen.getByText("Back")); + await user.click(screen.getByText("Back")); expect(mockGoBackHistory).toHaveBeenCalled(); }); @@ -724,7 +723,7 @@ test("revoked ticket", async () => { mockNormalizedStakeTransactions[mockTxHash]; selectors.currentBlockHeight = jest.fn(() => 924414); - render(, { + const { user } = render(, { initialState: { grpc: { regularTransactions: {}, @@ -742,7 +741,7 @@ test("revoked ticket", async () => { } }); - await wait(() => getIODetails()); + await waitFor(() => getIODetails()); expect(getHeaderTitleIconClassName()).toMatch("ticket"); expect(getTitleText()).toMatch("Ticket, Revoked"); @@ -770,7 +769,7 @@ test("revoked ticket", async () => { expect(screen.getByText(rawTx)).toBeInTheDocument(); expect(getHeightText()).toMatch("Height815405"); - user.click(screen.getByText("Back")); + await user.click(screen.getByText("Back")); expect(mockGoBackHistory).toHaveBeenCalled(); }); @@ -783,7 +782,7 @@ test("immature ticket", async () => { mockNormalizedStakeTransactions[mockTxHash]; selectors.currentBlockHeight = jest.fn(() => 930691); - render(, { + const { user } = render(, { initialState: { grpc: { regularTransactions: {}, @@ -801,7 +800,7 @@ test("immature ticket", async () => { } }); - await wait(() => getIODetails()); + await waitFor(() => getIODetails()); expect(getHeaderTitleIconClassName()).toMatch("immature"); expect(getTitleText()).toMatch("Immature"); @@ -830,7 +829,7 @@ test("immature ticket", async () => { expect(getHeightText()).toMatch("Height930690"); expect(getVSPHostText()).toMatch("VSP host:mockVspHost-immature"); - user.click(screen.getByText("Back")); + await user.click(screen.getByText("Back")); expect(mockGoBackHistory).toHaveBeenCalled(); }); @@ -843,7 +842,7 @@ test("live ticket", async () => { mockNormalizedStakeTransactions[mockTxHash]; selectors.currentBlockHeight = jest.fn(() => 931147); - render(, { + const { user } = render(, { initialState: { grpc: { regularTransactions: {}, @@ -861,7 +860,7 @@ test("live ticket", async () => { } }); - await wait(() => getIODetails()); + await waitFor(() => getIODetails()); expect(getHeaderTitleIconClassName()).toMatch("ticket"); expect(getTitleText()).toMatch("Live"); @@ -890,7 +889,7 @@ test("live ticket", async () => { expect(getHeightText()).toMatch("Height930696"); expect(getVSPHostText()).toMatch("VSP host:mockVspHost-live"); - user.click(screen.getByText("Back")); + await user.click(screen.getByText("Back")); expect(mockGoBackHistory).toHaveBeenCalled(); // fetch VSP Ticket Status @@ -898,18 +897,18 @@ test("live ticket", async () => { const fetchVSPTicketStatusBt = screen.getByRole("button", { name: "Fetch VSP Ticket Status" }); - user.click(fetchVSPTicketStatusBt); + await user.click(fetchVSPTicketStatusBt); //cancel first - user.click(screen.getByRole("button", { name: "Cancel" })); - user.click(fetchVSPTicketStatusBt); + await user.click(screen.getByRole("button", { name: "Cancel" })); + await user.click(fetchVSPTicketStatusBt); const testPassphrase = "test-pass"; - user.type(screen.getByLabelText("Private Passphrase"), testPassphrase); + await user.type(screen.getByLabelText("Private Passphrase"), testPassphrase); - user.click(screen.getByRole("button", { name: "Continue" })); + await user.click(screen.getByRole("button", { name: "Continue" })); - await wait(() => screen.getByText("Fee tx hash:")); + await waitFor(() => screen.getByText("Fee tx hash:")); expect(screen.getByText("Fee tx hash:").parentNode.textContent).toMatch( `Fee tx hash:${mockVSPTicketInfoResponse.data.feetxhash}` @@ -936,7 +935,7 @@ test("unmined ticket", async () => { mockNormalizedStakeTransactions[mockTxHash]; selectors.currentBlockHeight = jest.fn(() => 712277); - render(, { + const { user } = render(, { initialState: { grpc: { regularTransactions: {}, @@ -946,7 +945,7 @@ test("unmined ticket", async () => { } }); - await wait(() => getIODetails()); + await waitFor(() => getIODetails()); expect(getHeaderTitleIconClassName()).toMatch("unmined"); expect(getTitleText()).toMatch("Unmined"); @@ -959,10 +958,10 @@ test("unmined ticket", async () => { "To addresses: TsRWjysgoNXTS7JAQXfsaAzts8UM3Dq3WUg TseGZ7HkiWybyH6LnKdokifkFCn4cJYddm4 TsR28UZRprhgQQhzWns2M6cAwchrNVvbYq2 " ); - user.click(getAbandonTransactionButton()); + await user.click(getAbandonTransactionButton()); expect(mockAbandonTransactionAttempt).toHaveBeenCalledWith(mockTxHash); - user.click(getRebroadcastTransaction()); + await user.click(getRebroadcastTransaction()); expect(mockPublishUnminedTransactionsAttempt).toHaveBeenCalled(); expect(getWalletInputsText()).toMatch("Wallet Inputsdefault0.2000298 DCR"); @@ -978,7 +977,7 @@ test("unmined ticket", async () => { expect(queryHeight()).not.toBeInTheDocument(); // not mined expect(getVSPHostText()).toMatch("VSP host:mockVspHost-unmined"); - user.click(screen.getByText("Back")); + await user.click(screen.getByText("Back")); expect(mockGoBackHistory).toHaveBeenCalled(); }); @@ -1036,7 +1035,7 @@ test("show not yet fetched regular tx", async () => { } }); - await wait(() => getIODetails()); + await waitFor(() => getIODetails()); }); test("show not yet fetched stake tx", async () => { @@ -1090,7 +1089,7 @@ test("show not yet fetched stake tx", async () => { } }); - await wait(() => getIODetails()); + await waitFor(() => getIODetails()); }); test("show not yet fetched stake tx (won't find it)", async () => { @@ -1145,5 +1144,5 @@ test("show not yet fetched stake tx (won't find it)", async () => { } }); - await wait(() => screen.getByText("Transaction not found")); + await waitFor(() => screen.getByText("Transaction not found")); }); diff --git a/test/unit/components/views/TransactionsPage/ExportTab/ExportTab.spec.js b/test/unit/components/views/TransactionsPage/ExportTab/ExportTab.spec.js index 5f0bec917d..a6a732050b 100644 --- a/test/unit/components/views/TransactionsPage/ExportTab/ExportTab.spec.js +++ b/test/unit/components/views/TransactionsPage/ExportTab/ExportTab.spec.js @@ -1,7 +1,6 @@ import { ExportTab } from "components/views/TransactionsPage/ExportTab"; import TransactionsPage from "components/views/TransactionsPage/"; import { render } from "test-utils.js"; -import user from "@testing-library/user-event"; import { screen } from "@testing-library/react"; import * as sta from "actions/StatisticsActions"; @@ -16,9 +15,9 @@ beforeEach(() => { ); }); -test("render ExportTab within its parent", () => { - render(); - user.click(screen.getByText("Export")); +test("render ExportTab within its parent", async () => { + const { user } = render(); + await user.click(screen.getByText("Export")); expect( screen.getByText(/Export different types/i).textContent @@ -35,86 +34,86 @@ const getDestinationInput = () => screen.getByPlaceholderText("Choose destination..."); const getExportButton = () => screen.getByText("Export"); -test("test Daily Balances type control", () => { - render(); +test("test Daily Balances type control", async () => { + const { user } = render(); expect(getExportDescription()).toMatchInlineSnapshot( '"Exports all transactions recorded in the wallet."' ); expect(getExportFields()).toMatchInlineSnapshot( '"Time, Hash, Type, Direction, Fee, Amount, Credits, Debits."' ); - user.type(getDestinationInput(), mockFilePath); - user.click(getExportButton()); + await user.type(getDestinationInput(), mockFilePath); + await user.click(getExportButton()); expect(mockExportStatToCSV).toHaveBeenCalledWith({ calcFunction: sta.transactionStats, csvFilename: mockFilePath }); }); -test("test Daily Balances type control", () => { - render(); - user.click(getExportTypeSelect()); - user.click(screen.getByText("Daily Balances")); +test("test Daily Balances type control", async () => { + const { user } = render(); + await user.click(getExportTypeSelect()); + await user.click(screen.getByText("Daily Balances")); expect(getExportDescription()).toMatchInlineSnapshot( '"Export the different types of balances, with a daily aggregation."' ); expect(getExportFields()).toMatchInlineSnapshot( '"Time, Spendable, Immature, Locked, ImmatureNonWallet, LockedNonWallet, Total, StakeRewards, StakeFees, TotalStake, Sent, Received, Voted, Revoked, Ticket."' ); - user.type(getDestinationInput(), mockFilePath); - user.click(getExportButton()); + await user.type(getDestinationInput(), mockFilePath); + await user.click(getExportButton()); expect(mockExportStatToCSV).toHaveBeenCalledWith({ calcFunction: sta.dailyBalancesStats, csvFilename: mockFilePath }); }); -test("test Balances type control", () => { - render(); - user.click(getExportTypeSelect()); - user.click(screen.getByText("Balances")); +test("test Balances type control", async () => { + const { user } = render(); + await user.click(getExportTypeSelect()); + await user.click(screen.getByText("Balances")); expect(getExportDescription()).toMatchInlineSnapshot( '"Export the different types of balances after each event that changes it."' ); expect(getExportFields()).toMatchInlineSnapshot( '"Time, Spendable, Locked, Immature, LockedNonWallet, ImmatureNonWallet, Total, StakeRewards, StakeFees, TotalStake."' ); - user.type(getDestinationInput(), mockFilePath); - user.click(getExportButton()); + await user.type(getDestinationInput(), mockFilePath); + await user.click(getExportButton()); expect(mockExportStatToCSV).toHaveBeenCalledWith({ calcFunction: sta.balancesStats, csvFilename: mockFilePath }); }); -test("test Vote time type control", () => { - render(); - user.click(getExportTypeSelect()); - user.click(screen.getByText("Vote Time")); +test("test Vote time type control", async () => { + const { user } = render(); + await user.click(getExportTypeSelect()); + await user.click(screen.getByText("Vote Time")); expect(getExportDescription()).toMatchInlineSnapshot( '"Export a time-to-vote histogram in days (how many days from ticket purchase until the ticket was selected for voting)."' ); expect(getExportFields()).toMatchInlineSnapshot('"DaysToVote, Count."'); - user.type(getDestinationInput(), mockFilePath); - user.click(getExportButton()); + await user.type(getDestinationInput(), mockFilePath); + await user.click(getExportButton()); expect(mockExportStatToCSV).toHaveBeenCalledWith({ calcFunction: sta.voteTimeStats, csvFilename: mockFilePath }); }); -test("test Tickets type control", () => { - render(); - user.click(getExportTypeSelect()); - user.click(screen.getByText("Tickets")); +test("test Tickets type control", async () => { + const { user } = render(); + await user.click(getExportTypeSelect()); + await user.click(screen.getByText("Tickets")); expect(getExportDescription()).toMatchInlineSnapshot( '"Export ticket and vote information."' ); expect(getExportFields()).toMatchInlineSnapshot( '"Time, SpenderTimestamp, Status, TicketHash, SpenderHash, SentAmount, ReturnedAmount."' ); - user.type(getDestinationInput(), mockFilePath); - user.click(getExportButton()); + await user.type(getDestinationInput(), mockFilePath); + await user.click(getExportButton()); expect(mockExportStatToCSV).toHaveBeenCalledWith({ calcFunction: sta.ticketStats, csvFilename: mockFilePath diff --git a/test/unit/components/views/TransactionsPage/HistoryTab/HistoryTab.spec.js b/test/unit/components/views/TransactionsPage/HistoryTab/HistoryTab.spec.js index 46d5a169bf..feab5f0b0e 100644 --- a/test/unit/components/views/TransactionsPage/HistoryTab/HistoryTab.spec.js +++ b/test/unit/components/views/TransactionsPage/HistoryTab/HistoryTab.spec.js @@ -1,8 +1,7 @@ import { HistoryTab } from "components/views/TransactionsPage/HistoryTab"; import TransactionsPage from "components/views/TransactionsPage/"; import { render } from "test-utils.js"; -import { screen, wait } from "@testing-library/react"; -import user from "@testing-library/user-event"; +import { screen, waitFor, act } from "@testing-library/react"; export const GETNEXTADDRESS_SUCCESS = "GETNEXTADDRESS_SUCCESS"; import * as sel from "selectors"; import * as ta from "actions/TransactionActions"; @@ -14,23 +13,6 @@ let mockWalletService; const selectors = sel; const transactionActions = ta; -const initialState = { - grpc: { - transactionsFilter: { - search: null, - listDirection: "desc", - types: [], - directions: [], - maxAmount: null, - minAmount: null - }, - regularTransactions: {}, - getRegularTxsAux: { - noMoreTransactions: false - } - } -}; - const getTestTxs = (startTs) => { const txList = {}; const startDate = new Date(startTs * 1000); @@ -104,6 +86,28 @@ const mockTotalSpent = 5600005850; const mockEstimatedFee = 5850; const mockEstimatedSize = 585; +const initialState = { + grpc: { + transactionsFilter: { + search: null, + listDirection: "desc", + types: [], + directions: [], + maxAmount: null, + minAmount: null + }, + regularTransactions: {}, + getRegularTxsAux: { + noMoreTransactions: false + } + }, + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } +}; + beforeEach(() => { selectors.isTestNet = jest.fn(() => false); selectors.isMainNet = jest.fn(() => false); @@ -177,10 +181,10 @@ const incAllTestTxs = (mockGetTransactionsResponse, ts) => { return mockGetTransactionsResponse; }; -const viewAllTxs = (mockGetTransactionsResponse, chunkCount) => { +const viewAllTxs = async (user, mockGetTransactionsResponse, chunkCount) => { let i = 1; while (queryLoadingMoreLabel()) { - user.click(getLoadingMoreLabel()); + await user.click(getLoadingMoreLabel()); mockGetTransactionsResponse = incAllTestTxs(mockGetTransactionsResponse); if (i++ == chunkCount) { mockGetTransactionsResponse.getRegularTxsAux.noMoreTransactions = true; @@ -227,15 +231,15 @@ test("test txList", async () => { 1587545280 ); - render(, { + const { user } = render(, { initialState: cloneDeep(initialState) }); - user.click(screen.getByText("History")); + await user.click(screen.getByText("History")); // Scroll down to the bottom. // The data should be fetched by getTransactions request expect(getHistoryPageContent().childElementCount).toBe(0); - viewAllTxs(mockGetTransactionsResponse, chunkCount); + await viewAllTxs(user, mockGetTransactionsResponse, chunkCount); expect(getHistoryPageContent().childElementCount).toBe( Object.keys(allTestTxs).length ); @@ -246,13 +250,13 @@ test("test txList", async () => { expect(queryLoadingMoreLabel()).not.toBeInTheDocument(); // go to Send and come back. no more getTransactions call should happen - user.click(screen.getByText("Send")); - user.click(screen.getByText("History")); + await user.click(screen.getByText("Send")); + await user.click(screen.getByText("History")); mockGetTransactions.mockClear(); // start with BATCH_TX_COUNT, and loads the rest gradually expect(getHistoryPageContent().childElementCount).toBe(BATCH_TX_COUNT); - viewAllTxs(mockGetTransactionsResponse, chunkCount); + await viewAllTxs(user, mockGetTransactionsResponse, chunkCount); expect(getHistoryPageContent().childElementCount).toBe( Object.keys(allTestTxs).length ); @@ -263,17 +267,19 @@ test("test txList", async () => { const txTypeFilterButton = screen.getAllByRole("button", { name: "EyeFilterMenu" })[1]; - user.click(txTypeFilterButton); - user.click(getTxTypeFilterMenuItem("Sent")); - jest.advanceTimersByTime(101); + await user.click(txTypeFilterButton); + await user.click(getTxTypeFilterMenuItem("Sent")); + act(() => { + jest.runAllTimers(); + }); - await wait(() => + await waitFor(() => expect(getHistoryPageContent().childElementCount).not.toBe( Object.keys(allTestTxs).length ) ); - viewAllTxs(mockGetTransactionsResponse, chunkCount); + await viewAllTxs(user, mockGetTransactionsResponse, chunkCount); let expectedVisibleItems = countTxsByType(allTestTxs, ["sent"]); expect(screen.getAllByText("Sent").length).toBe(expectedVisibleItems); @@ -282,16 +288,18 @@ test("test txList", async () => { expect(queryLoadingMoreLabel()).not.toBeInTheDocument(); // show sent and received txs - user.click(txTypeFilterButton); - user.click(getTxTypeFilterMenuItem("Received")); - jest.advanceTimersByTime(101); + await user.click(txTypeFilterButton); + await user.click(getTxTypeFilterMenuItem("Received")); + act(() => { + jest.runAllTimers(); + }); - await wait(() => + await waitFor(() => expect(getHistoryPageContent().childElementCount).not.toBe( Object.keys(allTestTxs).length ) ); - viewAllTxs(mockGetTransactionsResponse, chunkCount); + await viewAllTxs(user, mockGetTransactionsResponse, chunkCount); expectedVisibleItems = countTxsByType(allTestTxs, ["sent", "received"]); expect( @@ -302,17 +310,19 @@ test("test txList", async () => { expect(queryLoadingMoreLabel()).not.toBeInTheDocument(); // show just received txs - user.click(txTypeFilterButton); - user.click(getTxTypeFilterMenuItem("Sent")); - jest.advanceTimersByTime(101); + await user.click(txTypeFilterButton); + await user.click(getTxTypeFilterMenuItem("Sent")); + act(() => { + jest.runAllTimers(); + }); - await wait(() => + await waitFor(() => expect(getHistoryPageContent().childElementCount).not.toBe( Object.keys(allTestTxs).length ) ); - viewAllTxs(mockGetTransactionsResponse, chunkCount); + await viewAllTxs(user, mockGetTransactionsResponse, chunkCount); expectedVisibleItems = countTxsByType(allTestTxs, ["received"]); expect(screen.getAllByText("Received").length).toBe(expectedVisibleItems); @@ -321,16 +331,18 @@ test("test txList", async () => { expect(queryLoadingMoreLabel()).not.toBeInTheDocument(); // show just received and Ticket Fee txs - user.click(txTypeFilterButton); - user.click(getTxTypeFilterMenuItem("Ticket fee")); - jest.advanceTimersByTime(101); + await user.click(txTypeFilterButton); + await user.click(getTxTypeFilterMenuItem("Ticket fee")); + act(() => { + jest.runAllTimers(); + }); - await wait(() => + await waitFor(() => expect(getHistoryPageContent().childElementCount).not.toBe( Object.keys(allTestTxs).length ) ); - viewAllTxs(mockGetTransactionsResponse, chunkCount); + await viewAllTxs(user, mockGetTransactionsResponse, chunkCount); expectedVisibleItems = countTxsByType(allTestTxs, ["received", "ticketfee"]); expect( @@ -342,19 +354,21 @@ test("test txList", async () => { expect(queryLoadingMoreLabel()).not.toBeInTheDocument(); // show all txs again - user.click(txTypeFilterButton); - user.click(getTxTypeFilterMenuItem("Ticket fee")); - user.click(txTypeFilterButton); - user.click(getTxTypeFilterMenuItem("All")); - jest.advanceTimersByTime(101); + await user.click(txTypeFilterButton); + await user.click(getTxTypeFilterMenuItem("Ticket fee")); + await user.click(txTypeFilterButton); + await user.click(getTxTypeFilterMenuItem("All")); + act(() => { + jest.runAllTimers(); + }); - await wait(() => + await waitFor(() => expect(getHistoryPageContent().childElementCount).not.toBe( Object.keys(allTestTxs).length ) ); - viewAllTxs(mockGetTransactionsResponse, chunkCount); + await viewAllTxs(user, mockGetTransactionsResponse, chunkCount); expect(getHistoryPageContent().childElementCount).toBe( Object.keys(allTestTxs).length ); @@ -362,16 +376,18 @@ test("test txList", async () => { expect(queryLoadingMoreLabel()).not.toBeInTheDocument(); // show just mixed txs - user.click(txTypeFilterButton); - user.click(getTxTypeFilterMenuItem("Mixed")); - jest.advanceTimersByTime(101); + await user.click(txTypeFilterButton); + await user.click(getTxTypeFilterMenuItem("Mixed")); + act(() => { + jest.runAllTimers(); + }); - await wait(() => + await waitFor(() => expect(getHistoryPageContent().childElementCount).not.toBe( Object.keys(allTestTxs).length ) ); - viewAllTxs(mockGetTransactionsResponse, chunkCount); + await viewAllTxs(user, mockGetTransactionsResponse, chunkCount); expectedVisibleItems = countTxsByType(allTestTxs, ["sent"], true); expect(screen.getAllByText("Mix").length).toBe(expectedVisibleItems); @@ -380,17 +396,19 @@ test("test txList", async () => { expect(queryLoadingMoreLabel()).not.toBeInTheDocument(); // show all txs again by toggle Mixed option - user.click(txTypeFilterButton); - user.click(getTxTypeFilterMenuItem("Mixed")); - jest.advanceTimersByTime(101); + await user.click(txTypeFilterButton); + await user.click(getTxTypeFilterMenuItem("Mixed")); + act(() => { + jest.runAllTimers(); + }); - await wait(() => + await waitFor(() => expect(getHistoryPageContent().childElementCount).not.toBe( Object.keys(allTestTxs).length ) ); - viewAllTxs(mockGetTransactionsResponse, chunkCount); + await viewAllTxs(user, mockGetTransactionsResponse, chunkCount); expect(getHistoryPageContent().childElementCount).toBe( Object.keys(allTestTxs).length ); @@ -398,12 +416,17 @@ test("test txList", async () => { expect(queryLoadingMoreLabel()).not.toBeInTheDocument(); // filter by hash id - user.type( + await user.type( screen.getByPlaceholderText("Filter by Address or Hash"), Object.keys(allTestTxs)[3] ); - jest.advanceTimersByTime(101); - await wait(() => expect(getHistoryPageContent().childElementCount).toBe(1)); + act(() => { + jest.runAllTimers(); + }); + + await waitFor(() => + expect(getHistoryPageContent().childElementCount).toBe(1) + ); }); test("show only sent txs which are coming from wallet and not from redux", async () => { @@ -435,26 +458,26 @@ test("show only sent txs which are coming from wallet and not from redux", async 1587545280 ); - render(, { + const { user } = render(, { initialState: cloneDeep(initialState) }); - user.click(screen.getByText("History")); - user.click(getLoadingMoreLabel()); + await user.click(screen.getByText("History")); + await user.click(getLoadingMoreLabel()); // show just sent txs mockGetTransactions.mockClear(); const txTypeFilterButton = screen.getAllByRole("button", { name: "EyeFilterMenu" })[1]; - user.click(txTypeFilterButton); - user.click(getTxTypeFilterMenuItem("Sent")); + await user.click(txTypeFilterButton); + await user.click(getTxTypeFilterMenuItem("Sent")); jest.advanceTimersByTime(101); - await wait(() => + await waitFor(() => expect(getHistoryPageContent().childElementCount).not.toBe(0) ); - viewAllTxs(mockGetTransactionsResponse, chunkCount); + await viewAllTxs(user, mockGetTransactionsResponse, chunkCount); const expectedVisibleItems = countTxsByType(allTestTxs, ["sent"]); expect(screen.getAllByText("Sent").length).toBe(expectedVisibleItems); @@ -472,19 +495,19 @@ test("test tx sorting", async () => { const mockchangeTransactionsFilter = (transactionActions.changeTransactionsFilter = jest.fn(() => () => {})); - render(, { + const { user } = render(, { initialState: cloneDeep(initialState) }); - user.click(screen.getByText("History")); + await user.click(screen.getByText("History")); // change sorting, show the oldest first const txSortButton = screen.getAllByRole("button", { name: "EyeFilterMenu" })[0]; - user.click(txSortButton); - user.click(screen.getByText("Oldest")); + await user.click(txSortButton); + await user.click(screen.getByText("Oldest")); jest.advanceTimersByTime(101); - await wait(() => + await waitFor(() => expect(mockchangeTransactionsFilter).toHaveBeenCalledWith({ ...initialState.grpc.transactionsFilter, listDirection: "asc" @@ -492,10 +515,10 @@ test("test tx sorting", async () => { ); // change back sorting, show the newest first - user.click(txSortButton); - user.click(screen.getByText("Newest")); + await user.click(txSortButton); + await user.click(screen.getByText("Newest")); jest.advanceTimersByTime(101); - await wait(() => + await waitFor(() => expect(mockchangeTransactionsFilter).toHaveBeenCalledWith({ ...initialState.grpc.transactionsFilter, listDirection: "desc" @@ -503,7 +526,7 @@ test("test tx sorting", async () => { ); }); -test("test no txs", () => { +test("test no txs", async () => { const mockGetTransactionsResponse = { type: transactionActions.GETTRANSACTIONS_COMPLETE, getRegularTxsAux: { noMoreTransactions: true }, @@ -520,13 +543,13 @@ test("test no txs", () => { ...cloneDeep(initialState) }; initialStateMod.grpc.getRegularTxsAux.noMoreTransactions = true; - render(, { + const { user } = render(, { initialState: initialStateMod, currentSettings: { network: "testnet" } }); - user.click(screen.getByText("History")); - user.click(getLoadingMoreLabel()); + await user.click(screen.getByText("History")); + await user.click(getLoadingMoreLabel()); expect(screen.getByText("No Transactions Found")); }); diff --git a/test/unit/components/views/TransactionsPage/ReceiveTab/ReceiveTab.spec.js b/test/unit/components/views/TransactionsPage/ReceiveTab/ReceiveTab.spec.js index 8b714ae177..2d0d186c02 100644 --- a/test/unit/components/views/TransactionsPage/ReceiveTab/ReceiveTab.spec.js +++ b/test/unit/components/views/TransactionsPage/ReceiveTab/ReceiveTab.spec.js @@ -1,8 +1,7 @@ import { ReceiveTab } from "components/views/TransactionsPage/ReceiveTab"; import TransactionsPage from "components/views/TransactionsPage/"; import { render } from "test-utils.js"; -import user from "@testing-library/user-event"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; import * as sel from "selectors"; import * as ca from "actions/ControlActions"; import * as wl from "wallet"; @@ -135,9 +134,18 @@ const getCopyButton = () => screen.getByText("Copy").nextElementSibling; const getQRCodeButton = () => screen.getByText("QR code").nextElementSibling; const getModalCloseButton = () => screen.getByText("Close"); -test("render ReceiveTab within its parent", () => { - render(); - user.click(screen.getByText("Receive")); +test("render ReceiveTab within its parent", async () => { + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + + await user.click(screen.getByText("Receive")); expect( screen.getByText(/Each time you request a payment/i).textContent @@ -147,24 +155,42 @@ test("render ReceiveTab within its parent", () => { }); test("change destination account", async () => { - render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + selectors.nextAddressAccount = jest.fn(() => mockAccount2); expect(screen.getByText(mockNextAddress)).toBeInTheDocument(); - user.click(screen.getByText(mockDefaultAccount.name)); + await user.click(screen.getByText(mockDefaultAccount.name)); expect(screen.getByText(mockEmptyAccount.name)).toBeInTheDocument(); expect(screen.getByText(mockAccount2.name)).toBeInTheDocument(); expect(screen.getAllByText(mockDefaultAccount.name).length).toBe(2); - user.click(screen.getByText(mockAccount2.name)); - await wait(() => + await user.click(screen.getByText(mockAccount2.name)); + await waitFor(() => expect(screen.getAllByText(mockAccount2.name).length).toBe(1) ); expect(mockGetNextAddressAttempt).toHaveBeenCalled(); }); -test("generate new address", () => { - render(); - user.click(screen.getByText(/generate new address/i)); +test("generate new address", async () => { + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + + await user.click(screen.getByText(/generate new address/i)); expect(mockGetNextAddressAttempt).toHaveBeenCalled(); }); @@ -181,18 +207,36 @@ test("show error when there is no walletService", () => { expect(mockWalletService).toHaveBeenCalled(); }); -test("test copy button", () => { - render(); - user.click(getCopyButton()); +test("test copy button", async () => { + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + + await user.click(getCopyButton()); expect(mockCopy).toHaveBeenCalledWith(mockNextAddress); }); -test("test QR button", () => { - render(); - user.click(getQRCodeButton()); +test("test QR button", async () => { + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + + await user.click(getQRCodeButton()); expect(mockGenQRCodeSVG).toHaveBeenCalledWith(`decred:${mockNextAddress}`); expect(screen.getAllByText(mockNextAddress).length).toBe(2); // + modal - user.click(getModalCloseButton()); + await user.click(getModalCloseButton()); // set amount mockGenQRCodeSVG.mockClear(); @@ -205,11 +249,11 @@ test("test QR button", () => { }); expect(amountInput.value).toBe(mockAmountValue); - user.click(getQRCodeButton()); + await user.click(getQRCodeButton()); expect(mockGenQRCodeSVG).toHaveBeenCalledWith( `decred:${mockNextAddress}?amount=${mockAmountValue}` ); expect(screen.getAllByText(mockNextAddress).length).toBe(2); // + modal - user.click(getModalCloseButton()); + await user.click(getModalCloseButton()); expect(screen.getAllByText(mockNextAddress).length).toBe(1); // modal has been disappeared }); diff --git a/test/unit/components/views/TransactionsPage/SendTab/SendTab.spec.js b/test/unit/components/views/TransactionsPage/SendTab/SendTab.spec.js index a70ba86063..dc1338cfaf 100644 --- a/test/unit/components/views/TransactionsPage/SendTab/SendTab.spec.js +++ b/test/unit/components/views/TransactionsPage/SendTab/SendTab.spec.js @@ -1,8 +1,7 @@ import { SendTab } from "components/views/TransactionsPage/SendTab"; import TransactionsPage from "components/views/TransactionsPage/"; import { render } from "test-utils.js"; -import user from "@testing-library/user-event"; -import { screen, wait } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; import * as sel from "selectors"; import * as ca from "actions/ControlActions"; import * as ta from "actions/TransactionActions"; @@ -11,6 +10,8 @@ import { DCR } from "constants"; import { fireEvent } from "@testing-library/react"; jest.mock("electron"); export const GETNEXTADDRESS_SUCCESS = "GETNEXTADDRESS_SUCCESS"; +import debouce from "lodash/debounce"; +jest.mock("lodash/debounce"); const mockMixedAccountValue = 6; const validAmount = 12; @@ -95,7 +96,6 @@ let mockIsMainNet; let mockWalletService; let mockConstructTransactionAttempt; let mockValidateAddress; -let mockGetNextAddressAttempt; const selectors = sel; const controlActions = ca; @@ -103,6 +103,7 @@ const transactionActions = ta; const wallet = wl; beforeEach(() => { + debouce.mockImplementation((fn) => fn); mockIsTestNet = selectors.isTestNet = jest.fn(() => false); mockIsMainNet = selectors.isMainNet = jest.fn(() => false); mockWalletService = selectors.walletService = jest.fn(() => { @@ -118,9 +119,7 @@ beforeEach(() => { selectors.estimatedSignedSize = jest.fn(() => mockEstimatedSize); selectors.totalSpent = jest.fn(() => mockTotalSpent); selectors.nextAddress = jest.fn(() => mockNextAddress); - selectors.nextAddressAccount = jest.fn(() => mockDefaultAccount); selectors.constructTxLowBalance = jest.fn(() => false); - selectors.publishTxResponse = jest.fn(() => "mockpublishtxresponse"); selectors.getNotMixedAcctIfAllowed = jest.fn(() => [0, 2]); selectors.isTrezor = jest.fn(() => false); selectors.isWatchingOnly = jest.fn(() => false); @@ -144,19 +143,17 @@ beforeEach(() => { }; }); controlActions.onClearTransaction = jest.fn(() => {}); - mockGetNextAddressAttempt = controlActions.getNextAddressAttempt = jest.fn( - () => (dispatch) => { - const res = { - address: "mock-next-address", - accountNumber: mockAccount2.value - }; - dispatch({ - getNextAddressResponse: res, - type: GETNEXTADDRESS_SUCCESS - }); - Promise.resolve(res); - } - ); + controlActions.getNextAddressAttempt = jest.fn(() => (dispatch) => { + const res = { + address: "mock-next-address", + accountNumber: mockAccount2.value + }; + dispatch({ + getNextAddressResponse: res, + type: GETNEXTADDRESS_SUCCESS + }); + Promise.resolve(res); + }); transactionActions.listUnspentOutputs = jest.fn( () => () => Promise.resolve(mockUnspentOutputs) ); @@ -201,7 +198,15 @@ const getAllAmountInput = () => screen.getAllByLabelText("Amount"); const getAllSendToInput = () => screen.getAllByLabelText("Send to"); test("render SendTab within its parent", () => { - render(); + render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); const amountInput = getAmountInput(); const sendToInput = getSendToInput(); @@ -249,7 +254,15 @@ test("render SendTab within its parent", () => { test("render SendTab within its parent in testnet mode", () => { mockIsTestNet = selectors.isTestNet = jest.fn(() => true); - render(); + render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); expect(screen.getByText(/testnet Decred addresses/i).textContent) .toMatchInlineSnapshot(` @@ -259,7 +272,15 @@ test("render SendTab within its parent in testnet mode", () => { }); test("test amount input", async () => { - render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); let amountInput = getAmountInput(); const sendAllButton = getSendAllButton(); @@ -268,15 +289,15 @@ test("test amount input", async () => { // Sending from unmixed accounts is disabled, // can't select other than mixed account. - user.click(screen.getByText(mockMixedAccount.name)); + await user.click(screen.getByText(mockMixedAccount.name)); expect(screen.queryByText(mockDefaultAccount.name)).not.toBeInTheDocument(); expect(screen.queryByText(mockAccount2.name)).not.toBeInTheDocument(); - user.click(screen.getAllByText(mockMixedAccount.name)[1]); + await user.click(screen.getAllByText(mockMixedAccount.name)[1]); // click on send all amount button - user.click(sendAllButton); + await user.click(sendAllButton); expect(queryAmountInput()).not.toBeInTheDocument(); - await wait(() => + await waitFor(() => expect(screen.getByText("Amount").nextElementSibling.textContent).toBe( `${mockMixedAccount.spendableAndUnit}100% of Account Balance` ) @@ -293,7 +314,7 @@ test("test amount input", async () => { fireEvent.change(sendToInput, { target: { value: mockValidAddress } }); - await wait(() => + await waitFor(() => expect(mockConstructTransactionAttempt).toHaveBeenCalledWith( mockMixedAccountValue, 0, @@ -315,43 +336,69 @@ test("test amount input", async () => { ); // switch back to normal mode - user.click(getCancelSendAllButton()); + await user.click(getCancelSendAllButton()); expect(amountInput.value).toBe(""); expect(screen.getByText(/0% of account balance/i)).toBeInTheDocument(); // type arbitrary amount and check the percent value amountInput = getAmountInput(); - user.type(amountInput, "12"); - expect(screen.getByText(/4.80% of account balance/i)).toBeInTheDocument(); + fireEvent.change(amountInput, { + target: { value: "12" } + }); + await waitFor(() => + expect(screen.getByText(/4.80% of account balance/i)).toBeInTheDocument() + ); // clear amountInput, should get error msg - user.clear(amountInput); - expect(screen.getByText(/This field is required/i)).toBeInTheDocument(); + fireEvent.change(amountInput, { + target: { value: "" } + }); + + await waitFor(() => + expect(screen.getByText(/This field is required/i)).toBeInTheDocument() + ); // retype validAmount - user.type(amountInput, `${validAmount}`); - expect(screen.queryByText(/This field is required/i)).not.toBeInTheDocument(); + fireEvent.change(amountInput, { + target: { value: `${validAmount}` } + }); + await waitFor(() => + expect( + screen.queryByText(/This field is required/i) + ).not.toBeInTheDocument() + ); // type more than 100% amount - user.clear(amountInput); - user.type(amountInput, "234232"); - expect(screen.getByText(/>100% of account balance/i)).toBeInTheDocument(); + fireEvent.change(amountInput, { + target: { value: "234232" } + }); + await waitFor(() => + expect(screen.getByText(/>100% of account balance/i)).toBeInTheDocument() + ); }); test("test `send to` input", async () => { - render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); const amountInput = getAmountInput(); const sendToInput = getSendToInput(); - user.type(amountInput, `${validAmount}`); + await user.type(amountInput, `${validAmount}`); // type invalid address into send to input. Should get an error message. const mockAddress = "mockAddress"; const expectedErrorMsg = "Please enter a valid address"; fireEvent.change(sendToInput, { target: { value: mockAddress } }); - await wait(() => expect(sendToInput.value).toBe(mockAddress)); + await waitFor(() => expect(sendToInput.value).toBe(mockAddress)); expect(screen.getByText(expectedErrorMsg)).toBeInTheDocument(); expect(mockValidateAddress).toHaveBeenCalledWith(mockAddress); @@ -365,10 +412,10 @@ test("test `send to` input", async () => { fireEvent.change(sendToInput, { target: { value: mockValidAddress } }); - await wait(() => expect(sendToInput.value).toBe(mockValidAddress)); + await waitFor(() => expect(sendToInput.value).toBe(mockValidAddress)); expect(screen.queryByText(expectedErrorMsg)).not.toBeInTheDocument(); - await wait(() => + await waitFor(() => expect(mockConstructTransactionAttempt).toHaveBeenCalledWith( mockMixedAccountValue, 0, @@ -378,28 +425,98 @@ test("test `send to` input", async () => { ); // test clear button - user.click(getClearAddressButton()); - await wait(() => expect(sendToInput.value).toBe("")); + await user.click(getClearAddressButton()); + await waitFor(() => expect(sendToInput.value).toBe("")); }); test("test paste button", async () => { - render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); const sendToInput = getSendToInput(); const pasteButton = getPasteButton(); const amountInput = getAmountInput(); - user.type(amountInput, `${validAmount}`); + await user.type(amountInput, `${validAmount}`); // test paste button const mockPastedAddress = "mockPastedAddress"; wallet.readFromClipboard.mockImplementation(() => mockPastedAddress); - user.click(pasteButton); - await wait(() => expect(sendToInput.value).toBe(mockPastedAddress)); + await user.click(pasteButton); + await waitFor(() => expect(sendToInput.value).toBe(mockPastedAddress)); +}); + +test("test paste button (paste address with trailing and leading spaces)", async () => { + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + + const sendToInput = getSendToInput(); + const pasteButton = getPasteButton(); + const amountInput = getAmountInput(); + + await user.type(amountInput, `${validAmount}`); + // test paste button + const mockPastedAddress = "mockPastedAddress"; + + wallet.readFromClipboard.mockImplementation( + () => ` ${mockPastedAddress} ` + ); + + await user.click(pasteButton); + await waitFor(() => expect(sendToInput.value).toBe(mockPastedAddress)); +}); + +test("type address with trailing and leading spaces", async () => { + render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + + const sendToInput = getSendToInput(); + const amountInput = getAmountInput(); + + fireEvent.change(amountInput, { + target: { value: `${validAmount}` } + }); + // test paste button + const mockPastedAddress = "mockPastedAddress"; + + // type address with trailing and leading spaces + fireEvent.change(sendToInput, { + target: { value: ` ${mockPastedAddress} ` } + }); + await waitFor(() => expect(sendToInput.value).toBe(mockPastedAddress)); }); test("construct a valid transaction", async () => { - render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); selectors.unsignedTransaction = jest.fn(() => 1); mockValidateAddress = controlActions.validateAddress = jest.fn(() => () => { @@ -409,7 +526,7 @@ test("construct a valid transaction", async () => { }; }); - await fillOutputForm(0); + await fillOutputForm(user, 0); expect(getDetailsValueColumn().textContent).toMatch( /56.0000585 DCR0.0000585 DCR585 Bytes/ @@ -417,63 +534,88 @@ test("construct a valid transaction", async () => { const sendButton = getSendButton(); expect(sendButton.disabled).toBe(false); - user.click(sendButton); + await user.click(sendButton); // modal has been shown expect(screen.getByText(/Private Passphrase/i)).toBeInTheDocument(); // close modal - user.click(screen.getByText("Cancel")); + await user.click(screen.getByText("Cancel")); expect(screen.queryByText(/Private Passphrase/i)).not.toBeInTheDocument(); }); test("test insufficient funds", () => { selectors.constructTxLowBalance = jest.fn(() => true); - render(); + render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + expect(getSendButton().disabled).toBe(true); expect(screen.getByText(/insufficient funds/i)).toBeInTheDocument(); }); test("`Sending from unmixed account` is allowed", async () => { selectors.getNotMixedAcctIfAllowed = jest.fn(() => []); - render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); - user.click(screen.getByText(mockMixedAccount.name)); + await user.click(screen.getByText(mockMixedAccount.name)); expect(screen.getByText(mockDefaultAccount.name)).toBeInTheDocument(); expect(screen.getByText(mockAccount2.name)).toBeInTheDocument(); expect(screen.getAllByText(mockMixedAccount.name).length).toBe(2); - user.click(screen.getByText(mockEmptyAccount.name)); - await wait(() => - expect(screen.getAllByText(mockEmptyAccount.name).length).toBe(1) - ); + fireEvent.click(screen.getByText(mockEmptyAccount.name)); + expect(screen.queryByText(mockMixedAccount.name)).not.toBeInTheDocument(); // valid amount but the source account is empty const amountInput = getAmountInput(); - user.type(amountInput, `${validAmount}`); + fireEvent.change(amountInput, { + target: { value: `${validAmount}` } + }); + await waitFor(() => expect(amountInput.value).toBe(`${validAmount}`)); // changing account while sending all mode is on // should change the amount accordingly click on send all amount button - user.click(getSendAllButton()); - await wait(() => + await user.click(getSendAllButton()); + await waitFor(() => expect(screen.getByText("Amount").nextElementSibling.textContent).toBe( `${mockEmptyAccount.spendableAndUnit}100% of Account Balance` ) ); - user.click(screen.getByText(mockEmptyAccount.name)); - user.click(screen.getByText(mockAccount2.name)); - await wait(() => + await user.click(screen.getByText(mockEmptyAccount.name)); + await user.click(screen.getByText(mockAccount2.name)); + await waitFor(() => expect(screen.getByText("Amount").nextElementSibling.textContent).toBe( `${mockAccount2.spendableAndUnit}100% of Account Balance` ) ); }); -const fillOutputForm = async (index) => { +const fillOutputForm = async (user, index) => { const amountInput = getAllAmountInput()[index]; const sendToInput = getAllSendToInput()[index]; - user.type(amountInput, `${mockOutputs[index].amount}`); + fireEvent.change(amountInput, { + target: { value: `${mockOutputs[index].amount}` } + }); + await waitFor(() => + expect(amountInput.value).toBe(`${mockOutputs[index].amount}`) + ); fireEvent.change(sendToInput, { target: { value: mockOutputs[index].address } }); - await wait(() => expect(sendToInput.value).toBe(mockOutputs[index].address)); + await waitFor(() => + expect(sendToInput.value).toBe(mockOutputs[index].address) + ); const expectedOutputs = []; for (let i = 0; i <= index; i++) { @@ -483,7 +625,7 @@ const fillOutputForm = async (index) => { }); } - await wait(() => + await waitFor(() => expect(mockConstructTransactionAttempt).toHaveBeenCalledWith( mockMixedAccountValue, 0, @@ -494,7 +636,15 @@ const fillOutputForm = async (index) => { }; test("test sending to multiple addresses", async () => { - render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); mockValidateAddress = controlActions.validateAddress = jest.fn(() => () => { return { @@ -506,43 +656,52 @@ test("test sending to multiple addresses", async () => { expect(sendAllButton.disabled).toBe(false); // fill the first form - await fillOutputForm(0); - user.click(getAddOutputButton()); + await fillOutputForm(user, 0); + await user.click(getAddOutputButton()); expect(sendAllButton.disabled).toBe(true); // fill the second form - await fillOutputForm(1); - user.click(getAddOutputButton()); + await fillOutputForm(user, 1); + await user.click(getAddOutputButton()); // fill the third form - fillOutputForm(2); + await fillOutputForm(user, 2); // delete the last form const deletedOutputButtons = getAllDeleteOutputButton(); - user.click( + await user.click( deletedOutputButtons[deletedOutputButtons.length - 1].nextElementSibling ); expect(getAllAmountInput().length).toBe(2); // clicking on send self button should delete all forms but the first - user.click(getSendSelfButton()); + await user.click(getSendSelfButton()); expect(getAllAmountInput().length).toBe(1); }); test("send funds to another account", async () => { - render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); const sendSelfButton = getSendSelfButton(); - user.type(getAmountInput(), `${validAmount}`); - user.click(sendSelfButton); - user.click(screen.getAllByRole("combobox")[1]); - selectors.nextAddressAccount = jest.fn(() => mockAccount2); - user.click(screen.getByText(mockAccount2.name)); - await wait(() => - expect(screen.queryByText(mockDefaultAccount.name)).not.toBeInTheDocument() + fireEvent.change(getAmountInput(), { + target: { value: `${validAmount}` } + }); + await user.click(sendSelfButton); + await user.click(screen.getByText(mockDefaultAccount.name)); + await user.click(screen.getByText(mockAccount2.name)); + await waitFor(() => + expect(screen.getAllByText(mockAccount2.name).length).toBe(1) ); - await wait(() => + await waitFor(() => expect(mockConstructTransactionAttempt).toHaveBeenCalledWith( mockMixedAccountValue, 0, @@ -550,16 +709,25 @@ test("send funds to another account", async () => { undefined ) ); - expect(mockGetNextAddressAttempt).toHaveBeenCalled(); - // switch back from send to self mode - user.click(sendSelfButton); - expect(screen.queryByText(mockAccount2.name)).not.toBeInTheDocument(); + await user.click(sendSelfButton); + await waitFor(() => + expect(screen.queryByText(mockAccount2.name)).not.toBeInTheDocument() + ); }); test("Privacy Mixer, Autobuyer or Purchase Ticket Attempt running", () => { selectors.getRunningIndicator = jest.fn(() => true); - render(); + render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + expect(getSendButton().disabled).toBe(true); expect( screen.getByText( @@ -573,7 +741,16 @@ test("show unsigned raw transaction", () => { selectors.isWatchingOnly = jest.fn(() => true); selectors.isTrezor = jest.fn(() => false); selectors.unsignedRawTx = jest.fn(() => mockUnsignedRawTx); - render(); + render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + expect(screen.getByText(/Unsigned Raw Transaction:/)).toBeInTheDocument(); expect(screen.getByText(mockUnsignedRawTx)).toBeInTheDocument(); }); @@ -581,29 +758,15 @@ test("show unsigned raw transaction", () => { test("watching only trezor should show send button", () => { selectors.isWatchingOnly = jest.fn(() => true); selectors.isTrezor = jest.fn(() => true); - render(); - expect(getSendButton()).toBeInTheDocument(); -}); - -test("address input have to allow leading/trailing spaces", async () => { - render(); - const index = 0; - const sendToInput = getAllSendToInput()[index]; - // test pasting address with trailing and leading spaces with paste putton - const pasteButton = getPasteButton(); - const mockPastedAddress = "mockPastedAddress"; - wallet.readFromClipboard.mockImplementation( - () => ` ${mockPastedAddress} ` - ); - - user.click(pasteButton); - await wait(() => expect(sendToInput.value).toBe(mockPastedAddress)); - - // type address with trailing and leading spaces - user.clear(sendToInput); - screen.debug(); - fireEvent.change(sendToInput, { - target: { value: ` ${mockOutputs[index].address} ` } + render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } }); - await wait(() => expect(sendToInput.value).toBe(mockOutputs[index].address)); + + expect(getSendButton()).toBeInTheDocument(); }); diff --git a/test/unit/components/views/TrezorPage/TrezorPage.spec.js b/test/unit/components/views/TrezorPage/TrezorPage.spec.js index 1f3ffa2458..9cc967e894 100644 --- a/test/unit/components/views/TrezorPage/TrezorPage.spec.js +++ b/test/unit/components/views/TrezorPage/TrezorPage.spec.js @@ -2,7 +2,7 @@ import TrezorPageContent from "views/TrezorPage/TrezorPageContent"; import TrezorPageSection from "views/TrezorPage/TrezorPageSection"; import { render } from "test-utils.js"; import user from "@testing-library/user-event"; -import { screen } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; import * as sel from "selectors"; import * as trza from "actions/TrezorActions"; @@ -65,20 +65,22 @@ const getDisablePassphraseOnDeviceProtectionToggleTooltip = () => const getEnablePassphraseOnDeviceProtectionToggleTooltip = () => screen.getByText("Enable Passphrase On Device Protection"); -test("no trezor is detected", () => { +test("no trezor is detected", async () => { selectors.trezorDevice = jest.fn(() => null); - render(); + const { user } = render( + + ); expect( screen.getByText(/no trezor is detected/i).textContent ).toMatchInlineSnapshot( '"No Trezor is detected. Connect the Device and check if Trezor bridge is installed and running on latest firmware."' ); - user.click(screen.getByText(/connect to trezor/i)); + await user.click(screen.getByText(/connect to trezor/i)); expect(mockTrezorConnect).toHaveBeenCalled(); }); -test("test pin protection switch", () => { +test("test pin protection switch", async () => { const { store } = render( ); @@ -95,16 +97,19 @@ test("test pin protection switch", () => { // features has been fetched store.dispatch({ type: trza.TRZ_GETFEATURES_SUCCESS, features }); - expect(pinProtectionLabel.previousElementSibling.className).not.toBe( - "spinner" - ); - expect(getDisablePinProtectionToggleTooltip()).toBeInTheDocument(); - expect(pinProtectionLabel.textContent).toMatchInlineSnapshot( - '"Toggle PIN Protection (on)"' - ); + + await waitFor(() => { + expect(pinProtectionLabel.previousElementSibling.className).not.toBe( + "spinner" + ); + expect(getDisablePinProtectionToggleTooltip()).toBeInTheDocument(); + expect(pinProtectionLabel.textContent).toMatchInlineSnapshot( + '"Toggle PIN Protection (on)"' + ); + }); // click on switch - user.click( + await user.click( getDisablePinProtectionToggleTooltip().nextElementSibling.firstElementChild ); expect(mockTogglePinProtection).toHaveBeenCalled(); @@ -112,14 +117,17 @@ test("test pin protection switch", () => { // features has been fetched again features.pin_protection = false; store.dispatch({ type: trza.TRZ_GETFEATURES_SUCCESS, features }); - expect(getEnablePinProtectionToggleTooltip()).toBeInTheDocument(); - expect(pinProtectionLabel.textContent).toMatchInlineSnapshot( - '"Toggle PIN Protection (off)"' - ); + + await waitFor(() => { + expect(getEnablePinProtectionToggleTooltip()).toBeInTheDocument(); + expect(pinProtectionLabel.textContent).toMatchInlineSnapshot( + '"Toggle PIN Protection (off)"' + ); + }); }); -test("test passphrase protection switch", () => { - const { store } = render( +test("test passphrase protection switch", async () => { + const { store, user } = render( ); const features = { @@ -137,16 +145,18 @@ test("test passphrase protection switch", () => { // features has been fetched store.dispatch({ type: trza.TRZ_GETFEATURES_SUCCESS, features }); - expect(passphraseProtectionLabel.previousElementSibling.className).not.toBe( - "spinner" - ); - expect(getDisablePassphraseProtectionToggleTooltip()).toBeInTheDocument(); - expect(passphraseProtectionLabel.textContent).toMatchInlineSnapshot( - '"Toggle Passphrase Protection (on)"' - ); + await waitFor(() => { + expect(passphraseProtectionLabel.previousElementSibling.className).not.toBe( + "spinner" + ); + expect(getDisablePassphraseProtectionToggleTooltip()).toBeInTheDocument(); + expect(passphraseProtectionLabel.textContent).toMatchInlineSnapshot( + '"Toggle Passphrase Protection (on)"' + ); + }); // click on switch - user.click( + await user.click( getDisablePassphraseProtectionToggleTooltip().nextElementSibling .firstElementChild ); @@ -155,14 +165,17 @@ test("test passphrase protection switch", () => { // features has been fetched again features.passphrase_protection = false; store.dispatch({ type: trza.TRZ_GETFEATURES_SUCCESS, features }); - expect(getEnablePassphraseProtectionToggleTooltip()).toBeInTheDocument(); - expect(passphraseProtectionLabel.textContent).toMatchInlineSnapshot( - '"Toggle Passphrase Protection (off)"' - ); + + await waitFor(() => { + expect(getEnablePassphraseProtectionToggleTooltip()).toBeInTheDocument(); + expect(passphraseProtectionLabel.textContent).toMatchInlineSnapshot( + '"Toggle Passphrase Protection (off)"' + ); + }); }); -test("test passphrase on device protection switch", () => { - const { store } = render( +test("test passphrase on device protection switch", async () => { + const { store, user } = render( ); const features = { @@ -181,18 +194,20 @@ test("test passphrase on device protection switch", () => { // features has been fetched store.dispatch({ type: trza.TRZ_GETFEATURES_SUCCESS, features }); - expect( - passphraseOnDeviceProtectionLabel.previousElementSibling.className - ).not.toBe("spinner"); - expect( - getDisablePassphraseOnDeviceProtectionToggleTooltip() - ).toBeInTheDocument(); - expect(passphraseOnDeviceProtectionLabel.textContent).toMatchInlineSnapshot( - '"Toggle Passphrase Protection On Device (on)"' - ); + await waitFor(() => { + expect( + passphraseOnDeviceProtectionLabel.previousElementSibling.className + ).not.toBe("spinner"); + expect( + getDisablePassphraseOnDeviceProtectionToggleTooltip() + ).toBeInTheDocument(); + expect(passphraseOnDeviceProtectionLabel.textContent).toMatchInlineSnapshot( + '"Toggle Passphrase Protection On Device (on)"' + ); + }); // click on switch - user.click( + await user.click( getDisablePassphraseOnDeviceProtectionToggleTooltip().nextElementSibling .firstElementChild ); @@ -201,42 +216,51 @@ test("test passphrase on device protection switch", () => { // features has been fetched again features.passphrase_always_on_device = false; store.dispatch({ type: trza.TRZ_GETFEATURES_SUCCESS, features }); - expect( - getEnablePassphraseOnDeviceProtectionToggleTooltip() - ).toBeInTheDocument(); - expect(passphraseOnDeviceProtectionLabel.textContent).toMatchInlineSnapshot( - '"Toggle Passphrase Protection On Device (off)"' - ); + await waitFor(() => { + expect( + getEnablePassphraseOnDeviceProtectionToggleTooltip() + ).toBeInTheDocument(); + expect(passphraseOnDeviceProtectionLabel.textContent).toMatchInlineSnapshot( + '"Toggle Passphrase Protection On Device (off)"' + ); + }); }); -test("test `Label and Homescreen` section", () => { - render(); - user.click(screen.getByLabelText("Use Decred Symbol on homescreen")); +test("test `Label and Homescreen` section", async () => { + const { user } = render( + + ); + await user.click(screen.getByLabelText("Use Decred Symbol on homescreen")); expect(mockChangeToDecredHomeScreen).toHaveBeenCalled(); // change device label const testDeviceLabel = "test-device-label"; - user.type(screen.getByLabelText("Trezor Device Label"), testDeviceLabel); - user.click(screen.getByText("Change Label")); + await user.type( + screen.getByLabelText("Trezor Device Label"), + testDeviceLabel + ); + await user.click(screen.getByText("Change Label")); expect(mockChangeLabel).toHaveBeenCalledWith(testDeviceLabel); }); -test("test `Device Setup and Recovery` and `Firmware Update` section", () => { - render(); - user.click(screen.getByRole("button", { name: "Wipe Device" })); +test("test `Device Setup and Recovery` and `Firmware Update` section", async () => { + const { user } = render( + + ); + await user.click(screen.getByRole("button", { name: "Wipe Device" })); expect(mockWipeDevice).toHaveBeenCalled(); - user.click(screen.getByRole("button", { name: "Recover Device" })); + await user.click(screen.getByRole("button", { name: "Recover Device" })); expect(mockRecoverDevice).toHaveBeenCalled(); - user.click(screen.getByRole("button", { name: "Initialize Device" })); + await user.click(screen.getByRole("button", { name: "Initialize Device" })); expect(mockInitDevice).toHaveBeenCalled(); - user.click(screen.getByRole("button", { name: "Backup Device" })); + await user.click(screen.getByRole("button", { name: "Backup Device" })); expect(mockBackupDevice).toHaveBeenCalled(); const testPath = "test-path"; - user.type(screen.getByLabelText("Path to firmware file"), testPath); - user.click(screen.getByRole("button", { name: "Update Firmware" })); + await user.type(screen.getByLabelText("Path to firmware file"), testPath); + await user.click(screen.getByRole("button", { name: "Update Firmware" })); expect(mockUpdateFirmware).toHaveBeenCalledWith(testPath); }); diff --git a/webpack/ui.dev.js b/webpack/ui.dev.js index 5daa13a937..04201fd5d0 100644 --- a/webpack/ui.dev.js +++ b/webpack/ui.dev.js @@ -10,10 +10,13 @@ const HtmlWebpackPlugin = require("html-webpack-plugin"); const CopyWebpackPlugin = require("copy-webpack-plugin"); const port = process.env.PORT || 3000; +const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); module.exports = merge(baseConfig, { mode: "development", - + devServer: { + hot: true, + }, devtool: "inline-source-map", entry: [ @@ -70,14 +73,21 @@ module.exports = merge(baseConfig, { } ], exclude: /\.module\.css$/ - } + }, + { + test: /\.[jt]sx?$/, + exclude: /node_modules/, + use: [ + { + loader: require.resolve('babel-loader'), + options: { + plugins: [require.resolve('react-refresh/babel')], + }, + }, + ], + }, ] }, - resolve: { - alias: { - "react-dom": "@hot-loader/react-dom" - } - }, node: { // Trezor-connect currently fails without this. @@ -86,6 +96,7 @@ module.exports = merge(baseConfig, { plugins: [ new webpack.HotModuleReplacementPlugin(), + new ReactRefreshWebpackPlugin(), new webpack.DefinePlugin({ __ELECTRON_ENV: JSON.stringify("renderer") diff --git a/yarn.lock b/yarn.lock index 2a3c8aea2c..bd5d5536e0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,6 +7,11 @@ resolved "https://registry.yarnpkg.com/7zip-bin/-/7zip-bin-5.1.1.tgz#9274ec7460652f9c632c59addf24efb1684ef876" integrity sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ== +"@adobe/css-tools@^4.0.1": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.2.0.tgz#e1a84fca468f4b337816fcb7f0964beb620ba855" + integrity sha512-E09FiIft46CmH5Qnjb0wsW54/YQd69LsxeKUOWawmws1XWvyFGURnAChH0mlr7YPFR1ofwvUQfcL0J3lMxXqPA== + "@ampproject/remapping@^2.1.0", "@ampproject/remapping@^2.2.0": version "2.2.1" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" @@ -22,12 +27,20 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" - integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" + integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ== + dependencies: + "@babel/highlight" "^7.22.5" + +"@babel/code-frame@^7.21.4": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" + integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== dependencies: - "@babel/highlight" "^7.18.6" + "@babel/highlight" "^7.23.4" + chalk "^2.4.2" "@babel/code-frame@^7.22.13": version "7.22.13" @@ -37,11 +50,16 @@ "@babel/highlight" "^7.22.13" chalk "^2.4.2" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.1", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.0": +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.1", "@babel/compat-data@^7.20.5": version "7.22.3" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.3.tgz#cd502a6a0b6e37d7ad72ce7e71a7160a3ae36f7e" integrity sha512-aNtko9OPOwVESUFp3MZfD8Uzxl7JzSeJpd7npIoxCasU37PFbAQRpKglkaKwlHOyeJdrREpo8TW8ldrkYWwvIQ== +"@babel/compat-data@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.5.tgz#b1f6c86a02d85d2dd3368a2b67c09add8cd0c255" + integrity sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA== + "@babel/core@7.20.2": version "7.20.2" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.2.tgz#8dc9b1620a673f92d3624bd926dc49a52cf25b92" @@ -64,20 +82,20 @@ semver "^6.3.0" "@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.17.9", "@babel/core@^7.7.2", "@babel/core@^7.7.5": - version "7.22.1" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.1.tgz#5de51c5206f4c6f5533562838337a603c1033cfd" - integrity sha512-Hkqu7J4ynysSXxmAahpN1jjRwVJ+NdpraFLIWflgjpVob3KNyK3/tIUc7Q7szed8WMp0JNa7Qtd1E9Oo22F9gA== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.5.tgz#d67d9747ecf26ee7ecd3ebae1ee22225fe902a89" + integrity sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.22.0" - "@babel/helper-compilation-targets" "^7.22.1" - "@babel/helper-module-transforms" "^7.22.1" - "@babel/helpers" "^7.22.0" - "@babel/parser" "^7.22.0" - "@babel/template" "^7.21.9" - "@babel/traverse" "^7.22.1" - "@babel/types" "^7.22.0" + "@babel/code-frame" "^7.22.5" + "@babel/generator" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helpers" "^7.22.5" + "@babel/parser" "^7.22.5" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -93,7 +111,7 @@ eslint-visitor-keys "^2.1.0" semver "^6.3.0" -"@babel/generator@^7.20.2", "@babel/generator@^7.22.0": +"@babel/generator@^7.20.2": version "7.22.3" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.3.tgz#0ff675d2edb93d7596c5f6728b52615cfc0df01e" integrity sha512-C17MW4wlk//ES/CJDL51kPNwl+qiBQyN7b9SKyVp11BLGFeSPoVaHrv+MNt8jwQFhQWowW88z1eeBx3pFz9v8A== @@ -103,6 +121,16 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" +"@babel/generator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.5.tgz#1e7bf768688acfb05cf30b2369ef855e82d984f7" + integrity sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA== + dependencies: + "@babel/types" "^7.22.5" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + "@babel/generator@^7.23.0": version "7.23.0" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420" @@ -120,45 +148,52 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.3.tgz#c9b83d1ba74e163e023f008a3d3204588a7ceb60" - integrity sha512-ahEoxgqNoYXm0k22TvOke48i1PkavGu0qGCmcq9ugi6gnmvKNaMjKBSrZTnWUi1CFEeNAUiVba0Wtzm03aSkJg== +"@babel/helper-annotate-as-pure@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" + integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== dependencies: - "@babel/types" "^7.22.3" + "@babel/types" "^7.22.5" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.0", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.1": - version "7.22.1" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.1.tgz#bfcd6b7321ffebe33290d68550e2c9d7eb7c7a58" - integrity sha512-Rqx13UM3yVB5q0D/KwQ8+SPfX/+Rnsy1Lw1k/UwOC4KC6qrzIQoY3lYnBu5EHKBlEHHcj0M0W8ltPSkD8rqfsQ== +"@babel/helper-builder-binary-assignment-operator-visitor@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.5.tgz#a3f4758efdd0190d8927fcffd261755937c71878" + integrity sha512-m1EP3lVOPptR+2DwD125gziZNcmoNSHGmJROKoy87loWUQyJaVXDgpmruWqDARZSmtYQ+Dl25okU8+qhVzuykw== dependencies: - "@babel/compat-data" "^7.22.0" - "@babel/helper-validator-option" "^7.21.0" + "@babel/types" "^7.22.5" + +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.20.0", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.5.tgz#fc7319fc54c5e2fa14b2909cf3c5fd3046813e02" + integrity sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw== + dependencies: + "@babel/compat-data" "^7.22.5" + "@babel/helper-validator-option" "^7.22.5" browserslist "^4.21.3" lru-cache "^5.1.1" semver "^6.3.0" "@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.2", "@babel/helper-create-class-features-plugin@^7.21.0": - version "7.22.1" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.1.tgz#ae3de70586cc757082ae3eba57240d42f468c41b" - integrity sha512-SowrZ9BWzYFgzUMwUmowbPSGu6CXL5MSuuCkG3bejahSpSymioPmuLdhPxNOc9MjuNGjy7M/HaXvJ8G82Lywlw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.22.1" - "@babel/helper-function-name" "^7.21.0" - "@babel/helper-member-expression-to-functions" "^7.22.0" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-replace-supers" "^7.22.1" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/helper-split-export-declaration" "^7.18.6" + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.5.tgz#2192a1970ece4685fbff85b48da2c32fcb130b7c" + integrity sha512-xkb58MyOYIslxu3gKmVXmjTtUPvBU4odYzbiIQbWwLKIHCsx6UGZGX6F1IznMFVnDdirseUZopzN+ZRt8Xb33Q== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.5" semver "^6.3.0" -"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.1": - version "7.22.1" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.1.tgz#a7ed9a8488b45b467fca353cd1a44dc5f0cf5c70" - integrity sha512-WWjdnfR3LPIe+0EY8td7WmjhytxXtjKAEpnAxun/hkNiyOaPlvGK+NZaBFIdi9ndYV3Gav7BpFvtUwnaJlwi1w== +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.5.tgz#bb2bf0debfe39b831986a4efbf4066586819c6e4" + integrity sha512-1VpEFOIbMRaXyDeUwUfmTIxExLwQ+zkW+Bh5zXpApA3oQedBx9v/updixWxnx/bZpKw7u8VxWjb/qWpIcmPq8A== dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-annotate-as-pure" "^7.22.5" regexpu-core "^5.3.1" semver "^6.3.0" @@ -184,7 +219,12 @@ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== -"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0": +"@babel/helper-environment-visitor@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" + integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== + +"@babel/helper-function-name@^7.19.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== @@ -192,6 +232,14 @@ "@babel/template" "^7.20.7" "@babel/types" "^7.21.0" +"@babel/helper-function-name@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" + integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== + dependencies: + "@babel/template" "^7.22.5" + "@babel/types" "^7.22.5" + "@babel/helper-function-name@^7.23.0": version "7.23.0" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" @@ -200,13 +248,6 @@ "@babel/template" "^7.22.15" "@babel/types" "^7.23.0" -"@babel/helper-hoist-variables@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" - integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== - dependencies: - "@babel/types" "^7.18.6" - "@babel/helper-hoist-variables@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" @@ -221,14 +262,28 @@ dependencies: "@babel/types" "^7.22.3" -"@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.21.4": +"@babel/helper-member-expression-to-functions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz#0a7c56117cad3372fbf8d2fb4bf8f8d64a1e76b2" + integrity sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.21.4": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz#ac88b2f76093637489e718a90cec6cf8a9b029af" integrity sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg== dependencies: "@babel/types" "^7.21.4" -"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.20.2", "@babel/helper-module-transforms@^7.21.5", "@babel/helper-module-transforms@^7.22.1": +"@babel/helper-module-imports@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz#1a8f4c9f4027d23f520bd76b364d44434a72660c" + integrity sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-module-transforms@^7.20.2": version "7.22.1" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.1.tgz#e0cad47fedcf3cae83c11021696376e2d5a50c63" integrity sha512-dxAe9E7ySDGbQdCVOY/4+UcD8M9ZFqZcZhSPsPacvCG4M+9lwtDDQfI2EoaSvmf7W/8yCBkGU0m7Pvt1ru3UZw== @@ -242,6 +297,20 @@ "@babel/traverse" "^7.22.1" "@babel/types" "^7.22.0" +"@babel/helper-module-transforms@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz#0f65daa0716961b6e96b164034e737f60a80d2ef" + integrity sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw== + dependencies: + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" + "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" @@ -249,11 +318,23 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.21.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": +"@babel/helper-optimise-call-expression@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" + integrity sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": version "7.21.5" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz#345f2377d05a720a4e5ecfa39cbf4474a4daed56" integrity sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg== +"@babel/helper-plugin-utils@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" + integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== + "@babel/helper-remap-async-to-generator@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" @@ -264,7 +345,17 @@ "@babel/helper-wrap-function" "^7.18.9" "@babel/types" "^7.18.9" -"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.19.1", "@babel/helper-replace-supers@^7.20.7", "@babel/helper-replace-supers@^7.22.1": +"@babel/helper-remap-async-to-generator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.5.tgz#14a38141a7bf2165ad38da61d61cf27b43015da2" + integrity sha512-cU0Sq1Rf4Z55fgz7haOakIyM7+x/uCFwXpLPaeRzfoUtAEAuUZjZvFPjL/rk5rW693dIgn2hng1W7xbT7lWT4g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-wrap-function" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/helper-replace-supers@^7.19.1": version "7.22.1" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.1.tgz#38cf6e56f7dc614af63a21b45565dd623f0fdc95" integrity sha512-ut4qrkE4AuSfrwHSps51ekR1ZY/ygrP1tp0WFm8oVq6nzc/hvfV/22JylndIbsf2U2M9LOMwiSddr6y+78j+OQ== @@ -276,6 +367,18 @@ "@babel/traverse" "^7.22.1" "@babel/types" "^7.22.0" +"@babel/helper-replace-supers@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.5.tgz#71bc5fb348856dea9fdc4eafd7e2e49f585145dc" + integrity sha512-aLdNM5I3kdI/V9xGNyKSF3X/gTyMUBohTZ+/3QdQKAA9vxIiy12E+8E2HoOP1/DjeqU+g6as35QHJNMDDYpuCg== + dependencies: + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" + "@babel/helper-simple-access@^7.21.5": version "7.21.5" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz#d697a7971a5c39eac32c7e63c0921c06c8a249ee" @@ -283,6 +386,13 @@ dependencies: "@babel/types" "^7.21.5" +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers@^7.20.0": version "7.20.0" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" @@ -290,6 +400,13 @@ dependencies: "@babel/types" "^7.20.0" +"@babel/helper-skip-transparent-expression-wrappers@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847" + integrity sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-split-export-declaration@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" @@ -297,6 +414,13 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-split-export-declaration@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz#88cf11050edb95ed08d596f7a044462189127a08" + integrity sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-split-export-declaration@^7.22.6": version "7.22.6" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" @@ -304,11 +428,6 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-string-parser@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz#2b3eea65443c6bdc31c22d037c65f6d323b6b2bd" - integrity sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w== - "@babel/helper-string-parser@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" @@ -324,11 +443,21 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== -"@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0": +"@babel/helper-validator-identifier@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" + integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== + +"@babel/helper-validator-option@^7.18.6": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== +"@babel/helper-validator-option@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" + integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== + "@babel/helper-wrap-function@^7.18.6", "@babel/helper-wrap-function@^7.18.9": version "7.20.5" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz#75e2d84d499a0ab3b31c33bcfe59d6b8a45f62e3" @@ -339,7 +468,17 @@ "@babel/traverse" "^7.20.5" "@babel/types" "^7.20.5" -"@babel/helpers@^7.20.1", "@babel/helpers@^7.22.0": +"@babel/helper-wrap-function@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.5.tgz#44d205af19ed8d872b4eefb0d2fa65f45eb34f06" + integrity sha512-bYqLIBSEshYcYQyfks8ewYA8S30yaGSeRslcvKMvoUk6HHPySbxHq9YRi6ghhzEU+yhQv9bP/jXnygkStOcqZw== + dependencies: + "@babel/helper-function-name" "^7.22.5" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/helpers@^7.20.1": version "7.22.3" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.3.tgz#53b74351da9684ea2f694bf0877998da26dd830e" integrity sha512-jBJ7jWblbgr7r6wYZHMdIqKc73ycaTcCaWRq4/2LpuPHcx7xMlZvpGQkOYc9HeSjn6rcx15CPlgVcBtZ4WZJ2w== @@ -348,7 +487,16 @@ "@babel/traverse" "^7.22.1" "@babel/types" "^7.22.3" -"@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6": +"@babel/helpers@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.5.tgz#74bb4373eb390d1ceed74a15ef97767e63120820" + integrity sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q== + dependencies: + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/highlight@^7.10.4": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== @@ -366,12 +514,30 @@ chalk "^2.4.2" js-tokens "^4.0.0" +"@babel/highlight@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" + integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== + dependencies: + "@babel/helper-validator-identifier" "^7.22.5" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/highlight@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" + integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + "@babel/parser@7.20.3": version "7.20.3" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.3.tgz#5358cf62e380cf69efcb87a7bb922ff88bfac6e2" integrity sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg== -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.2", "@babel/parser@^7.20.7", "@babel/parser@^7.21.9", "@babel/parser@^7.22.0": +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.2", "@babel/parser@^7.20.7", "@babel/parser@^7.21.9": version "7.22.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.4.tgz#a770e98fd785c231af9d93f6459d36770993fb32" integrity sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA== @@ -381,21 +547,26 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== +"@babel/parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.5.tgz#721fd042f3ce1896238cf1b341c77eb7dee7dbea" + integrity sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q== + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" - integrity sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz#87245a21cd69a73b0b81bcda98d443d6df08f05e" + integrity sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.18.9": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.3.tgz#a75be1365c0c3188c51399a662168c1c98108659" - integrity sha512-6r4yRwEnorYByILoDRnEqxtojYKuiIv9FojW2E8GUKo9eWBwbKcd9IiZOZpdyXc64RmyGGyPu3/uAcrz/dq2kQ== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz#fef09f9499b1f1c930da8a0c419db42167d792ca" + integrity sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/plugin-transform-optional-chaining" "^7.22.3" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-transform-optional-chaining" "^7.22.5" "@babel/plugin-proposal-async-generator-functions@^7.20.1": version "7.20.7" @@ -553,9 +724,9 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-proposal-private-property-in-object@^7.18.6": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz#19496bd9883dd83c23c7d7fc45dcd9ad02dfa1dc" - integrity sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw== + version "7.21.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz#69d597086b6760c4126525cfa154f34631ff272c" + integrity sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-create-class-features-plugin" "^7.21.0" @@ -607,18 +778,18 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-decorators@^7.19.0": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.22.3.tgz#760f2d812d56c1d05970d01cdcd3c05e3d87d6ca" - integrity sha512-R16Zuge73+8/nLcDjkIpyhi5wIbN7i7fiuLJR8yQX7vPAa/ltUKtd3iLbb4AgP5nrLi91HnNUNosELIGUGH1bg== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.22.5.tgz#329fe2907c73de184033775637dbbc507f09116a" + integrity sha512-avpUOBS7IU6al8MmF1XpAyj9QYeLPuSDJI5D4pVMSMdL7xQokKqJPYQC67RCT0aCTashUXPiGwMJ0DEXXCEmMA== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-do-expressions@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-do-expressions/-/plugin-syntax-do-expressions-7.18.6.tgz#8581baedc0f128cdf0292e3003a7f44e47b87368" - integrity sha512-kTogvOsjBTVOSZtkkziiXB5hwGXqwhq2gBXDaiWVruRLDT7C2GqfbsMnicHJ7ePq2GE8UJeWS34YbNP6yDhwUA== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-do-expressions/-/plugin-syntax-do-expressions-7.22.5.tgz#49e3af8e2d1254a47d545d4b52d2ef0dc5bcbd51" + integrity sha512-60pOTgQGY00/Kiozrtu286Aqg50IxDy/jIHhlMzXjYTs1Q8lbeOgqC9NLidtqfBNwdX6bZCT6FJ2i5xzt+JKzw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-dynamic-import@^7.8.3": version "7.8.3" @@ -628,11 +799,11 @@ "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-export-default-from@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.18.6.tgz#8df076711a4818c4ce4f23e61d622b0ba2ff84bc" - integrity sha512-Kr//z3ujSVNx6E9z9ih5xXXMqK07VVTuqPmqGe6Mss/zW5XPeLZeSDZoP9ab/hT4wPKqAgjl2PnhPrcpk8Seew== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.22.5.tgz#ac3a24b362a04415a017ab96b9b4483d0e2a6e44" + integrity sha512-ODAqWWXB/yReh/jVQDag/3/tl6lgBueQkk/TcfW/59Oykm4c8a55XloX0CTk2k2VJiFWMgHby9xNX29IbCv9dQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-export-namespace-from@^7.8.3": version "7.8.3" @@ -641,33 +812,33 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-syntax-flow@^7.18.6": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.21.4.tgz#3e37fca4f06d93567c1cd9b75156422e90a67107" - integrity sha512-l9xd3N+XG4fZRxEP3vXdK6RW7vN1Uf5dxzRC/09wV86wqZ/YYQooBIGNsiRdfNR3/q2/5pPzV4B54J/9ctX5jw== +"@babel/plugin-syntax-flow@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.22.5.tgz#163b820b9e7696ce134df3ee716d9c0c98035859" + integrity sha512-9RdCl0i+q0QExayk2nOS7853w08yLucnnPML6EN9S8fgMPVtdLDCdx/cOQ/i44Lb9UeQX9A35yaqBBOMMZxPxQ== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-function-bind@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-function-bind/-/plugin-syntax-function-bind-7.18.6.tgz#3214e8bfc71ec1de636ddbc01838c2829e560b19" - integrity sha512-wZN0Aq/AScknI9mKGcR3TpHdASMufFGaeJgc1rhPmLtZ/PniwjePSh8cfh8tXMB3U4kh/3cRKrLjDtedejg8jQ== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-function-bind/-/plugin-syntax-function-bind-7.22.5.tgz#4a01aa675dac0431b47eb440900ed0d4efd54d50" + integrity sha512-Sjy7XIhHF9L++0Mk/3Y4H4439cjI//wc/jE8Ly3+qGPkTUYYEhe4rzMv/JnyZpekfOBL22X6DAq42I7GM/3KzA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-function-sent@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-function-sent/-/plugin-syntax-function-sent-7.18.6.tgz#ce2e8e9979f8a26246bba81534e605c6d1369e5e" - integrity sha512-f3OJHIlFIkg+cP1Hfo2SInLhsg0pz2Ikmgo7jMdIIKC+3jVXQlHB0bgSapOWxeWI0SU28qIWmfn5ZKu1yPJHkg== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-function-sent/-/plugin-syntax-function-sent-7.22.5.tgz#9bb1d4d8e835c30e0eb1c45bd3883658ccc0ff94" + integrity sha512-tKOWGUAVv+JGJ1tcOIFdCqxUX97lgAUnmLpWt/9JtEkgk9WQ5OolN+y9rWj6mtLM+d0kAzTGLu/kRQqr5/PEsA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-import-assertions@^7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz#bb50e0d4bea0957235390641209394e87bdb9cc4" - integrity sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz#07d252e2aa0bc6125567f742cd58619cb14dce98" + integrity sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg== dependencies: - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-import-meta@^7.10.4", "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" @@ -683,12 +854,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.21.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz#f264ed7bf40ffc9ec239edabc17a50c4f5b6fea2" - integrity sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ== +"@babel/plugin-syntax-jsx@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz#a6b68e84fb76e759fc3b93e901876ffabbe1d918" + integrity sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" @@ -740,11 +911,11 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-throw-expressions@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-throw-expressions/-/plugin-syntax-throw-expressions-7.18.6.tgz#50889d493f7ef9631d79bae6b30f58fa8c06449f" - integrity sha512-rp1CqEZXGv1z1YZ3qYffBH3rhnOxrTwQG8fh2yqulTurwv9zu3Gthfd+niZBLSOi1rY6146TgF+JmVeDXaX4TQ== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-throw-expressions/-/plugin-syntax-throw-expressions-7.22.5.tgz#9ab803c17e25bbfedf8375acbbb0aa9f51c55496" + integrity sha512-oCyfA7rDVcQIydA7ZOmnHCQTzz5JvG9arY++Z+ASL/q5q+mJLblaRNHoK6ggV54X2c14wCK/lQi7z1DujmEmZA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-top-level-await@^7.14.5", "@babel/plugin-syntax-top-level-await@^7.8.3": version "7.14.5" @@ -754,276 +925,276 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-arrow-functions@^7.18.6": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.21.5.tgz#9bb42a53de447936a57ba256fbf537fc312b6929" - integrity sha512-wb1mhwGOCaXHDTcsRYMKF9e5bbMgqwxtqa2Y1ifH96dXJPwbuLX9qHy3clhrxVqgMz7nyNXs8VkxdH8UBcjKqA== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz#e5ba566d0c58a5b2ba2a8b795450641950b71958" + integrity sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-async-to-generator@^7.18.6": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz#dfee18623c8cb31deb796aa3ca84dda9cea94354" - integrity sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz#c7a85f44e46f8952f6d27fe57c2ed3cc084c3775" + integrity sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ== dependencies: - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-remap-async-to-generator" "^7.18.9" + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-remap-async-to-generator" "^7.22.5" "@babel/plugin-transform-block-scoped-functions@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" - integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz#27978075bfaeb9fa586d3cb63a3d30c1de580024" + integrity sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-block-scoping@^7.20.2": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz#e737b91037e5186ee16b76e7ae093358a5634f02" - integrity sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz#8bfc793b3a4b2742c0983fadc1480d843ecea31b" + integrity sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-classes@^7.20.2": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz#f469d0b07a4c5a7dbb21afad9e27e57b47031665" - integrity sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.21.0" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-replace-supers" "^7.20.7" - "@babel/helper-split-export-declaration" "^7.18.6" + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.5.tgz#635d4e98da741fad814984639f4c0149eb0135e1" + integrity sha512-2edQhLfibpWpsVBx2n/GKOz6JdGQvLruZQfGr9l1qes2KQaWswjBzhQF7UDUZMNaMMQeYnQzxwOMPsbYF7wqPQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.5" globals "^11.1.0" "@babel/plugin-transform-computed-properties@^7.18.9": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.21.5.tgz#3a2d8bb771cd2ef1cd736435f6552fe502e11b44" - integrity sha512-TR653Ki3pAwxBxUe8srfF3e4Pe3FTA46uaNHYyQwIoM4oWKSoOZiDNyHJ0oIoDIUPSRQbQG7jzgVBX3FPVne1Q== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz#cd1e994bf9f316bd1c2dafcd02063ec261bb3869" + integrity sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" - "@babel/template" "^7.20.7" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/template" "^7.22.5" "@babel/plugin-transform-destructuring@^7.20.2": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz#73b46d0fd11cd6ef57dea8a381b1215f4959d401" - integrity sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz#d3aca7438f6c26c78cdd0b0ba920a336001b27cc" + integrity sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-dotall-regex@^7.18.6", "@babel/plugin-transform-dotall-regex@^7.4.4": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz#b286b3e7aae6c7b861e45bed0a2fafd6b1a4fef8" - integrity sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz#dbb4f0e45766eb544e193fb00e65a1dd3b2a4165" + integrity sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-duplicate-keys@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz#687f15ee3cdad6d85191eb2a372c4528eaa0ae0e" - integrity sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz#b6e6428d9416f5f0bba19c70d1e6e7e0b88ab285" + integrity sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-exponentiation-operator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" - integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz#402432ad544a1f9a480da865fda26be653e48f6a" + integrity sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-flow-strip-types@^7.18.6": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.21.0.tgz#6aeca0adcb81dc627c8986e770bfaa4d9812aff5" - integrity sha512-FlFA2Mj87a6sDkW4gfGrQQqwY/dLlBAyJa2dJEZ+FHXUVHBflO2wyKvg+OOEzXfrKYIa4HWl0mgmbCzt0cMb7w== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.22.5.tgz#0bb17110c7bf5b35a60754b2f00c58302381dee2" + integrity sha512-tujNbZdxdG0/54g/oua8ISToaXTFBf8EnSb5PgQSciIXWOWKX3S4+JR7ZE9ol8FZwf9kxitzkGQ+QWeov/mCiA== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-flow" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-flow" "^7.22.5" "@babel/plugin-transform-for-of@^7.18.8": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.5.tgz#e890032b535f5a2e237a18535f56a9fdaa7b83fc" - integrity sha512-nYWpjKW/7j/I/mZkGVgHJXh4bA1sfdFnJoOXwJuj4m3Q2EraO/8ZyrkCau9P5tbHQk01RMSt6KYLCsW7730SXQ== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz#ab1b8a200a8f990137aff9a084f8de4099ab173f" + integrity sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-function-name@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" - integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz#935189af68b01898e0d6d99658db6b164205c143" + integrity sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg== dependencies: - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-literals@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" - integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz#e9341f4b5a167952576e23db8d435849b1dd7920" + integrity sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-member-expression-literals@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" - integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz#4fcc9050eded981a468347dd374539ed3e058def" + integrity sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-modules-amd@^7.19.6": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz#3daccca8e4cc309f03c3a0c4b41dc4b26f55214a" - integrity sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz#4e045f55dcf98afd00f85691a68fc0780704f526" + integrity sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ== dependencies: - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-modules-commonjs@^7.19.6": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.5.tgz#d69fb947eed51af91de82e4708f676864e5e47bc" - integrity sha512-OVryBEgKUbtqMoB7eG2rs6UFexJi6Zj6FDXx+esBLPTCxCNxAY9o+8Di7IsUGJ+AVhp5ncK0fxWUBd0/1gPhrQ== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz#7d9875908d19b8c0536085af7b053fd5bd651bfa" + integrity sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA== dependencies: - "@babel/helper-module-transforms" "^7.21.5" - "@babel/helper-plugin-utils" "^7.21.5" - "@babel/helper-simple-access" "^7.21.5" + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" "@babel/plugin-transform-modules-systemjs@^7.19.6": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.3.tgz#cc507e03e88d87b016feaeb5dae941e6ef50d91e" - integrity sha512-V21W3bKLxO3ZjcBJZ8biSvo5gQ85uIXW2vJfh7JSWf/4SLUSr1tOoHX3ruN4+Oqa2m+BKfsxTR1I+PsvkIWvNw== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz#18c31410b5e579a0092638f95c896c2a98a5d496" + integrity sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ== dependencies: - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-module-transforms" "^7.22.1" - "@babel/helper-plugin-utils" "^7.21.5" - "@babel/helper-validator-identifier" "^7.19.1" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" "@babel/plugin-transform-modules-umd@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz#81d3832d6034b75b54e62821ba58f28ed0aab4b9" - integrity sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz#4694ae40a87b1745e3775b6a7fe96400315d4f98" + integrity sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ== dependencies: - "@babel/helper-module-transforms" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-named-capturing-groups-regex@^7.19.1": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.3.tgz#db6fb77e6b3b53ec3b8d370246f0b7cf67d35ab4" - integrity sha512-c6HrD/LpUdNNJsISQZpds3TXvfYIAbo+efE9aWmY/PmSRD0agrJ9cPMt4BmArwUQ7ZymEWTFjTyp+yReLJZh0Q== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz#67fe18ee8ce02d57c855185e27e3dc959b2e991f" + integrity sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.1" - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-new-target@^7.18.6": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.3.tgz#deb0377d741cbee2f45305868b9026dcd6dd96e2" - integrity sha512-5RuJdSo89wKdkRTqtM9RVVJzHum9c2s0te9rB7vZC1zKKxcioWIy+xcu4OoIAjyFZhb/bp5KkunuLin1q7Ct+w== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz#1b248acea54ce44ea06dfd37247ba089fcf9758d" + integrity sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-object-super@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" - integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz#794a8d2fcb5d0835af722173c1a9d704f44e218c" + integrity sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-replace-supers" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.5" -"@babel/plugin-transform-optional-chaining@^7.22.3": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.3.tgz#5fd24a4a7843b76da6aeec23c7f551da5d365290" - integrity sha512-63v3/UFFxhPKT8j8u1jTTGVyITxl7/7AfOqK8C5gz1rHURPUGe3y5mvIf68eYKGoBNahtJnTxBKug4BQOnzeJg== +"@babel/plugin-transform-optional-chaining@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.5.tgz#1003762b9c14295501beb41be72426736bedd1e0" + integrity sha512-AconbMKOMkyG+xCng2JogMCDcqW8wedQAqpVIL4cOSescZ7+iW8utC6YDZLMCSUIReEA733gzRSaOSXMAt/4WQ== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-transform-parameters@^7.20.1", "@babel/plugin-transform-parameters@^7.20.7": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.3.tgz#24477acfd2fd2bc901df906c9bf17fbcfeee900d" - integrity sha512-x7QHQJHPuD9VmfpzboyGJ5aHEr9r7DsAsdxdhJiTB3J3j8dyl+NFZ+rX5Q2RWFDCs61c06qBfS4ys2QYn8UkMw== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz#c3542dd3c39b42c8069936e48717a8d179d63a18" + integrity sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-property-literals@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" - integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz#b5ddabd73a4f7f26cd0e20f5db48290b88732766" + integrity sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-react-display-name@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz#8b1125f919ef36ebdfff061d664e266c666b9415" - integrity sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.22.5.tgz#3c4326f9fce31c7968d6cb9debcaf32d9e279a2b" + integrity sha512-PVk3WPYudRF5z4GKMEYUrLjPl38fJSKNaEOkFuoprioowGuWN6w2RKznuFNSlJx7pzzXXStPUnNSOEO0jL5EVw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-react-jsx-development@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz#dbe5c972811e49c7405b630e4d0d2e1380c0ddc5" - integrity sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz#e716b6edbef972a92165cd69d92f1255f7e73e87" + integrity sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A== dependencies: - "@babel/plugin-transform-react-jsx" "^7.18.6" + "@babel/plugin-transform-react-jsx" "^7.22.5" -"@babel/plugin-transform-react-jsx@^7.18.6": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.3.tgz#5a1f380df3703ba92eb1a930a539c6d88836f690" - integrity sha512-JEulRWG2f04a7L8VWaOngWiK6p+JOSpB+DAtwfJgOaej1qdbNxqtK7MwTBHjUA10NeFcszlFNqCdbRcirzh2uQ== +"@babel/plugin-transform-react-jsx@^7.18.6", "@babel/plugin-transform-react-jsx@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.5.tgz#932c291eb6dd1153359e2a90cb5e557dcf068416" + integrity sha512-rog5gZaVbUip5iWDMTYbVM15XQq+RkUKhET/IHR6oizR+JEoN6CAfTTuHcK4vwUyzca30qqHqEpzBOnaRMWYMA== dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-module-imports" "^7.21.4" - "@babel/helper-plugin-utils" "^7.21.5" - "@babel/plugin-syntax-jsx" "^7.21.4" - "@babel/types" "^7.22.3" + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-jsx" "^7.22.5" + "@babel/types" "^7.22.5" "@babel/plugin-transform-react-pure-annotations@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz#561af267f19f3e5d59291f9950fd7b9663d0d844" - integrity sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.22.5.tgz#1f58363eef6626d6fa517b95ac66fe94685e32c0" + integrity sha512-gP4k85wx09q+brArVinTXhWiyzLl9UpmGva0+mWyKxk6JZequ05x3eUcIUE+FyttPKJFRRVtAvQaJ6YF9h1ZpA== dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-regenerator@^7.18.6": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.21.5.tgz#576c62f9923f94bcb1c855adc53561fd7913724e" - integrity sha512-ZoYBKDb6LyMi5yCsByQ5jmXsHAQDDYeexT1Szvlmui+lADvfSecr5Dxd/PkrTC3pAD182Fcju1VQkB4oCp9M+w== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.5.tgz#cd8a68b228a5f75fa01420e8cc2fc400f0fc32aa" + integrity sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-plugin-utils" "^7.22.5" regenerator-transform "^0.15.1" "@babel/plugin-transform-reserved-words@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz#b1abd8ebf8edaa5f7fe6bbb8d2133d23b6a6f76a" - integrity sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz#832cd35b81c287c4bcd09ce03e22199641f964fb" + integrity sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-shorthand-properties@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" - integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz#6e277654be82b5559fc4b9f58088507c24f0c624" + integrity sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-spread@^7.19.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz#c2d83e0b99d3bf83e07b11995ee24bf7ca09401e" - integrity sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz#6487fd29f229c95e284ba6c98d65eafb893fea6b" + integrity sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" "@babel/plugin-transform-sticky-regex@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz#c6706eb2b1524028e317720339583ad0f444adcc" - integrity sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz#295aba1595bfc8197abd02eae5fc288c0deb26aa" + integrity sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-strict-mode@7.18.6": version "7.18.6" @@ -1033,33 +1204,33 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-template-literals@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" - integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz#8f38cf291e5f7a8e60e9f733193f0bcc10909bff" + integrity sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-typeof-symbol@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz#c8cea68263e45addcd6afc9091429f80925762c0" - integrity sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz#5e2ba478da4b603af8673ff7c54f75a97b716b34" + integrity sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-unicode-escapes@^7.18.10": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.21.5.tgz#1e55ed6195259b0e9061d81f5ef45a9b009fb7f2" - integrity sha512-LYm/gTOwZqsYohlvFUe/8Tujz75LqqVC2w+2qPHLR+WyWHGCZPN1KBpJCJn+4Bk4gOkQy/IXKIge6az5MqwlOg== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.5.tgz#ce0c248522b1cb22c7c992d88301a5ead70e806c" + integrity sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-unicode-regex@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" - integrity sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz#ce7e7bb3ef208c4ff67e02a22816656256d7a183" + integrity sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/polyfill@^7.12.1": version "7.12.1" @@ -1198,15 +1369,7 @@ resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime-corejs3@^7.10.2": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.22.3.tgz#a684fb6b2eb987d9eb4ee7f1bba832473a29f0f1" - integrity sha512-6bdmknScYKmt8I9VjsJuKKGr+TwUb555FTf6tT1P/ANlCjTHCiYLhiQ4X/O7J731w5NOqu8c1aYHEVuOwPz7jA== - dependencies: - core-js-pure "^3.30.2" - regenerator-runtime "^0.13.11" - -"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.18.3", "@babel/runtime@^7.2.0", "@babel/runtime@^7.5.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.1.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.18.3", "@babel/runtime@^7.2.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.22.3" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.3.tgz#0a7fce51d43adbf0f7b517a71f4c3aaca92ebcbb" integrity sha512-XsDuspWKLUsxwCp6r7EhsExHtYfbe5oAGQ19kqngTdCPUoPQzOPdUbD/pB9PJiwb2ptYKQDjSJT3R6dC+EPqfQ== @@ -1231,6 +1394,15 @@ "@babel/parser" "^7.22.15" "@babel/types" "^7.22.15" +"@babel/template@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" + integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw== + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/parser" "^7.22.5" + "@babel/types" "^7.22.5" + "@babel/traverse@^7.1.0", "@babel/traverse@^7.20.1", "@babel/traverse@^7.20.5", "@babel/traverse@^7.22.1": version "7.23.2" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" @@ -1247,6 +1419,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.5.tgz#44bd276690db6f4940fdb84e1cb4abd2f729ccd1" + integrity sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ== + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/generator" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.5" + "@babel/parser" "^7.22.5" + "@babel/types" "^7.22.5" + debug "^4.1.0" + globals "^11.1.0" + "@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.4", "@babel/types@^7.21.5", "@babel/types@^7.22.0", "@babel/types@^7.22.3", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.22.4" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.4.tgz#56a2653ae7e7591365dabf20b76295410684c071" @@ -1481,9 +1669,9 @@ integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA== "@emotion/react@^11.8.1": - version "11.11.0" - resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.11.0.tgz#408196b7ef8729d8ad08fc061b03b046d1460e02" - integrity sha512-ZSK3ZJsNkwfjT3JpDAWJZlrGD81Z3ytNDsxw1LKq1o+xkmO5pnWfr6gmCC8gHEFf3nSSX/09YrG67jybNPxSUw== + version "11.11.1" + resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.11.1.tgz#b2c36afac95b184f73b08da8c214fdf861fa4157" + integrity sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA== dependencies: "@babel/runtime" "^7.18.3" "@emotion/babel-plugin" "^11.11.0" @@ -1583,11 +1771,6 @@ resolved "https://registry.yarnpkg.com/@formatjs/intl-utils/-/intl-utils-2.3.0.tgz#2dc8c57044de0340eb53a7ba602e59abf80dc799" integrity sha512-KWk80UPIzPmUg+P0rKh6TqspRw0G6eux1PuJr+zz47ftMaZ9QDwbGzHZbtzWkl5hgayM/qrKRutllRC7D/vVXQ== -"@gar/promisify@^1.1.3": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" - integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== - "@grpc/grpc-js@1.8.8": version "1.8.8" resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.8.8.tgz#a7c6765d0302f47ba67c0ce3cb79718d6b028248" @@ -1631,6 +1814,18 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -1703,6 +1898,13 @@ "@types/node" "*" jest-mock "^26.6.2" +"@jest/expect-utils@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.5.0.tgz#f74fad6b6e20f924582dc8ecbf2cb800fe43a036" + integrity sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg== + dependencies: + jest-get-type "^29.4.3" + "@jest/fake-timers@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.6.2.tgz#459c329bcf70cee4af4d7e3f3e67848123535aad" @@ -1756,6 +1958,13 @@ optionalDependencies: node-notifier "^8.0.0" +"@jest/schemas@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788" + integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg== + dependencies: + "@sinclair/typebox" "^0.25.16" + "@jest/source-map@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.6.2.tgz#29af5e1e2e324cafccc936f218309f54ab69d535" @@ -1807,25 +2016,6 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" -"@jest/types@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.9.0.tgz#63cb26cb7500d069e5a389441a7c6ab5e909fc59" - integrity sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^1.1.1" - "@types/yargs" "^13.0.0" - -"@jest/types@^25.5.0": - version "25.5.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-25.5.0.tgz#4d6a4793f7b9599fc3680877b856a97dbccf2a9d" - integrity sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^1.1.1" - "@types/yargs" "^15.0.0" - chalk "^3.0.0" - "@jest/types@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" @@ -1837,6 +2027,18 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" +"@jest/types@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.5.0.tgz#f59ef9b031ced83047c67032700d8c807d6e1593" + integrity sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog== + dependencies: + "@jest/schemas" "^29.4.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + "@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": version "0.3.3" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" @@ -1986,9 +2188,9 @@ eslint-scope "5.1.1" "@noble/hashes@^1.2.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1" - integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg== + version "1.3.1" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.1.tgz#8831ef002114670c603c458ab8b11328406953a9" + integrity sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -2011,22 +2213,13 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@npmcli/fs@^2.1.0": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-2.1.2.tgz#a9e2541a4a2fec2e69c29b35e6060973da79b865" - integrity sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ== +"@npmcli/fs@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-3.1.0.tgz#233d43a25a91d68c3a863ba0da6a3f00924a173e" + integrity sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w== dependencies: - "@gar/promisify" "^1.1.3" semver "^7.3.5" -"@npmcli/move-file@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-2.0.1.tgz#26f6bdc379d87f75e55739bab89db525b06100e4" - integrity sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ== - dependencies: - mkdirp "^1.0.4" - rimraf "^3.0.2" - "@peculiar/asn1-schema@^2.3.6": version "2.3.6" resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.6.tgz#3dd3c2ade7f702a9a94dfb395c192f5fa5d6b922" @@ -2054,6 +2247,26 @@ tslib "^2.5.0" webcrypto-core "^1.7.7" +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@pmmmwh/react-refresh-webpack-plugin@^0.5.10": + version "0.5.10" + resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz#2eba163b8e7dbabb4ce3609ab5e32ab63dda3ef8" + integrity sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA== + dependencies: + ansi-html-community "^0.0.8" + common-path-prefix "^3.0.0" + core-js-pure "^3.23.3" + error-stack-parser "^2.0.6" + find-up "^5.0.0" + html-entities "^2.1.0" + loader-utils "^2.0.4" + schema-utils "^3.0.0" + source-map "^0.7.3" + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" @@ -2193,10 +2406,10 @@ "@react-spring/shared" "~9.7.2" "@react-spring/types" "~9.7.2" -"@sheerun/mutationobserver-shim@^0.3.2": - version "0.3.3" - resolved "https://registry.yarnpkg.com/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz#5405ee8e444ed212db44e79351f0c70a582aae25" - integrity sha512-DetpxZw1fzPD5xUBrIAoplLChO2VB8DlL5Gg+I1IR9b2wPqYIca2WSUxL5g1vLeR4MsQq1NeWriXAVffV+U1Fw== +"@sinclair/typebox@^0.25.16": + version "0.25.24" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" + integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== "@sindresorhus/is@^0.14.0": version "0.14.0" @@ -2251,77 +2464,62 @@ dependencies: defer-to-connect "^2.0.0" -"@testing-library/dom@*", "@testing-library/dom@>=7": - version "9.3.0" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-9.3.0.tgz#ed8ce10aa5e05eb6eaf0635b5b8975d889f66075" - integrity sha512-Dffe68pGwI6WlLRYR2I0piIkyole9cSBH5jGQKCGMRpHW5RHCqAUaqc2Kv0tUyd4dU4DLPKhJIjyKOnjv4tuUw== +"@testing-library/dom@>=7": + version "9.3.1" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-9.3.1.tgz#8094f560e9389fb973fe957af41bf766937a9ee9" + integrity sha512-0DGPd9AR3+iDTjGoMpxIkAsUihHZ3Ai6CneU6bRRrffXMgzCdlNk43jTrD2/5LT6CBb3MWTP8v510JzYtahD2w== dependencies: "@babel/code-frame" "^7.10.4" "@babel/runtime" "^7.12.5" "@types/aria-query" "^5.0.1" - aria-query "^5.0.0" + aria-query "5.1.3" chalk "^4.1.0" dom-accessibility-api "^0.5.9" lz-string "^1.5.0" pretty-format "^27.0.2" -"@testing-library/dom@^6.15.0": - version "6.16.0" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-6.16.0.tgz#04ada27ed74ad4c0f0d984a1245bb29b1fd90ba9" - integrity sha512-lBD88ssxqEfz0wFL6MeUyyWZfV/2cjEZZV3YRpb2IoJRej/4f1jB0TzqIOznTpfR1r34CNesrubxwIlAQ8zgPA== - dependencies: - "@babel/runtime" "^7.8.4" - "@sheerun/mutationobserver-shim" "^0.3.2" - "@types/testing-library__dom" "^6.12.1" - aria-query "^4.0.2" - dom-accessibility-api "^0.3.0" - pretty-format "^25.1.0" - wait-for-expect "^3.0.2" - -"@testing-library/dom@^7.21.3": - version "7.31.2" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.31.2.tgz#df361db38f5212b88555068ab8119f5d841a8c4a" - integrity sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ== +"@testing-library/dom@^8.20.0", "@testing-library/dom@^8.5.0": + version "8.20.1" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.20.1.tgz#2e52a32e46fc88369eef7eef634ac2a192decd9f" + integrity sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g== dependencies: "@babel/code-frame" "^7.10.4" "@babel/runtime" "^7.12.5" - "@types/aria-query" "^4.2.0" - aria-query "^4.2.2" + "@types/aria-query" "^5.0.1" + aria-query "5.1.3" chalk "^4.1.0" - dom-accessibility-api "^0.5.6" - lz-string "^1.4.4" - pretty-format "^26.6.2" + dom-accessibility-api "^0.5.9" + lz-string "^1.5.0" + pretty-format "^27.0.2" -"@testing-library/jest-dom@^4.2.4": - version "4.2.4" - resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-4.2.4.tgz#00dfa0cbdd837d9a3c2a7f3f0a248ea6e7b89742" - integrity sha512-j31Bn0rQo12fhCWOUWy9fl7wtqkp7In/YP2p5ZFyRuiiB9Qs3g+hS4gAmDWONbAHcRmVooNJ5eOHQDCOmUFXHg== +"@testing-library/jest-dom@^5.16.5": + version "5.16.5" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz#3912846af19a29b2dbf32a6ae9c31ef52580074e" + integrity sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA== dependencies: - "@babel/runtime" "^7.5.1" - chalk "^2.4.1" - css "^2.2.3" + "@adobe/css-tools" "^4.0.1" + "@babel/runtime" "^7.9.2" + "@types/testing-library__jest-dom" "^5.9.1" + aria-query "^5.0.0" + chalk "^3.0.0" css.escape "^1.5.1" - jest-diff "^24.0.0" - jest-matcher-utils "^24.0.0" - lodash "^4.17.11" - pretty-format "^24.0.0" + dom-accessibility-api "^0.5.6" + lodash "^4.17.15" redent "^3.0.0" -"@testing-library/react@^9.4.0": - version "9.5.0" - resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-9.5.0.tgz#71531655a7890b61e77a1b39452fbedf0472ca5e" - integrity sha512-di1b+D0p+rfeboHO5W7gTVeZDIK5+maEgstrZbWZSSvxDyfDRkkyBE1AJR5Psd6doNldluXlCWqXriUfqu/9Qg== - dependencies: - "@babel/runtime" "^7.8.4" - "@testing-library/dom" "^6.15.0" - "@types/testing-library__react" "^9.1.2" - -"@testing-library/user-event@12.8.3": - version "12.8.3" - resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-12.8.3.tgz#1aa3ed4b9f79340a1e1836bc7f57c501e838704a" - integrity sha512-IR0iWbFkgd56Bu5ZI/ej8yQwrkCv8Qydx6RzwbKz9faXazR/+5tvYKsZQgyXJiwgpcva127YO6JcWy7YlCfofQ== +"@testing-library/react@^13.4.0": + version "13.4.0" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-13.4.0.tgz#6a31e3bf5951615593ad984e96b9e5e2d9380966" + integrity sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw== dependencies: "@babel/runtime" "^7.12.5" + "@testing-library/dom" "^8.5.0" + "@types/react-dom" "^18.0.0" + +"@testing-library/user-event@^14.4.3": + version "14.4.3" + resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.4.3.tgz#af975e367743fa91989cd666666aec31a8f50591" + integrity sha512-kCUc5MEwaEMakkO5x7aoD+DLi02ehmEM2QCGWvNqAS1dV/fAvORWEjnjsEIvml59M7Y5kCkWN6fCCyPOe8OL6Q== "@tootallnate/once@1": version "1.1.2" @@ -2426,11 +2624,6 @@ varuint-bitcoin "^1.1.2" wif "^2.0.6" -"@types/aria-query@^4.2.0": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc" - integrity sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig== - "@types/aria-query@^5.0.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.1.tgz#3286741fb8f1e1580ac28784add4c7a1d49bdfbc" @@ -2463,9 +2656,9 @@ "@babel/types" "^7.0.0" "@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.0.tgz#4709d34d3eba3e1dad1950d40e80c6b5e0b81fc9" - integrity sha512-TBOjqAGf0hmaqRwpii5LLkJLg7c6OMm4nHLmpsUxwk9bBHtoTC6dAHdVWdGv4TBxj2CZOZY8Xfq8WmfoVi7n4Q== + version "7.20.1" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.1.tgz#dd6f1d2411ae677dcb2db008c962598be31d6acf" + integrity sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg== dependencies: "@babel/types" "^7.20.7" @@ -2546,9 +2739,9 @@ "@types/estree" "*" "@types/eslint@*": - version "8.40.0" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.40.0.tgz#ae73dc9ec5237f2794c4f79efd6a4c73b13daf23" - integrity sha512-nbq2mvc/tBrK9zQQuItvjJl++GTN5j06DaPtp3hZCpngmG6Q3xoyEmd0TwZI0gAy/G1X0zhGBbr2imsGFdFV0g== + version "8.40.2" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.40.2.tgz#2833bc112d809677864a4b0e7d1de4f04d7dac2d" + integrity sha512-PRVjQ4Eh9z9pmmtaq8nTjZjQwKFk7YIHIud3lRoKRBgUQjgjRmoGxxGEPXQkF+lH7QkHJRNr5F4aBgYCW0lqpQ== dependencies: "@types/estree" "*" "@types/json-schema" "*" @@ -2622,14 +2815,6 @@ dependencies: "@types/istanbul-lib-coverage" "*" -"@types/istanbul-reports@^1.1.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz#e875cc689e47bce549ec81f3df5e6f6f11cfaeb2" - integrity sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw== - dependencies: - "@types/istanbul-lib-coverage" "*" - "@types/istanbul-lib-report" "*" - "@types/istanbul-reports@^3.0.0": version "3.0.1" resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" @@ -2637,6 +2822,14 @@ dependencies: "@types/istanbul-lib-report" "*" +"@types/jest@*": + version "29.5.2" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.2.tgz#86b4afc86e3a8f3005b297ed8a72494f89e6395b" + integrity sha512-mSoZVJF5YzGVCk+FsDxzDuH7s+SCkzrgKZzf0Z0T2WudhBUPoF6ktoTPC4R0ZoCPCV5xUvuU6ias5NvxcBcMMg== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + "@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8": version "7.0.12" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" @@ -2687,9 +2880,9 @@ integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== "@types/node@*", "@types/node@>=12.12.47", "@types/node@>=13.7.0": - version "20.2.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.2.5.tgz#26d295f3570323b2837d322180dfbf1ba156fefb" - integrity sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ== + version "20.3.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.1.tgz#e8a83f1aa8b649377bb1fb5d7bac5cb90e784dfe" + integrity sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg== "@types/node@10.12.18": version "10.12.18" @@ -2731,10 +2924,10 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== -"@types/react-dom@*": - version "18.2.4" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.4.tgz#13f25bfbf4e404d26f62ac6e406591451acba9e0" - integrity sha512-G2mHoTMTL4yoydITgOGwWdWMVd8sNgyEP85xVmMKAPUBwQWm9wBPQUmvbeF4V3WBY1P7mmL4BkjQ0SqUpf1snw== +"@types/react-dom@^18.0.0": + version "18.2.6" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.6.tgz#ad621fa71a8db29af7c31b41b2ea3d8a6f4144d1" + integrity sha512-2et4PDvg6PVCyS7fuTc4gPoksV58bW0RwSxWKcPRcHZf0PRUGq03TKcD/rUHe3azfV6/5/biUBJw+HhCQjaP0A== dependencies: "@types/react" "*" @@ -2756,9 +2949,9 @@ "@types/react" "*" "@types/react@*": - version "18.2.7" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.7.tgz#dfb4518042a3117a045b8c222316f83414a783b3" - integrity sha512-ojrXpSH2XFCmHm7Jy3q44nXDyN54+EYKP2lBhJ2bqfyPj6cIUW/FZW/Csdia34NQgq7KYcAlHi5184m4X88+yw== + version "18.2.13" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.13.tgz#a98c09bde8b18f80021935b11d2d29ef5f4dcb2f" + integrity sha512-vJ+zElvi/Zn9cVXB5slX2xL8PZodPCwPRDpittQdw43JR2AJ5k3vKdgJJyneV/cYgIbLQUwXa9JVDvUZXGba+Q== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -2782,32 +2975,16 @@ integrity sha512-YesPanU1+WCigC/Aj1Mga8UCOjHIfMNHZ3zzDsUY7lI8GlKnh/Kv2QwJOQ+jNQ36Ru7IfzSedlG14hppYaN13A== "@types/stack-utils@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" - integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== - -"@types/testing-library__dom@*": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@types/testing-library__dom/-/testing-library__dom-7.5.0.tgz#e0a00dd766983b1d6e9d10d33e708005ce6ad13e" - integrity sha512-mj1aH4cj3XUpMEgVpognma5kHVtbm6U6cHZmEFzCRiXPvKkuHrFr3+yXdGLXvfFRBaQIVshPGHI+hGTOJlhS/g== - dependencies: - "@testing-library/dom" "*" - -"@types/testing-library__dom@^6.12.1": - version "6.14.0" - resolved "https://registry.yarnpkg.com/@types/testing-library__dom/-/testing-library__dom-6.14.0.tgz#1aede831cb4ed4a398448df5a2c54b54a365644e" - integrity sha512-sMl7OSv0AvMOqn1UJ6j1unPMIHRXen0Ita1ujnMX912rrOcawe4f7wu0Zt9GIQhBhJvH2BaibqFgQ3lP+Pj2hA== - dependencies: - pretty-format "^24.3.0" + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" + integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== -"@types/testing-library__react@^9.1.2": - version "9.1.3" - resolved "https://registry.yarnpkg.com/@types/testing-library__react/-/testing-library__react-9.1.3.tgz#35eca61cc6ea923543796f16034882a1603d7302" - integrity sha512-iCdNPKU3IsYwRK9JieSYAiX0+aYDXOGAmrC/3/M7AqqSDKnWWVv07X+Zk1uFSL7cMTUYzv4lQRfohucEocn5/w== +"@types/testing-library__jest-dom@^5.9.1": + version "5.14.6" + resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.6.tgz#4887f6e1af11215428ab02777873bcede98a53b0" + integrity sha512-FkHXCb+ikSoUP4Y4rOslzTdX5sqYwMxfefKh1GmZ8ce1GOkEHntSp6b5cGadmNfp5e4BMEWOMx+WSKd5/MqlDA== dependencies: - "@types/react-dom" "*" - "@types/testing-library__dom" "*" - pretty-format "^25.1.0" + "@types/jest" "*" "@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3": version "2.0.6" @@ -2836,13 +3013,6 @@ resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== -"@types/yargs@^13.0.0": - version "13.0.12" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.12.tgz#d895a88c703b78af0465a9de88aa92c61430b092" - integrity sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ== - dependencies: - "@types/yargs-parser" "*" - "@types/yargs@^15.0.0": version "15.0.15" resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.15.tgz#e609a2b1ef9e05d90489c2f5f45bbfb2be092158" @@ -2850,7 +3020,7 @@ dependencies: "@types/yargs-parser" "*" -"@types/yargs@^17.0.1": +"@types/yargs@^17.0.1", "@types/yargs@^17.0.8": version "17.0.24" resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.24.tgz#b3ef8d50ad4aa6aecf6ddc97c580a00f5aa11902" integrity sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw== @@ -3090,9 +3260,9 @@ acorn@^7.1.1, acorn@^7.4.0: integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.2.4, acorn@^8.7.1, acorn@^8.8.2: - version "8.8.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" - integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + version "8.9.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.9.0.tgz#78a16e3b2bcc198c10822786fa6679e245db5b59" + integrity sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ== agent-base@6, agent-base@^6.0.2: version "6.0.2" @@ -3194,7 +3364,7 @@ ansi-escapes@^4.2.1: dependencies: type-fest "^0.21.3" -ansi-html-community@0.0.8: +ansi-html-community@0.0.8, ansi-html-community@^0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== @@ -3204,17 +3374,17 @@ ansi-regex@^2.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== -ansi-regex@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" - integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== - ansi-regex@^5.0.0, ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-styles@^3.2.0, ansi-styles@^3.2.1: +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + +ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== @@ -3233,6 +3403,11 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + ansi-styles@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.0.0.tgz#cb102df1c56f5123eab8b67cd7b98027a0279178" @@ -3341,21 +3516,20 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -aria-query@^4.0.2, aria-query@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b" - integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA== - dependencies: - "@babel/runtime" "^7.10.2" - "@babel/runtime-corejs3" "^7.10.2" - -aria-query@^5.0.0: +aria-query@5.1.3: version "5.1.3" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== dependencies: deep-equal "^2.0.5" +aria-query@^5.0.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.2.1.tgz#bc285d9d654d1df121bcd0c134880d415ca67c15" + integrity sha512-7uFg4b+lETFgdaJyETnILsXgnnzVnkHcgRbwbPwevm5x/LmUlt3MjczMRe1zg824iBgXZNRPTBftNYyRSKLp2g== + dependencies: + dequal "^2.0.3" + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -3756,7 +3930,7 @@ base-x@^4.0.0: resolved "https://registry.yarnpkg.com/base-x/-/base-x-4.0.0.tgz#d0e3b7753450c73f8ad2389b5c018a4af7b2224a" integrity sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw== -base64-js@^1.0.2, base64-js@^1.3.1, base64-js@^1.5.1: +base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -4084,12 +4258,12 @@ browserify-zlib@^0.2.0: pako "~1.0.5" browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.21.3, browserslist@^4.21.4, browserslist@^4.21.5: - version "4.21.7" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.7.tgz#e2b420947e5fb0a58e8f4668ae6e23488127e551" - integrity sha512-BauCXrQ7I2ftSqd2mvKHGo85XR0u7Ru3C/Hxsy/0TkfCtjrmAbPdzLGasmoiBxplpDXlPvdjX9u7srIMfgasNA== + version "4.21.9" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.9.tgz#e11bdd3c313d7e2a9e87e8b4b0c7872b13897635" + integrity sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg== dependencies: - caniuse-lite "^1.0.30001489" - electron-to-chromium "^1.4.411" + caniuse-lite "^1.0.30001503" + electron-to-chromium "^1.4.431" node-releases "^2.0.12" update-browserslist-db "^1.0.11" @@ -4174,13 +4348,13 @@ buffer-xor@^1.0.3: resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== -buffer@5.6.0: - version "5.6.0" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" - integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== +buffer@6.0.3, buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" + base64-js "^1.3.1" + ieee754 "^1.2.1" buffer@^5.1.0, buffer@^5.5.0: version "5.7.1" @@ -4190,14 +4364,6 @@ buffer@^5.1.0, buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" -buffer@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" - integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - bufferutil@4.0.7: version "4.0.7" resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.7.tgz#60c0d19ba2c992dd8273d3f73772ffc894c153ad" @@ -4253,29 +4419,23 @@ bytes@3.1.2: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== -cacache@^16.1.0: - version "16.1.3" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" - integrity sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ== +cacache@^17.0.0: + version "17.1.3" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-17.1.3.tgz#c6ac23bec56516a7c0c52020fd48b4909d7c7044" + integrity sha512-jAdjGxmPxZh0IipMdR7fK/4sDSrHMLUV0+GvVUsjwyGNKHsh79kW/otg+GkbXwl6Uzvy9wsvHOX4nUoWldeZMg== dependencies: - "@npmcli/fs" "^2.1.0" - "@npmcli/move-file" "^2.0.0" - chownr "^2.0.0" - fs-minipass "^2.1.0" - glob "^8.0.1" - infer-owner "^1.0.4" + "@npmcli/fs" "^3.1.0" + fs-minipass "^3.0.0" + glob "^10.2.2" lru-cache "^7.7.1" - minipass "^3.1.6" + minipass "^5.0.0" minipass-collect "^1.0.2" minipass-flush "^1.0.5" minipass-pipeline "^1.2.4" - mkdirp "^1.0.4" p-map "^4.0.0" - promise-inflight "^1.0.1" - rimraf "^3.0.2" - ssri "^9.0.0" + ssri "^10.0.0" tar "^6.1.11" - unique-filename "^2.0.0" + unique-filename "^3.0.0" cache-base@^1.0.1: version "1.0.1" @@ -4311,9 +4471,9 @@ cacheable-request@^6.0.0: responselike "^1.0.2" cacheable-request@^7.0.2: - version "7.0.2" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.2.tgz#ea0d0b889364a25854757301ca12b2da77f91d27" - integrity sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew== + version "7.0.4" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.4.tgz#7a33ebf08613178b403635be7b899d3e69bbe817" + integrity sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg== dependencies: clone-response "^1.0.2" get-stream "^5.1.0" @@ -4363,10 +4523,10 @@ camelcase@^6.0.0, camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001464, caniuse-lite@^1.0.30001489: - version "1.0.30001492" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001492.tgz#4a06861788a52b4c81fd3344573b68cc87fe062b" - integrity sha512-2efF8SAZwgAX1FJr87KWhvuJxnGJKOnctQa8xLOskAXNXq8oiuqgl6u1kk3fFpsp3GgvzlRjiK1sl63hNtFADw== +caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001464, caniuse-lite@^1.0.30001503: + version "1.0.30001506" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001506.tgz#35bd814b310a487970c585430e9e80ee23faf14b" + integrity sha512-6XNEcpygZMCKaufIcgpQNZNf00GEqc7VQON+9Rd0K1bMYo8xhMZRAo5zpbnbMNizi4YNgIDAFrdykWsvY3H4Hw== capture-exit@^2.0.0: version "2.0.0" @@ -4392,7 +4552,7 @@ ccount@^1.0.0: resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: +chalk@^2.0.0, chalk@^2.3.2, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -4747,6 +4907,11 @@ commander@^8.3.0: resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== +common-path-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/common-path-prefix/-/common-path-prefix-3.0.0.tgz#7d007a7e07c58c4b4d5f433131a19141b29f11e0" + integrity sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w== + commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" @@ -4873,16 +5038,16 @@ core-decorators@^0.20.0: integrity sha512-7cp/Pz3AmQXjRwhAsFN+8ndRiBNyLxtZgC/fhKvrwQTf2ZlZma6LnimoJPrOqgxZ0tIeI9VvSs+QKe0OPJ0SuA== core-js-compat@^3.25.1: - version "3.30.2" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.30.2.tgz#83f136e375babdb8c80ad3c22d67c69098c1dd8b" - integrity sha512-nriW1nuJjUgvkEjIot1Spwakz52V9YkYHZAQG6A1eCgC8AA1p0zngrQEP9R0+V6hji5XilWKG1Bd0YRppmGimA== + version "3.31.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.31.0.tgz#4030847c0766cc0e803dcdfb30055d7ef2064bf1" + integrity sha512-hM7YCu1cU6Opx7MXNu0NuumM0ezNeAeRKadixyiQELWY3vT3De9S4J5ZBMraWV2vZnrE1Cirl0GtFtDtMUXzPw== dependencies: browserslist "^4.21.5" -core-js-pure@^3.30.2: - version "3.30.2" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.30.2.tgz#005a82551f4af3250dcfb46ed360fad32ced114e" - integrity sha512-p/npFUJXXBkCCTIlEGBdghofn00jWG6ZOtdoIXSJmAu2QBvN0IqpZXWweOytcwE6cfx8ZvVUy1vw8zxhe4Y2vg== +core-js-pure@^3.23.3: + version "3.31.0" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.31.0.tgz#052fd9e82fbaaf86457f5db1fadcd06f15966ff2" + integrity sha512-/AnE9Y4OsJZicCzIe97JP5XoPKQJfTuEG43aEVLFJGOJpyqELod+pE6LEl63DfG1Mp8wX97LDaDpy1GmLEUxlg== core-js@^2.4.0, core-js@^2.6.5: version "2.6.12" @@ -4910,10 +5075,10 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" -cosmiconfig@^8.1.3: - version "8.1.3" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.1.3.tgz#0e614a118fcc2d9e5afc2f87d53cd09931015689" - integrity sha512-/UkO2JKI18b5jVMJUp0lvKFMpa/Gye+ZgZjKD+DGEN9y7NRcf/nK1A0sp67ONmKtnDCNMS44E6jrk0Yc3bDuUw== +cosmiconfig@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.2.0.tgz#f7d17c56a590856cd1e7cee98734dca272b0d8fd" + integrity sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ== dependencies: import-fresh "^3.2.1" js-yaml "^4.1.0" @@ -5086,16 +5251,6 @@ css.escape@^1.5.1: resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg== -css@^2.2.3: - version "2.2.4" - resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929" - integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw== - dependencies: - inherits "^2.0.3" - source-map "^0.6.1" - source-map-resolve "^0.5.2" - urix "^0.1.0" - cssdb@^7.1.0: version "7.6.0" resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-7.6.0.tgz#beac8f7a5f676db62d3c33da517ef4c9eb008f8b" @@ -5400,6 +5555,11 @@ dependency-tree@^8.0.0: precinct "^8.0.0" typescript "^3.9.7" +dequal@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + des.js@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.1.0.tgz#1d37f5766f3bbff4ee9638e871a8768c173b81da" @@ -5523,16 +5683,16 @@ dex@./modules/dex: dependencies: sbffi "^1.0.4" -diff-sequences@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" - integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew== - diff-sequences@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== +diff-sequences@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" + integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== + diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" @@ -5611,11 +5771,6 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -dom-accessibility-api@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.3.0.tgz#511e5993dd673b97c87ea47dba0e3892f7e0c983" - integrity sha512-PzwHEmsRP3IGY4gv/Ug+rMeaTIyTJvadCb+ujYXYeIylbHJezIyNToe8KfEgHTCEYyC+/bUghYOGg8yMGlZ6vA== - dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9: version "0.5.16" resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" @@ -5669,11 +5824,6 @@ dom-serializer@^2.0.0: domhandler "^5.0.2" entities "^4.2.0" -dom-walk@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" - integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== - domain-browser@^4.19.0: version "4.22.0" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-4.22.0.tgz#6ddd34220ec281f9a65d3386d267ddd35c491f9f" @@ -5785,6 +5935,11 @@ duplexer@^0.1.2: resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -5865,10 +6020,10 @@ electron-store@7.0.3: conf "^9.0.0" type-fest "^0.20.2" -electron-to-chromium@^1.4.411: - version "1.4.416" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.416.tgz#7291f704168d3842ae4da3ae9fdc7bfbeb97d116" - integrity sha512-AUYh0XDTb2vrj0rj82jb3P9hHSyzQNdTPYWZIhPdCOui7/vpme7+HTE07BE5jwuqg/34TZ8ktlRz6GImJ4IXjA== +electron-to-chromium@^1.4.431: + version "1.4.435" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.435.tgz#761c34300603b9f1234f0b6155870d3002435db6" + integrity sha512-B0CBWVFhvoQCW/XtjRzgrmqcgVWg6RXOEM/dK59+wFV93BFGR6AeNKc4OyhM+T3IhJaOOG8o/V+33Y2mwJWtzw== electron@26.4.3: version "26.4.3" @@ -5902,6 +6057,11 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + emojis-list@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" @@ -5932,9 +6092,9 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1: once "^1.4.0" enhanced-resolve@^5.14.1, enhanced-resolve@^5.8.3: - version "5.14.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.14.1.tgz#de684b6803724477a4af5d74ccae5de52c25f6b3" - integrity sha512-Vklwq2vDKtl0y/vtwjSesgJ5MYS7Etuk5txS8VdKL4AOS1aUlD96zqIfsOSLQsdv3xgMRbtkWM8eG9XDfKUPow== + version "5.15.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" + integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -5967,9 +6127,9 @@ env-paths@^2.2.0: integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== envinfo@^7.7.3: - version "7.8.1" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" - integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== + version "7.9.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.9.0.tgz#47594a13081be0d9be6e513534e8c58dbb26c7a1" + integrity sha512-RODB4txU+xImYDemN5DqaKC0CHk05XSVkOX4pq0hK26Qx+1LChkuOyUDlGEjYb3ACr0n9qBhFjg37hQuJvpkRQ== enzyme-adapter-react-16@1.15.7: version "1.15.7" @@ -6047,6 +6207,13 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +error-stack-parser@^2.0.6: + version "2.1.4" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286" + integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ== + dependencies: + stackframe "^1.3.4" + es-abstract@^1.19.0, es-abstract@^1.20.4: version "1.21.2" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff" @@ -6108,9 +6275,9 @@ es-get-iterator@^1.1.3: stop-iteration-iterator "^1.0.0" es-module-lexer@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.2.1.tgz#ba303831f63e6a394983fde2f97ad77b22324527" - integrity sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg== + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.3.0.tgz#6be9c9e0b4543a60cd166ff6f8b4e9dae0b0c16f" + integrity sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA== es-set-tostringtag@^2.0.1: version "2.0.1" @@ -6432,6 +6599,22 @@ expect@^26.6.2: jest-message-util "^26.6.2" jest-regex-util "^26.0.0" +expect@^29.0.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.5.0.tgz#68c0509156cb2a0adb8865d413b137eeaae682f7" + integrity sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg== + dependencies: + "@jest/expect-utils" "^29.5.0" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-util "^29.5.0" + +exponential-backoff@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" + integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== + express@4.18.2: version "4.18.2" resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" @@ -6695,6 +6878,14 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" @@ -6725,6 +6916,14 @@ for-in@^1.0.2: resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== +foreground-child@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" + integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + form-data@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" @@ -6798,14 +6997,21 @@ fs-extra@^9.0.0, fs-extra@^9.0.1: jsonfile "^6.0.1" universalify "^2.0.0" -fs-minipass@^2.0.0, fs-minipass@^2.1.0: +fs-minipass@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== dependencies: minipass "^3.0.0" -fs-monkey@^1.0.3: +fs-minipass@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-3.0.2.tgz#5b383858efa8c1eb8c33b39e994f7e8555b8b3a3" + integrity sha512-2GAfyfoaCDRrM6jaOS3UsBts8yJ55VioXdWcOL7dK9zdAuKT71+WBA4ifnNYqVjYv+4SsPxjK0JT4yIIn4cA/g== + dependencies: + minipass "^5.0.0" + +fs-monkey@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.4.tgz#ee8c1b53d3fe8bb7e5d2c5c5dfc0168afdd2f747" integrity sha512-INM/fWAxMICjttnD0DX1rBvinKskj5G1w+oy/pnm9u/tSlnBrzFonJMcalKJ30P8RRsPzKcCG7Q8l0jx5Fh9YQ== @@ -6982,6 +7188,17 @@ glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== +glob@^10.2.2: + version "10.2.7" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.2.7.tgz#9dd2828cd5bc7bd861e7738d91e7113dda41d7d8" + integrity sha512-jTKehsravOJo8IJxUGfZILnkvVJM/MOfHRs8QcXolVef2zNI9Tqyy5+SeuOAZd3upViEZQLyFpQhYiHLrMUNmA== + dependencies: + foreground-child "^3.1.0" + jackspeak "^2.0.3" + minimatch "^9.0.1" + minipass "^5.0.0 || ^6.0.2" + path-scurry "^1.7.0" + glob@^6.0.1: version "6.0.4" resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" @@ -7005,17 +7222,6 @@ glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^8.0.1: - version "8.1.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" - integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^5.0.1" - once "^1.3.0" - global-agent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-3.0.0.tgz#ae7cd31bd3583b93c5a16437a1afe27cc33a1ab6" @@ -7051,14 +7257,6 @@ global-prefix@^3.0.0: kind-of "^6.0.2" which "^1.3.1" -global@^4.3.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" - integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== - dependencies: - min-document "^2.19.0" - process "^0.11.10" - globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -7377,9 +7575,9 @@ html-encoding-sniffer@^2.0.1: whatwg-encoding "^1.0.5" html-entities@^2.1.0: - version "2.3.3" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.3.tgz#117d7626bece327fc8baace8868fa6f5ef856e46" - integrity sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA== + version "2.3.6" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.6.tgz#966391d58e5737c77bca4025e31721b496ab7454" + integrity sha512-9o0+dcpIw2/HxkNuYKxSJUF/MMRZQECK4GnF+oQOmJ83yCVHTWgCH5aOXxK5bozNRmM8wtgryjHD3uloPBDEGw== html-escaper@^2.0.0: version "2.0.2" @@ -7447,7 +7645,7 @@ htmlparser2@^8.0.1: domutils "^3.0.1" entities "^4.4.0" -http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0: +http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== @@ -7548,7 +7746,7 @@ identity-obj-proxy@^3.0.0: dependencies: harmony-reflect "^1.4.6" -ieee754@^1.1.13, ieee754@^1.1.4, ieee754@^1.2.1: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -7614,11 +7812,6 @@ indexes-of@^1.0.1: resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" integrity sha512-bup+4tap3Hympa+JBJUG7XuOsdNQ6fxt0MHyXMKuLBKn0OqsTfvUxkUrroEX1+B2VsSHvCjiIcZVxRtYa4nllA== -infer-owner@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" - integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== - inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -8235,6 +8428,15 @@ istanbul-reports@^3.0.2: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +jackspeak@^2.0.3: + version "2.2.1" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.2.1.tgz#655e8cf025d872c9c03d3eb63e8f0c024fef16a6" + integrity sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jake@^10.8.5: version "10.8.7" resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.7.tgz#63a32821177940c33f356e0ba44ff9d34e1c7d8f" @@ -8302,16 +8504,6 @@ jest-date-mock@^1.0.8: resolved "https://registry.yarnpkg.com/jest-date-mock/-/jest-date-mock-1.0.8.tgz#13468c0352c5a3614c6b356dbc6b88eb37d9e0b3" integrity sha512-0Lyp+z9xvuNmLbK+5N6FOhSiBeux05Lp5bbveFBmYo40Aggl2wwxFoIrZ+rOWC8nDNcLeBoDd2miQdEDSf3iQw== -jest-diff@^24.0.0, jest-diff@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.9.0.tgz#931b7d0d5778a1baf7452cb816e325e3724055da" - integrity sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ== - dependencies: - chalk "^2.0.1" - diff-sequences "^24.9.0" - jest-get-type "^24.9.0" - pretty-format "^24.9.0" - jest-diff@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" @@ -8322,6 +8514,16 @@ jest-diff@^26.6.2: jest-get-type "^26.3.0" pretty-format "^26.6.2" +jest-diff@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.5.0.tgz#e0d83a58eb5451dcc1fa61b1c3ee4e8f5a290d63" + integrity sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.4.3" + jest-get-type "^29.4.3" + pretty-format "^29.5.0" + jest-docblock@^26.0.0: version "26.0.0" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5" @@ -8365,16 +8567,16 @@ jest-environment-node@^26.6.2: jest-mock "^26.6.2" jest-util "^26.6.2" -jest-get-type@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.9.0.tgz#1684a0c8a50f2e4901b6644ae861f579eed2ef0e" - integrity sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q== - jest-get-type@^26.3.0: version "26.3.0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== +jest-get-type@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" + integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== + jest-haste-map@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" @@ -8428,16 +8630,6 @@ jest-leak-detector@^26.6.2: jest-get-type "^26.3.0" pretty-format "^26.6.2" -jest-matcher-utils@^24.0.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz#f5b3661d5e628dffe6dd65251dfdae0e87c3a073" - integrity sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA== - dependencies: - chalk "^2.0.1" - jest-diff "^24.9.0" - jest-get-type "^24.9.0" - pretty-format "^24.9.0" - jest-matcher-utils@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz#8e6fd6e863c8b2d31ac6472eeb237bc595e53e7a" @@ -8448,6 +8640,16 @@ jest-matcher-utils@^26.6.2: jest-get-type "^26.3.0" pretty-format "^26.6.2" +jest-matcher-utils@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz#d957af7f8c0692c5453666705621ad4abc2c59c5" + integrity sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw== + dependencies: + chalk "^4.0.0" + jest-diff "^29.5.0" + jest-get-type "^29.4.3" + pretty-format "^29.5.0" + jest-message-util@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" @@ -8463,6 +8665,21 @@ jest-message-util@^26.6.2: slash "^3.0.0" stack-utils "^2.0.2" +jest-message-util@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.5.0.tgz#1f776cac3aca332ab8dd2e3b41625435085c900e" + integrity sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.5.0" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.5.0" + slash "^3.0.0" + stack-utils "^2.0.3" + jest-mock@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.6.2.tgz#d6cb712b041ed47fe0d9b6fc3474bc6543feb302" @@ -8605,6 +8822,18 @@ jest-util@^26.6.2: is-ci "^2.0.0" micromatch "^4.0.2" +jest-util@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f" + integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ== + dependencies: + "@jest/types" "^29.5.0" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + jest-validate@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" @@ -8876,11 +9105,6 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== -klona@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.6.tgz#85bffbf819c03b2f53270412420a4555ef882e22" - integrity sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA== - known-css-properties@^0.21.0: version "0.21.0" resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.21.0.tgz#15fbd0bbb83447f3ce09d8af247ed47c68ede80d" @@ -8945,7 +9169,7 @@ loader-utils@^1.0.0: emojis-list "^3.0.0" json5 "^1.0.1" -loader-utils@^2.0.0, loader-utils@^2.0.3: +loader-utils@^2.0.0, loader-utils@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== @@ -8969,6 +9193,13 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" @@ -9009,7 +9240,7 @@ lodash.truncate@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== -lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0: +lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -9085,7 +9316,12 @@ lru-cache@^7.7.1: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== -lz-string@^1.4.4, lz-string@^1.5.0: +lru-cache@^9.1.1: + version "9.1.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-9.1.2.tgz#255fdbc14b75589d6d0e73644ca167a8db506835" + integrity sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ== + +lz-string@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== @@ -9133,27 +9369,26 @@ make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: dependencies: semver "^6.0.0" -make-fetch-happen@^10.0.3: - version "10.2.1" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164" - integrity sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w== +make-fetch-happen@^11.0.3: + version "11.1.1" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz#85ceb98079584a9523d4bf71d32996e7e208549f" + integrity sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w== dependencies: agentkeepalive "^4.2.1" - cacache "^16.1.0" - http-cache-semantics "^4.1.0" + cacache "^17.0.0" + http-cache-semantics "^4.1.1" http-proxy-agent "^5.0.0" https-proxy-agent "^5.0.0" is-lambda "^1.0.1" lru-cache "^7.7.1" - minipass "^3.1.6" - minipass-collect "^1.0.2" - minipass-fetch "^2.0.3" + minipass "^5.0.0" + minipass-fetch "^3.0.0" minipass-flush "^1.0.5" minipass-pipeline "^1.2.4" negotiator "^0.6.3" promise-retry "^2.0.1" socks-proxy-agent "^7.0.0" - ssri "^9.0.0" + ssri "^10.0.0" makeerror@1.0.12: version "1.0.12" @@ -9338,11 +9573,11 @@ mem@^8.1.1: mimic-fn "^3.1.0" memfs@^3.2.2: - version "3.5.1" - resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.5.1.tgz#f0cd1e2bfaef58f6fe09bfb9c2288f07fea099ec" - integrity sha512-UWbFJKvj5k+nETdteFndTpYxdeTMox/ULeqX5k/dpaQJCCFmj5EeKv3dBcyO2xmkRAx2vppRu5dVG7SOtsGOzA== + version "3.5.3" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.5.3.tgz#d9b40fe4f8d5788c5f895bda804cd0d9eeee9f3b" + integrity sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw== dependencies: - fs-monkey "^1.0.3" + fs-monkey "^1.0.4" memoize-one@^5.0.0: version "5.2.1" @@ -9527,13 +9762,6 @@ mimic-response@^3.1.0: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== -min-document@^2.19.0: - version "2.19.0" - resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" - integrity sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ== - dependencies: - dom-walk "^0.1.0" - min-indent@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" @@ -9579,6 +9807,13 @@ minimatch@^5.0.1: dependencies: brace-expansion "^2.0.1" +minimatch@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.1.tgz#8a555f541cf976c622daf078bb28f29fb927c253" + integrity sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w== + dependencies: + brace-expansion "^2.0.1" + minimist-options@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" @@ -9600,12 +9835,12 @@ minipass-collect@^1.0.2: dependencies: minipass "^3.0.0" -minipass-fetch@^2.0.3: - version "2.1.2" - resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-2.1.2.tgz#95560b50c472d81a3bc76f20ede80eaed76d8add" - integrity sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA== +minipass-fetch@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-3.0.3.tgz#d9df70085609864331b533c960fd4ffaa78d15ce" + integrity sha512-n5ITsTkDqYkYJZjcRWzZt9qnZKCT7nKCosJhHoj7S7zD+BP4jVbWs+odsniw5TA3E0sLomhTKOKjF86wf11PuQ== dependencies: - minipass "^3.1.6" + minipass "^5.0.0" minipass-sized "^1.0.3" minizlib "^2.1.2" optionalDependencies: @@ -9632,7 +9867,7 @@ minipass-sized@^1.0.3: dependencies: minipass "^3.0.0" -minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6: +minipass@^3.0.0: version "3.3.6" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== @@ -9644,6 +9879,11 @@ minipass@^5.0.0: resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== +"minipass@^5.0.0 || ^6.0.2": + version "6.0.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-6.0.2.tgz#542844b6c4ce95b202c0995b0a471f1229de4c81" + integrity sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w== + minizlib@^2.1.1, minizlib@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" @@ -9672,7 +9912,7 @@ mkdirp@^0.5.1, mkdirp@~0.5.1: dependencies: minimist "^1.2.6" -mkdirp@^1.0.3, mkdirp@^1.0.4: +mkdirp@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -9849,14 +10089,15 @@ node-gyp-build@^4.2.0, node-gyp-build@^4.2.2, node-gyp-build@^4.3.0: integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ== node-gyp@^9.0.0: - version "9.3.1" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.3.1.tgz#1e19f5f290afcc9c46973d68700cbd21a96192e4" - integrity sha512-4Q16ZCqq3g8awk6UplT7AuxQ35XN4R/yf/+wSAwcBUAjg7l58RTactWaP8fIDTi0FzI7YcVLujwExakZlfWkXg== + version "9.4.0" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.4.0.tgz#2a7a91c7cba4eccfd95e949369f27c9ba704f369" + integrity sha512-dMXsYP6gc9rRbejLXmTbVRYjAHw7ppswsKyMxuxJxxOHzluIO1rGp9TOQgjFJ+2MCqcOcQTOPB/8Xwhr+7s4Eg== dependencies: env-paths "^2.2.0" + exponential-backoff "^3.1.1" glob "^7.1.4" graceful-fs "^4.2.6" - make-fetch-happen "^10.0.3" + make-fetch-happen "^11.0.3" nopt "^6.0.0" npmlog "^6.0.0" rimraf "^3.0.2" @@ -10272,7 +10513,7 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-limit@^3.1.0: +p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== @@ -10293,6 +10534,13 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + p-map@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" @@ -10451,6 +10699,14 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-scurry@^1.7.0: + version "1.9.2" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.9.2.tgz#90f9d296ac5e37e608028e28a447b11d385b3f63" + integrity sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg== + dependencies: + lru-cache "^9.1.1" + minipass "^5.0.0 || ^6.0.2" + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -10516,7 +10772,7 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.3.1: +picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -10527,9 +10783,9 @@ pify@^4.0.1: integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== pirates@^4.0.1, pirates@^4.0.5: - version "4.0.5" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" - integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== pkg-dir@^3.0.0: version "3.0.0" @@ -10720,13 +10976,12 @@ postcss-less@^3.1.4: postcss "^7.0.14" postcss-loader@^7.0.1: - version "7.3.2" - resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-7.3.2.tgz#ac3344ad1f14bb65df135744b7efae4dbdad4301" - integrity sha512-c7qDlXErX6n0VT+LUsW+nwefVtTu3ORtVvK8EXuUIDcxo+b/euYqpuHlJAvePb0Af5e8uMjR/13e0lTuYifaig== + version "7.3.3" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-7.3.3.tgz#6da03e71a918ef49df1bb4be4c80401df8e249dd" + integrity sha512-YgO/yhtevGO/vJePCQmTxiaEwER94LABZN0ZMT4A0vsak9TpO+RvKRs7EmJ8peIlB9xfXCsS7M8LjqncsUZ5HA== dependencies: - cosmiconfig "^8.1.3" + cosmiconfig "^8.2.0" jiti "^1.18.2" - klona "^2.0.6" semver "^7.3.8" postcss-logical@^5.0.4: @@ -11041,26 +11296,6 @@ pretty-error@^4.0.0: lodash "^4.17.20" renderkid "^3.0.0" -pretty-format@^24.0.0, pretty-format@^24.3.0, pretty-format@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.9.0.tgz#12fac31b37019a4eea3c11aa9a959eb7628aa7c9" - integrity sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA== - dependencies: - "@jest/types" "^24.9.0" - ansi-regex "^4.0.0" - ansi-styles "^3.2.0" - react-is "^16.8.4" - -pretty-format@^25.1.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.5.0.tgz#7873c1d774f682c34b8d48b6743a2bf2ac55791a" - integrity sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ== - dependencies: - "@jest/types" "^25.5.0" - ansi-regex "^5.0.0" - ansi-styles "^4.0.0" - react-is "^16.12.0" - pretty-format@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" @@ -11080,6 +11315,15 @@ pretty-format@^27.0.2: ansi-styles "^5.0.0" react-is "^17.0.1" +pretty-format@^29.0.0, pretty-format@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.5.0.tgz#283134e74f70e2e3e7229336de0e4fce94ccde5a" + integrity sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw== + dependencies: + "@jest/schemas" "^29.4.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + pretty-ms@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-7.0.1.tgz#7d903eaab281f7d8e03c66f867e239dc32fb73e8" @@ -11102,11 +11346,6 @@ progress@^2.0.0, progress@^2.0.3: resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== -promise-inflight@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" - integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== - promise-retry@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" @@ -11132,7 +11371,7 @@ prop-types-exact@^1.2.0: object.assign "^4.1.0" reflect.ownkeys "^0.2.0" -prop-types@15.8.1, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@15.8.1, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -11218,10 +11457,10 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -punycode@1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" - integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw== +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== punycode@^2.1.0, punycode@^2.1.1: version "2.3.0" @@ -11276,16 +11515,18 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" +qs@^6.11.0: + version "6.11.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" + integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== + dependencies: + side-channel "^1.0.4" + querystring-es3@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" integrity sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA== -querystring@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" - integrity sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g== - querystringify@^2.1.1: version "2.2.0" resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" @@ -11376,15 +11617,13 @@ rc@1.2.8, rc@^1.2.7, rc@^1.2.8: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-dom@16.14.0: - version "16.14.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89" - integrity sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw== +react-dom@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" + integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" - prop-types "^15.6.2" - scheduler "^0.19.1" + scheduler "^0.23.0" react-draggable@4.4.5: version "4.4.5" @@ -11403,20 +11642,6 @@ react-event-listener@^0.6.6: prop-types "^15.6.0" warning "^4.0.1" -react-hot-loader@4.13.1: - version "4.13.1" - resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.13.1.tgz#979fd7598e27338b3faffae6ed01c65374dace5e" - integrity sha512-ZlqCfVRqDJmMXTulUGic4lN7Ic1SXgHAFw7y/Jb7t25GBgTR0fYAJ8uY4mrpxjRyWGWmqw77qJQGnYbzCvBU7g== - dependencies: - fast-levenshtein "^2.0.6" - global "^4.3.0" - hoist-non-react-statics "^3.3.0" - loader-utils "^2.0.3" - prop-types "^15.6.1" - react-lifecycles-compat "^3.0.4" - shallowequal "^1.1.0" - source-map "^0.7.3" - react-infinite-scroller@1.2.6: version "1.2.6" resolved "https://registry.yarnpkg.com/react-infinite-scroller/-/react-infinite-scroller-1.2.6.tgz#8b80233226dc753a597a0eb52621247f49b15f18" @@ -11464,7 +11689,7 @@ react-intl@^3.11.0: intl-messageformat-parser "^3.6.4" shallow-equal "^1.2.1" -react-is@^16.10.2, react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.4, react-is@^16.8.6: +react-is@^16.10.2, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.6: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -11474,6 +11699,11 @@ react-is@^17.0.0, react-is@^17.0.1, react-is@^17.0.2: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-is@^18.0.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== + react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" @@ -11519,6 +11749,11 @@ react-redux@7.2.9: prop-types "^15.7.2" react-is "^17.0.2" +react-refresh@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e" + integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ== + react-resize-detector@^8.0.4: version "8.1.0" resolved "https://registry.yarnpkg.com/react-resize-detector/-/react-resize-detector-8.1.0.tgz#1c7817db8bc886e2dbd3fbe3b26ea8e56be0524a" @@ -11637,14 +11872,12 @@ react-transition-group@^4.3.0: loose-envify "^1.4.0" prop-types "^15.6.2" -react@16.14.0: - version "16.14.0" - resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d" - integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g== +react@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" - prop-types "^15.6.2" read-config-file@6.2.0: version "6.2.0" @@ -12079,36 +12312,36 @@ ripemd160@2, ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -ripple-address-codec@^4.1.1, ripple-address-codec@^4.2.5: - version "4.2.5" - resolved "https://registry.yarnpkg.com/ripple-address-codec/-/ripple-address-codec-4.2.5.tgz#9d31b2066abd4cf1a135cd865b4e8e63269701e7" - integrity sha512-SZ96zZH+0REeyEcYVFl0vqcsGRXiFXS2RUgHupHhtVkOEk6men53vngVjJwBrSnY+oa6Cri15q1zSni3DEoxNw== +ripple-address-codec@^4.1.1, ripple-address-codec@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ripple-address-codec/-/ripple-address-codec-4.3.0.tgz#45edeb0312b4fe4607b37b7c4cff467802ad571d" + integrity sha512-Tvd81i7hpDmNqHvkj6iYlj8Tv3I1Romw5gfjni9eacewJvGV2xe+p2y0FAw39z72qfciRMhQyHvpnviBcWVBNw== dependencies: base-x "^3.0.9" create-hash "^1.1.2" ripple-binary-codec@^1.1.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/ripple-binary-codec/-/ripple-binary-codec-1.4.3.tgz#4737044f2aa5da496c1d57619339f26df01cd494" - integrity sha512-P4ALjAJWBJpRApTQO+dJCrHE6mZxm7ypZot9OS0a3RCKOWTReNw0pDWfdhCGh1qXh71TeQnAk4CHdMLwR/76oQ== + version "1.6.0" + resolved "https://registry.yarnpkg.com/ripple-binary-codec/-/ripple-binary-codec-1.6.0.tgz#848f93a10363a521f2a158751a873a9e89c25d15" + integrity sha512-fa0aMSbh1VOGEHIWCF/VuIvoMoQ/1HLJoBxm+oPNPIDyZJG1uRpLYph1pcvAlDuMutHM3ZHMzWjJpe3AaiMIUA== dependencies: assert "^2.0.0" big-integer "^1.6.48" - buffer "5.6.0" + buffer "6.0.3" create-hash "^1.2.0" decimal.js "^10.2.0" - ripple-address-codec "^4.2.5" + ripple-address-codec "^4.3.0" ripple-keypairs@^1.0.3: - version "1.1.5" - resolved "https://registry.yarnpkg.com/ripple-keypairs/-/ripple-keypairs-1.1.5.tgz#eabfc371f2ef293fdc462664e18cbba32c4f5c7e" - integrity sha512-wLJXIBsMVazn2Yp/7oP4PvgA4Gd1HtuZLftdEJFNOLgraf82phqa2AnNK3t9f3XeQnApW1jAe/FcFFOY6QUn5w== + version "1.3.0" + resolved "https://registry.yarnpkg.com/ripple-keypairs/-/ripple-keypairs-1.3.0.tgz#fb28f15d0c764e36af7b25c4c782c3997abf84ad" + integrity sha512-LzM3Up9Pwz3dYqnczzNptimN3AxtjeGbDGeiOzREzbkslKiZcJ615b/ghBN4H23SC6W1GAL95juEzzimDi4THw== dependencies: bn.js "^5.1.1" brorand "^1.0.5" elliptic "^6.5.4" hash.js "^1.0.3" - ripple-address-codec "^4.2.5" + ripple-address-codec "^4.3.0" ripple-lib-transactionparser@0.8.2: version "0.8.2" @@ -12168,9 +12401,9 @@ run-parallel@^1.1.9: queue-microtask "^1.2.2" runtypes@^6.5.1: - version "6.6.0" - resolved "https://registry.yarnpkg.com/runtypes/-/runtypes-6.6.0.tgz#48e353d8b0f641ab5ec5d80fa96dd7bd41ea3281" - integrity sha512-ddM7sgB3fyboDlBzEYFQ04L674sKjbs4GyW2W32N/5Ae47NRd/GyMASPC2PFw8drPHYGEcZ0mZ26r5RcB8msfQ== + version "6.7.0" + resolved "https://registry.yarnpkg.com/runtypes/-/runtypes-6.7.0.tgz#5ffc7e3a51142639d8a8e0155ef4bb8c2ae4c54b" + integrity sha512-3TLdfFX8YHNFOhwHrSJza6uxVBmBrEjnNQlNXvXCdItS0Pdskfg5vVXUTWIN+Y23QR09jWpSl99UHkA83m4uWA== rxjs@6: version "6.6.7" @@ -12266,6 +12499,13 @@ scheduler@^0.19.1: loose-envify "^1.1.0" object-assign "^4.1.1" +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== + dependencies: + loose-envify "^1.1.0" + schema-utils@^0.4.0: version "0.4.7" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187" @@ -12284,9 +12524,9 @@ schema-utils@^2.2.0, schema-utils@^2.6.5: ajv-keywords "^3.5.2" schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.2.tgz#36c10abca6f7577aeae136c804b0c741edeadc99" - integrity sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg== + version "3.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== dependencies: "@types/json-schema" "^7.0.8" ajv "^6.12.5" @@ -12430,11 +12670,6 @@ shallow-equal@^1.2.1: resolved "https://registry.yarnpkg.com/shallow-equal/-/shallow-equal-1.2.1.tgz#4c16abfa56043aa20d050324efa68940b0da79da" integrity sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA== -shallowequal@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" - integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== - sharedworker-loader@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/sharedworker-loader/-/sharedworker-loader-2.1.1.tgz#0bebdb9660f580e685326dac66ae79d76f01f215" @@ -12486,6 +12721,11 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.0.2.tgz#ff55bb1d9ff2114c13b400688fa544ac63c36967" + integrity sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q== + simple-concat@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" @@ -12617,7 +12857,7 @@ source-map-js@^1.0.2: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== -source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: +source-map-resolve@^0.5.0: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== @@ -12709,20 +12949,25 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== -ssri@^9.0.0: - version "9.0.1" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057" - integrity sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q== +ssri@^10.0.0: + version "10.0.4" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.4.tgz#5a20af378be586df139ddb2dfb3bf992cf0daba6" + integrity sha512-12+IR2CB2C28MMAw0Ncqwj5QbTcs0nGIhgJzYWzDkb21vWmfNI83KS4f3Ci6GI98WreIfG7o9UXp3C0qbpA8nQ== dependencies: - minipass "^3.1.1" + minipass "^5.0.0" -stack-utils@^2.0.2: +stack-utils@^2.0.2, stack-utils@^2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== dependencies: escape-string-regexp "^2.0.0" +stackframe@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" + integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== + stat-mode@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/stat-mode/-/stat-mode-1.0.0.tgz#68b55cb61ea639ff57136f36b216a291800d1465" @@ -12779,6 +13024,15 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -12788,14 +13042,14 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" string.prototype.matchall@^4.0.8: version "4.0.8" @@ -12861,6 +13115,13 @@ stringify-object@^3.2.1: is-obj "^1.0.1" is-regexp "^1.0.0" +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" @@ -12868,12 +13129,12 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1: dependencies: ansi-regex "^2.0.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== dependencies: - ansi-regex "^5.0.1" + ansi-regex "^6.0.1" strip-ansi@~0.1.0: version "0.1.1" @@ -13157,9 +13418,9 @@ terser-webpack-plugin@5.3.9, terser-webpack-plugin@^5.3.7: terser "^5.16.8" terser@^5.10.0, terser@^5.16.8: - version "5.17.7" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.17.7.tgz#2a8b134826fe179b711969fd9d9a0c2479b2a8c3" - integrity sha512-/bi0Zm2C6VAexlGgLlVxA0P2lru/sdLyfCVaRMfKVo9nWxbmz7f/sD8VPybPeSUJaJcwmCJis9pBIhcVcG1QcQ== + version "5.18.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.18.1.tgz#6d8642508ae9fb7b48768e48f16d675c89a78460" + integrity sha512-j1n0Ao919h/Ai5r43VAnfV/7azUYW43GPxK7qSATzrsERfW7+y2QW9Cp9ufnRF5CQUWbnLSo7UJokSWCqg4tsQ== dependencies: "@jridgewell/source-map" "^0.3.3" acorn "^8.8.2" @@ -13357,10 +13618,15 @@ tslib@^1.8.1, tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0, tslib@^2.5.0: - version "2.5.2" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.2.tgz#1b6f07185c881557b0ffa84b111a0106989e8338" - integrity sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA== +tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0: + version "2.5.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" + integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== + +tslib@^2.5.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== tsutils@^3.21.0: version "3.21.0" @@ -13534,17 +13800,17 @@ uniq@^1.0.1: resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" integrity sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA== -unique-filename@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2" - integrity sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A== +unique-filename@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea" + integrity sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g== dependencies: - unique-slug "^3.0.0" + unique-slug "^4.0.0" -unique-slug@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-3.0.0.tgz#6d347cf57c8a7a7a6044aabd0e2d74e4d76dc7c9" - integrity sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w== +unique-slug@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-4.0.0.tgz#6bae6bb16be91351badd24cdce741f892a6532e3" + integrity sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ== dependencies: imurmurhash "^0.1.4" @@ -13699,12 +13965,12 @@ url-parse@^1.5.3: requires-port "^1.0.0" url@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" - integrity sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ== + version "0.11.1" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.1.tgz#26f90f615427eca1b9f4d6a28288c147e2302a32" + integrity sha512-rWS3H04/+mzzJkv0eZ7vEDGiQbgquI1fGfOad6zKvgYQi1SzMmhl7c/DdRGxhaWrVH6z0qWITo8rpnxK/RfEhA== dependencies: - punycode "1.3.2" - querystring "0.2.0" + punycode "^1.4.1" + qs "^6.11.0" use@^3.1.0: version "3.1.1" @@ -13821,9 +14087,9 @@ vfile@^4.0.0: vfile-message "^2.0.0" victory-vendor@^36.6.8: - version "36.6.10" - resolved "https://registry.yarnpkg.com/victory-vendor/-/victory-vendor-36.6.10.tgz#e7e3646deaf0e850bc60dffdad6d7a4abee40632" - integrity sha512-7YqYGtsA4mByokBhCjk+ewwPhUfzhR1I3Da6/ZsZUv/31ceT77RKoaqrxRq5Ki+9we4uzf7+A+7aG2sfYhm7nA== + version "36.6.11" + resolved "https://registry.yarnpkg.com/victory-vendor/-/victory-vendor-36.6.11.tgz#acae770717c2dae541a54929c304ecab5ab6ac2a" + integrity sha512-nT8kCiJp8dQh8g991J/R5w5eE2KnO8EAIP0xocWlh9l2okngMWglOPoMZzJvek8Q1KUc4XE/mJxTZnvOB1sTYg== dependencies: "@types/d3-array" "^3.0.3" "@types/d3-ease" "^3.0.0" @@ -13859,11 +14125,6 @@ w3c-xmlserializer@^2.0.0: dependencies: xml-name-validator "^3.0.0" -wait-for-expect@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/wait-for-expect/-/wait-for-expect-3.0.2.tgz#d2f14b2f7b778c9b82144109c8fa89ceaadaa463" - integrity sha512-cfS1+DZxuav1aBYbaO/kE06EOS8yRw7qOFoD3XtjTkYvCvh3zUvNST8DXK/nPaeqIzIv3P3kL3lRJn8iwOiSag== - walkdir@^0.4.0, walkdir@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.4.1.tgz#dc119f83f4421df52e3061e514228a2db20afa39" @@ -14147,6 +14408,15 @@ worker-loader@^3.0.8: loader-utils "^2.0.0" schema-utils "^3.0.0" +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" @@ -14156,14 +14426,14 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" wrappy@1: version "1.0.2"