diff --git a/.github/workflows/coveralls.yml b/.github/workflows/coveralls.yml index 94139a4332b0..fc42c15c6c35 100644 --- a/.github/workflows/coveralls.yml +++ b/.github/workflows/coveralls.yml @@ -4,11 +4,15 @@ on: - master pull_request: types: [opened, synchronize, edited] -name: Coveralls +name: Reporter jobs: - build: - name: Reporter + test: + name: Run tests in parallel runs-on: Runner_16cores_Deriv-app + strategy: + matrix: + shard: [1,2,3,4,5,6,7,8,9,10] + fail-fast: true steps: - name: Checkout uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 @@ -16,9 +20,23 @@ jobs: uses: './.github/actions/setup_node' - name: Install dependencies uses: "./.github/actions/npm_install_from_cache" - - name: Build - uses: "./.github/actions/build" + - name: Build components package + working-directory: packages/components + run: npm run build - name: Test - run: JEST_MAX_WORKERS=95% npm run test:jest -- --collectCoverage - - name: Coveralls + run: JEST_MAX_WORKERS=95% SHARD_INDEX=${{ matrix.shard }} SHARD_COUNT=10 npm run test:shard -- --collectCoverage + - name: Coveralls Parallel uses: coverallsapp/github-action@3dfc5567390f6fa9267c0ee9c251e4c8c3f18949 + with: + flag-name: ${{ matrix.shard}} + parallel: true + + finish: + name: Coveralls Finished + needs: [test] + runs-on: Runner_16cores_Deriv-app + steps: + - name: Coveralls Finished + uses: coverallsapp/github-action@3dfc5567390f6fa9267c0ee9c251e4c8c3f18949 + with: + parallel-finished: true diff --git a/README.md b/README.md index b6fc695cd68a..ca939d499f0c 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This repository contains the various platforms of the Deriv application. [![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lerna.js.org/) ![Sonar Tech Debt](https://img.shields.io/sonar/tech_debt/binary-com_deriv-app?server=https%3A%2F%2Fsonarcloud.io) ![Sonar Violations (short format)](https://img.shields.io/sonar/violations/binary-com_deriv-app?server=https%3A%2F%2Fsonarcloud.io) -[![Coverage Status](https://coveralls.io/repos/github/binary-com/deriv-app/badge.svg?branch=master)](https://coveralls.io/github/binary-com/deriv-app?branch=master) +[![Coverage Status](https://coveralls.io/repos/github/deriv-com/deriv-app/badge.svg?branch=master)](https://coveralls.io/github/deriv-com/deriv-app?branch=master) **In this document**: diff --git a/package-lock.json b/package-lock.json index 00644d4e769b..5480489969c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@babel/preset-typescript": "^7.24.7", "@deriv-com/analytics": "1.26.2", + "@deriv/deriv-charts": "^2.7.1", "@sendbird/chat": "^4.9.7", "@types/react-transition-group": "^4.4.4", "babel-jest": "^29.7.0", @@ -3012,9 +3013,9 @@ } }, "node_modules/@deriv/deriv-charts": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@deriv/deriv-charts/-/deriv-charts-2.6.2.tgz", - "integrity": "sha512-t828MzqHl89zJQt7Qu4rxewiBQwBd6h80shebtZ8N4px+KACksJz0FBMiwUb9Pd7NQpkaxmUybfOJNlr1N+nQw==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@deriv/deriv-charts/-/deriv-charts-2.7.1.tgz", + "integrity": "sha512-r4ZLiF9AeCodC6gYpnAq7drEvKZsCWQspi+2FyMUsLWF5yz4g7fIvao+nsAkvECO6DeqFqBZH7k1nyTCxxBczA==", "dependencies": { "@types/lodash.set": "^4.3.7", "@welldone-software/why-did-you-render": "^3.3.8", @@ -50762,7 +50763,7 @@ "@deriv/api-types": "1.0.172", "@deriv/bot-skeleton": "^1.0.0", "@deriv/components": "^1.0.0", - "@deriv/deriv-charts": "^2.6.2", + "@deriv/deriv-charts": "^2.7.1", "@deriv/hooks": "^1.0.0", "@deriv/quill-icons": "1.23.3", "@deriv/shared": "^1.0.0", @@ -51158,7 +51159,7 @@ "@deriv/cfd": "^1.0.0", "@deriv/components": "^1.0.0", "@deriv/deriv-api": "^1.0.15", - "@deriv/deriv-charts": "^2.6.2", + "@deriv/deriv-charts": "^2.7.1", "@deriv/hooks": "^1.0.0", "@deriv/p2p": "^0.7.3", "@deriv/quill-design": "^1.3.2", @@ -51666,7 +51667,7 @@ "@deriv/api-types": "1.0.172", "@deriv/components": "^1.0.0", "@deriv/deriv-api": "^1.0.15", - "@deriv/deriv-charts": "^2.6.2", + "@deriv/deriv-charts": "^2.7.1", "@deriv/hooks": "^1.0.0", "@deriv/quill-icons": "1.23.3", "@deriv/shared": "^1.0.0", diff --git a/package.json b/package.json index 8eaa8a30b715..926e6c615009 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,7 @@ "test": "f () { npm run test:stylelint && npm run test:eslint-all && npm run test:jest ;}; f", "test:stylelint": "stylelint \"./packages/*/src/**/*.s(a|c)ss\"", "test:ci": "f () { npm run test:stylelint && npm run test:eslint-all ;}; f", + "test:shard": "jest --all --shard=${SHARD_INDEX}/${SHARD_COUNT} --maxWorkers=${JEST_MAX_WORKERS:-'50%'}", "test:jest": "jest --all --maxWorkers=${JEST_MAX_WORKERS:-'50%'}", "test:performance": "cd e2e-tests && jest -c ./jest.config.js --detectOpenHandles performance", "stylelint:fix": "stylelint \"./packages/*/src/**/*.s(a|c)ss\" --fix", diff --git a/packages/account/src/Configs/user-profile-validation-config.ts b/packages/account/src/Configs/user-profile-validation-config.ts index e16773fc3ac4..784f999e4c5a 100644 --- a/packages/account/src/Configs/user-profile-validation-config.ts +++ b/packages/account/src/Configs/user-profile-validation-config.ts @@ -1,7 +1,9 @@ -import { localize } from '@deriv-com/translations'; +import dayjs from 'dayjs'; import * as Yup from 'yup'; + +import { localize } from '@deriv-com/translations'; import { ValidationConstants } from '@deriv-com/utils'; -import dayjs from 'dayjs'; + import { TEmployeeDetailsTinValidationConfig } from '../Types'; const { @@ -25,6 +27,7 @@ type TINDepdendents = { */ is_tin_auto_set?: boolean; is_employment_status_tin_mandatory?: boolean; + is_required_for_tax_residence?: boolean; }; Yup.addMethod(Yup.string, 'validatePhoneNumberLength', function (message) { @@ -44,14 +47,15 @@ const makeTinOptional = ({ tin_skipped, is_tin_auto_set, is_employment_status_tin_mandatory, + is_required_for_tax_residence, }: TINDepdendents) => { const check_if_tin_skipped = tin_skipped && !is_tin_auto_set; if (is_real) { // Students and unemployed are not required to provide TIN to have a regulated MT5 jurisdiction - if (is_tin_auto_set && is_employment_status_tin_mandatory) { + if (is_tin_auto_set && !(is_employment_status_tin_mandatory && is_required_for_tax_residence)) { return true; } - return check_if_tin_skipped || !is_employment_status_tin_mandatory; + return check_if_tin_skipped || !(is_employment_status_tin_mandatory && is_required_for_tax_residence); } // Check For Virtual account if (is_mf) { @@ -95,6 +99,7 @@ export const getEmploymentAndTaxValidationSchema = ({ tin_skipped, is_tin_auto_set, is_employment_status_tin_mandatory, + is_required_for_tax_residence: Boolean(tin_config?.is_tin_mandatory), }), then: Yup.string().notRequired(), otherwise: Yup.string().required(localize('Tax identification number is required.')), diff --git a/packages/account/src/Constants/file-uploader.tsx b/packages/account/src/Constants/file-uploader.tsx index 8eca968cf3ad..2e870d9b57d5 100644 --- a/packages/account/src/Constants/file-uploader.tsx +++ b/packages/account/src/Constants/file-uploader.tsx @@ -25,7 +25,7 @@ export const getFileUploaderDescriptions = (page: string, is_eu?: boolean): TFil const proof_of_address_descriptions = { title: ( ), diff --git a/packages/account/src/Containers/Account/page-overlay-wrapper.tsx b/packages/account/src/Containers/Account/page-overlay-wrapper.tsx index 9e71d4e0260a..0f6d2e9e06fe 100644 --- a/packages/account/src/Containers/Account/page-overlay-wrapper.tsx +++ b/packages/account/src/Containers/Account/page-overlay-wrapper.tsx @@ -23,9 +23,10 @@ type PageOverlayWrapperProps = { */ const PageOverlayWrapper = observer(({ routes, subroutes }: PageOverlayWrapperProps) => { const history = useHistory(); - const { client, common } = useStore(); + const { client, common, ui } = useStore(); const { logout } = client; const { is_from_derivgo } = common; + const { setIsForcedToExitPnv } = ui; const { isDesktop } = useDevice(); const passkeysMenuCloseActionEventTrack = React.useCallback(() => { @@ -54,6 +55,11 @@ const PageOverlayWrapper = observer(({ routes, subroutes }: PageOverlayWrapperPr const selected_route = getSelectedRoute({ routes: subroutes, pathname: location.pathname }); const onClickLogout = async () => { + if (window.location.pathname.startsWith(shared_routes.phone_verification)) { + setIsForcedToExitPnv(true); + // Add a small delay to ensure state is updated before navigation because adding await doesn't work here + await new Promise(resolve => setTimeout(resolve, 0)); + } history.push(shared_routes.traders_hub); await logout(); }; diff --git a/packages/account/src/Containers/employment-tax-details-container/employment-tax-details-container.tsx b/packages/account/src/Containers/employment-tax-details-container/employment-tax-details-container.tsx index ff53accab6bf..e6526c16b5e5 100644 --- a/packages/account/src/Containers/employment-tax-details-container/employment-tax-details-container.tsx +++ b/packages/account/src/Containers/employment-tax-details-container/employment-tax-details-container.tsx @@ -1,19 +1,22 @@ import { useEffect, useMemo, useRef, useState } from 'react'; import { FormikValues, useFormikContext } from 'formik'; + +import { TinValidations } from '@deriv/api/types'; +import { Checkbox, useOnClickOutside } from '@deriv/components'; +import { useResidenceList } from '@deriv/hooks'; +import { getLegalEntityName } from '@deriv/shared'; +import { observer, useStore } from '@deriv/stores'; +import { Localize } from '@deriv-com/translations'; +import { useDevice } from '@deriv-com/ui'; + import { EmploymentStatusField, TaxIdentificationNumberField, TaxResidenceField, } from '../../Components/forms/form-fields'; import { isFieldImmutable } from '../../Helpers/utils'; -import { Checkbox, useOnClickOutside } from '@deriv/components'; -import { Localize } from '@deriv-com/translations'; -import { getLegalEntityName } from '@deriv/shared'; -import { useDevice } from '@deriv-com/ui'; -import { useResidenceList } from '@deriv/hooks'; + import './employment-tax-details-container.scss'; -import { TinValidations } from '@deriv/api/types'; -import { observer, useStore } from '@deriv/stores'; type TEmploymentTaxDetailsContainerProps = { editable_fields: string[]; @@ -41,7 +44,7 @@ const EmploymentTaxDetailsContainer = observer( const { is_virtual, account_settings, account_status } = client; - const { tin_employment_status_bypass } = tin_validation_config; + const { tin_employment_status_bypass, is_tin_mandatory } = tin_validation_config; const is_employment_status_mandatory = is_virtual ? true @@ -196,7 +199,10 @@ const EmploymentTaxDetailsContainer = observer( is_tin_popover_open={is_tin_popover_open} setIsTinPopoverOpen={setIsTinPopoverOpen} setIsTaxResidencePopoverOpen={setIsTaxResidencePopoverOpen} - required={(should_display_long_message && !values.tin_skipped) || is_tin_required} + required={ + (should_display_long_message && !values.tin_skipped) || + (is_tin_required && is_tin_mandatory) + } fieldFocused={ should_focus_fields && values.employment_status && diff --git a/packages/api-v2/src/APIProvider.tsx b/packages/api-v2/src/APIProvider.tsx index c4333c5f85fa..3ecb76ef7b4a 100644 --- a/packages/api-v2/src/APIProvider.tsx +++ b/packages/api-v2/src/APIProvider.tsx @@ -198,7 +198,11 @@ const APIProvider = ({ children }: PropsWithChildren) => { if (reconnect) { connectionRef.current = initializeConnection( () => { - reconnectTimerId = setTimeout(() => setReconnect(true), 500); + reconnectTimerId = setTimeout(() => { + if (isMounted.current) { + setReconnect(true); + } + }, 500); }, () => { if (!connectionRef.current) { diff --git a/packages/api-v2/src/hooks/useTradingPlatformStatus.ts b/packages/api-v2/src/hooks/useTradingPlatformStatus.ts index d312ee18e79c..9e817647f3c2 100644 --- a/packages/api-v2/src/hooks/useTradingPlatformStatus.ts +++ b/packages/api-v2/src/hooks/useTradingPlatformStatus.ts @@ -1,4 +1,5 @@ import { CFD_PLATFORMS } from '@deriv/shared'; +import { useCallback } from 'react'; import useAuthorizedQuery from '../useAuthorizedQuery'; import useQuery from '../useQuery'; @@ -24,14 +25,19 @@ const useTradingPlatformStatus = () => { * @param platform The platform identifier (e.g., 'ctrader', 'dxtrade', 'mt5'). * @returns The status of the identified platform ('active', 'maintenance', 'unavailable'). */ - const getPlatformStatus = (platform: string) => { - const platformStatus = - platform === CFD_PLATFORMS.MT5 || platform === CFD_PLATFORMS.DXTRADE || platform === CFD_PLATFORMS.CTRADER - ? tradingPlatformStatusData?.find((status: TPlatformStatus) => status.platform === platform)?.status - : undefined; // cashier may pass non-cfd platform (i.e. doughflow, p2p, paymentagent, etc) which doesn't have status property + const getPlatformStatus = useCallback( + (platform: string) => { + const platformStatus = + platform === CFD_PLATFORMS.MT5 || + platform === CFD_PLATFORMS.DXTRADE || + platform === CFD_PLATFORMS.CTRADER + ? tradingPlatformStatusData?.find((status: TPlatformStatus) => status.platform === platform)?.status + : undefined; // cashier may pass non-cfd platform (i.e. doughflow, p2p, paymentagent, etc) which doesn't have status property - return platformStatus; - }; + return platformStatus; + }, + [tradingPlatformStatusData] + ); return { ...rest, diff --git a/packages/api/src/hooks/useRemoteConfig.ts b/packages/api/src/hooks/useRemoteConfig.ts index 62f14a2af520..03fe79e85ce4 100644 --- a/packages/api/src/hooks/useRemoteConfig.ts +++ b/packages/api/src/hooks/useRemoteConfig.ts @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { ObjectUtils } from '@deriv-com/utils'; import initData from '../remote_config.json'; @@ -17,14 +17,23 @@ const remoteConfigQuery = async function () { function useRemoteConfig(enabled = false) { const [data, setData] = useState(initData); + const isMounted = useRef(false); useEffect(() => { - enabled && + isMounted.current = true; + + return () => { + isMounted.current = false; + }; + }, []); + + useEffect(() => { + if (enabled) { remoteConfigQuery() .then(async res => { const resHash = await ObjectUtils.hashObject(res); const dataHash = await ObjectUtils.hashObject(data); - if (resHash !== dataHash) { + if (resHash !== dataHash && isMounted.current) { setData(res); } }) @@ -32,7 +41,8 @@ function useRemoteConfig(enabled = false) { // eslint-disable-next-line no-console console.log('Remote Config error: ', error); }); - }, [enabled]); + } + }, [enabled, data]); return { data }; } diff --git a/packages/appstore/jest.config.js b/packages/appstore/jest.config.js index 30f0e20d7639..50e9aefcc5e4 100644 --- a/packages/appstore/jest.config.js +++ b/packages/appstore/jest.config.js @@ -7,6 +7,7 @@ module.exports = { '\\.s(c|a)ss$': '/../../__mocks__/styleMock.js', '^.+\\.svg$': '/../../__mocks__/fileMock.js', '@deriv-com/translations': '/../../__mocks__/translation.mock.js', + '^@deriv/account$': '/../account/src', '@deriv-com/ui': '/../../__mocks__/deriv-com.ui.mock.js', '^Assets/(.*)$': '/src/assets/$1', '^Components/(.*)$': '/src/components/$1', diff --git a/packages/appstore/src/components/cfds-listing/__tests__/index.spec.tsx b/packages/appstore/src/components/cfds-listing/__tests__/index.spec.tsx index d95e9f924781..76714f55b0c8 100644 --- a/packages/appstore/src/components/cfds-listing/__tests__/index.spec.tsx +++ b/packages/appstore/src/components/cfds-listing/__tests__/index.spec.tsx @@ -55,33 +55,74 @@ mockUseTradingPlatformStatus.mockReturnValue({ getPlatformStatus: jest.fn(), }); describe('CFDsListing', () => { - const mock = mockStore({ - traders_hub: { - selected_region: 'Non-EU', - has_any_real_account: true, - is_real: true, - no_MF_account: true, - is_demo_low_risk: true, - }, - client: { - is_landing_company_loaded: true, - real_account_creation_unlock_date: '2022-02-02', - }, - modules: { - cfd: { - toggleCompareAccountsModal: jest.fn(), - setAccountType: jest.fn(), - current_list: {}, + it('should not render the component when cfd accounts are not supported', () => { + const mock = mockStore({ + traders_hub: { + selected_region: 'Non-EU', + has_any_real_account: true, + is_real: true, + no_MF_account: true, + is_demo_low_risk: true, + available_dxtrade_accounts: [], + available_ctrader_accounts: [], + combined_cfd_mt5_accounts: [], }, - }, + client: { + is_landing_company_loaded: true, + real_account_creation_unlock_date: '2022-02-02', + }, + modules: { + cfd: { + toggleCompareAccountsModal: jest.fn(), + setAccountType: jest.fn(), + current_list: {}, + }, + }, + }); + const wrapper = ({ children }: { children: JSX.Element }) => ( + {children} + ); + + render(, { wrapper }); + expect(screen.queryByTestId('listing-container')).not.toBeInTheDocument(); }); - it('should render the component', () => { + it('should render the component when cfd accounts are supported', () => { + const mock = mockStore({ + traders_hub: { + selected_region: 'Non-EU', + has_any_real_account: true, + is_real: true, + no_MF_account: true, + is_demo_low_risk: true, + available_dxtrade_accounts: [], + available_ctrader_accounts: [], + combined_cfd_mt5_accounts: [ + { + landing_company_short: 'svg', + product: 'financial', + status: 'proof_failed', + login: '123', + }, + ], + }, + client: { + is_landing_company_loaded: true, + real_account_creation_unlock_date: '2022-02-02', + }, + modules: { + cfd: { + toggleCompareAccountsModal: jest.fn(), + setAccountType: jest.fn(), + current_list: {}, + }, + }, + }); const wrapper = ({ children }: { children: JSX.Element }) => ( {children} ); render(, { wrapper }); - expect(screen.getByTestId('listing-container')).toBeInTheDocument(); + expect(screen.queryByTestId('listing-container')).toBeInTheDocument(); }); }); diff --git a/packages/appstore/src/components/cfds-listing/index.tsx b/packages/appstore/src/components/cfds-listing/index.tsx index 6ceb219f5a8b..adc9978a680b 100644 --- a/packages/appstore/src/components/cfds-listing/index.tsx +++ b/packages/appstore/src/components/cfds-listing/index.tsx @@ -190,7 +190,15 @@ const CFDsListing = observer(() => { updateMT5AccountDetails(); }, [is_landing_company_loaded, is_populating_mt5_account_list]); - const is_mt5_list_loading = !is_landing_company_loaded || is_populating_mt5_account_list || is_switching; + const is_cfd_accounts_supported = + combined_cfd_mt5_accounts.length || available_dxtrade_accounts.length || available_ctrader_accounts.length; + + const is_mt5_list_loaded = is_landing_company_loaded && !is_populating_mt5_account_list && !is_switching; + + if (is_mt5_list_loaded && !is_cfd_accounts_supported) { + return null; + } + return ( { {has_svg_accounts_to_migrate && is_landing_company_loaded && } - {!is_mt5_list_loading ? ( + {is_mt5_list_loaded && combined_cfd_mt5_accounts.length ? ( {/* MT5 */} {combined_cfd_mt5_accounts.map((existing_account, index: number) => { diff --git a/packages/appstore/src/constants/wallet-migration-video-translations.ts b/packages/appstore/src/constants/wallet-migration-video-translations.ts index 6658a00c553d..cf69f3adf9f1 100644 --- a/packages/appstore/src/constants/wallet-migration-video-translations.ts +++ b/packages/appstore/src/constants/wallet-migration-video-translations.ts @@ -1,7 +1,7 @@ export const WALLET_MIGRATION_VIDEO_TRANSLATIONS = { AR: 'e52c6a3b483e287e39e983791f50e592', EN: '25df7df0d0af48090b086cd6f103d8f3', - // ES: 'ef6e04a732ebf193106e62c4d9307637', TODO: Uncomment this when Spanish translations are ready + ES: 'ef6e04a732ebf193106e62c4d9307637', FR: 'e444c765e24eaad80dcb1549d1018c0f', RU: '932be9817e60bdaae87d657d609b38d2', // TODO: Add translations for other languages diff --git a/packages/appstore/src/modules/traders-hub/index.tsx b/packages/appstore/src/modules/traders-hub/index.tsx index 372ca27a8891..da84cfc37332 100644 --- a/packages/appstore/src/modules/traders-hub/index.tsx +++ b/packages/appstore/src/modules/traders-hub/index.tsx @@ -57,7 +57,14 @@ const TradersHub = observer(() => { } = client; const { is_eu_demo, is_eu_real } = useContentFlag(); - const { selected_platform_type, setTogglePlatformType, is_eu_user } = traders_hub; + const { + selected_platform_type, + setTogglePlatformType, + is_eu_user, + combined_cfd_mt5_accounts, + available_ctrader_accounts, + available_dxtrade_accounts, + } = traders_hub; const traders_hub_ref = React.useRef(null); const can_show_notify = @@ -95,9 +102,12 @@ const TradersHub = observer(() => { setTogglePlatformType(event.target.value); }; if (!is_logged_in) return null; + const is_cfd_accounts_supported = + combined_cfd_mt5_accounts.length || available_dxtrade_accounts.length || available_ctrader_accounts.length; + const should_show_cfd_section = !!(is_mt5_allowed && is_cfd_accounts_supported); const getOrderedPlatformSections = () => { - if (is_mt5_allowed) { + if (should_show_cfd_section) { return ( { const desktopContent = !is_landing_company_loaded ? ( ) : ( - + ); const mobileTabletContent = ( {is_landing_company_loaded ? ( - is_mt5_allowed && ( + should_show_cfd_section && ( { ) : ( )} - {is_landing_company_loaded && !is_mt5_allowed && ( + {is_landing_company_loaded && !should_show_cfd_section && (
@@ -151,8 +161,8 @@ const TradersHub = observer(() => {
diff --git a/packages/appstore/webpack.config.js b/packages/appstore/webpack.config.js index aeb44ebe458a..a8ebd67a36d1 100644 --- a/packages/appstore/webpack.config.js +++ b/packages/appstore/webpack.config.js @@ -45,6 +45,7 @@ const default_plugins = [ new Dotenv(), new DefinePlugin({ 'process.env.TRUSTPILOT_API_KEY': JSON.stringify(process.env.TRUSTPILOT_API_KEY), + 'process.env.REMOTE_CONFIG_URL': JSON.stringify(process.env.REMOTE_CONFIG_URL), }), new IgnorePlugin({ resourceRegExp: /^\.\/locale$/, contextRegExp: /moment$/ }), new CircularDependencyPlugin({ exclude: /node_modules/, failOnError: true }), diff --git a/packages/bot-skeleton/src/scratch/hooks/workspace_svg.js b/packages/bot-skeleton/src/scratch/hooks/workspace_svg.js index 511e7406d683..7aa138afd32d 100644 --- a/packages/bot-skeleton/src/scratch/hooks/workspace_svg.js +++ b/packages/bot-skeleton/src/scratch/hooks/workspace_svg.js @@ -79,20 +79,30 @@ Blockly.WorkspaceSvg.prototype.addBlockNode = function (block_node) { const { flyout } = DBotStore.instance; const block = Blockly.Xml.domToBlock(block_node, flyout.getFlyout().targetWorkspace); const top_blocks = this.getTopBlocks(true); - const new_block = flyout.getFlyout().createBlock(false, block); + + if (config.single_instance_blocks.includes(block.type)) { + this.getAllBlocks().forEach(ws_block => { + if (ws_block.type === block.type && ws_block.id !== block.id) { + ws_block.dispose(); + } + }); + } if (top_blocks.length) { const last_block = top_blocks[top_blocks.length - 1]; const last_block_xy = last_block.getRelativeToSurfaceXY(); const extra_spacing = last_block.startHat_ ? Blockly.BlockSvg.START_HAT_HEIGHT : 0; const y = last_block_xy.y + last_block.getHeightWidth().height + extra_spacing + 30; - new_block.moveBy(last_block_xy.x, y); + block.moveBy(last_block_xy.x, y); } + flyout.setIsSearchFlyout(false); + flyout.setVisibility(false); + // Call svgResize to avoid glitching workspace. - Blockly.svgResize(new_block.workspace); + Blockly.svgResize(block.workspace); // kept this commented since it is making a glitching issue, - //this.centerOnBlock(new_block.id, false); + // this.centerOnBlock(new_block.id, false); }; /** diff --git a/packages/bot-web-ui/package.json b/packages/bot-web-ui/package.json index f1ea1c17897f..1d045cd2a377 100644 --- a/packages/bot-web-ui/package.json +++ b/packages/bot-web-ui/package.json @@ -77,7 +77,7 @@ "@deriv/api-types": "1.0.172", "@deriv/bot-skeleton": "^1.0.0", "@deriv/components": "^1.0.0", - "@deriv/deriv-charts": "^2.6.2", + "@deriv/deriv-charts": "^2.7.1", "@deriv/hooks": "^1.0.0", "@deriv/quill-icons": "1.23.3", "@deriv/shared": "^1.0.0", diff --git a/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/__tests__/quick-strategy.spec.tsx b/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/__tests__/quick-strategy.spec.tsx index 8ab4634fbf48..219765461e4f 100644 --- a/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/__tests__/quick-strategy.spec.tsx +++ b/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/__tests__/quick-strategy.spec.tsx @@ -252,18 +252,6 @@ describe('', () => { }); }); - it('It should submit the form', async () => { - render(, { - wrapper, - }); - - await waitFor(async () => { - await userEvent.click(screen.getByTestId('qs-run-button')); - }); - - expect(mock_DBot_store?.quick_strategy?.is_open).toBeFalsy(); - }); - it('It should close the form on close button click', async () => { render(, { wrapper, diff --git a/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/form-wrappers/__tests__/desktop-form-wrapper.spec.tsx b/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/form-wrappers/__tests__/desktop-form-wrapper.spec.tsx index bfecda4430fe..1fac13ab1054 100644 --- a/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/form-wrappers/__tests__/desktop-form-wrapper.spec.tsx +++ b/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/form-wrappers/__tests__/desktop-form-wrapper.spec.tsx @@ -82,7 +82,13 @@ describe('', () => { it('renders the DesktopFormWrapper component', () => { const { container } = render( - +
test
, { @@ -93,9 +99,15 @@ describe('', () => { expect(container).toBeInTheDocument(); }); - it('should close the form', () => { + it('should close the form', async () => { render( - +
test
, { @@ -105,16 +117,22 @@ describe('', () => { expect(mock_DBot_store?.quick_strategy.is_open).toBeTruthy(); const close_button = screen.getByTestId('qs-desktop-close-button'); - userEvent.click(close_button); - userEvent.type(close_button, '{enter}'); - userEvent.keyboard('{Enter}'); + await userEvent.click(close_button); + await userEvent.type(close_button, '{enter}'); + await userEvent.keyboard('{Enter}'); expect(onClickClose).toBeCalled(); }); - it('should change the selected strategy', () => { + it('should change qs modal view on strategy click', async () => { mock_DBot_store?.quick_strategy.setSelectedStrategy(quick_strategy_content[0].qs_name); render( - +
test
, { @@ -123,16 +141,22 @@ describe('', () => { ); expect(mock_DBot_store?.quick_strategy.selected_strategy).toBe(quick_strategy_content[0].qs_name); - const strategy = screen.getByText(STRATEGIES.D_ALEMBERT.label); - userEvent.click(strategy); - const disabledTab = screen.getByText(FORM_TABS[0].label); - userEvent.click(disabledTab); - expect(mock_DBot_store?.quick_strategy.selected_strategy).toBe(quick_strategy_content[1].qs_name); + const strategy = screen.getByText(STRATEGIES.MARTINGALE.label); + await userEvent.click(strategy); + + const run_button = screen.getByText('Run'); + expect(run_button).toBeInTheDocument(); }); - it('should submit the form on edit', async () => { + it('should submit the form on click of load', async () => { render( - +
test
, { @@ -140,14 +164,20 @@ describe('', () => { } ); expect(mock_DBot_store?.quick_strategy.is_open).toBeTruthy(); - const edit_button = screen.getByText('Edit'); - userEvent.click(edit_button); + const edit_button = screen.getByText('Load'); + await userEvent.click(edit_button); await waitFor(() => expect(mock_onSubmit).toBeCalled()); }); - it('should submit the form', async () => { + it('should submit the form on click of run', async () => { render( - +