From 9cc2fb2b1129a6628239d68ccb13da90dcedd589 Mon Sep 17 00:00:00 2001 From: George Usynin <103181646+heorhi-deriv@users.noreply.github.com> Date: Thu, 22 Jun 2023 13:32:25 +0300 Subject: [PATCH] align with feature branch (#11) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * synchronize amount inputs, add transfer button (WALL-554) with test link (#8962) * feat: :sparkles: account transfer component (demo) * fix: transfer hint message * fix: :art: apply comments * refactor: :art: remove reducer, simplify transfer logic, improve readability (#34) * fix: update padding of app-icon component (#33) * style: improve styles for account list * style: apply comments * fix: apply comments, refactor account modal in desktop * fix: :art: apply comments * feat: :sparkles: synchronize amount inputs, add transfer button * perf: refactor code * refactor: remove listener, add onScroll to mobile_dialog, remove ref from mobile_dialog * fix: apply comments * fix: reset amount_to if selected transfer_from is a wallet * feat: :sparkles: account transfer component (demo) * fix: transfer hint message * fix: :art: apply comments * refactor: :art: remove reducer, simplify transfer logic, improve readability (#34) * fix: update padding of app-icon component (#33) * style: improve styles for account list * style: apply comments * fix: apply comments, refactor account modal in desktop * fix: :art: apply comments * feat: add scrolling behaviour to account list in mobile view * fix: apply comments, fix test * refactor: :fire: remove mobile dialog, refactor scrolling, remove redundant logic * fix: name visibility after closing the modal * perf: improve transition behaviour * test: fix tests * refactor: :fire: remove mobile dialog, refactor scrolling, remove redundant logic * fix: add missing code * refactor: apply comments * fix: apply comments, refactor tests * test: :rocket: add tests for transfer related components * refactor: roll back Tab component * style: fix style for merged icon * fix: mock loginid * fix: mock account loginid * fix: typo, demo icon * fix: typo, demo icon, transition on closing, blinking icons --------- Co-authored-by: Hamid * chore: disable PT (#9021) * Fix: leave confirm tests failed (#9030) * fix: leave-confirm tests failed * refactor: modify usestate spyOn function * refactor: add more coverage * Adrienne / Release automation integration (#8914) * feat: added release automation workflow * chore: added config for first merge * chore: renamed first merge delay config * feat: added configuration to skip updating base branch * feat: added option for maximum tasks * chore: skip slack integration * chore: enabled github integration * chore: updated fe-toolbox version * chore: removed platform input * chore: updated refetch limit * chore: added gitguardian to skip checks * chore: fixed syntax issue * chore: added new lines at bottom * fix: fix wrong branch name (#9037) * henry/91713/test: Test coverage for POO statuses components (#8125) * test: Test coverage for POO statuses components * fix: Empty-Commit --------- Co-authored-by: Ali(Ako) Hosseini * henry/91709/test: add test coverage for poi unsupported (#8041) * test: add test coverage * test: switch from regex to string * fix: change test coverage --------- Co-authored-by: Henry Hein Co-authored-by: Ali(Ako) Hosseini * henry/dtra-131/feat: dropdown style and description change for all trade types (#8727) * feat: dropdown style and description change for all trade types * fix: minify svg * refactor: resolve edge cases * fix: remove comment * fix: resolve comments * fix: add animation, make image scrollable * refactor: refactor for better maintainability * fix: circleci issue * fix: disable animation for now * fix: indexing to contract type value and change view height for mobile * fix: remove outerscroll * fix: trying auto size * fix: autosizer didnt work * fix: make words bold * fix: fix bold * fix: remove navigation and pagnation * fix: refactor * fix: resolve comments * empty commit * empty commit * fix: rearrange glossary based on order * fix: animation bug * fix: disable swipe on mobile and animation issue * fix: remove commented out code * fix: scroll not working for trade-types * fix: test * fix: remove unintended test file * Kate / DTRA-202 / Apply new style to the trade type menu in mobile (#8931) * fix: remove special style from trade type menu in mobile * chore: change trade type text * fix: remove Change password text for DMT5 (#8927) * fix: remove Change password text for DMT5 * chore: empty commit --------- Co-authored-by: Sandeep Rajput <90243468+sandeep-deriv@users.noreply.github.com> Co-authored-by: Ali(Ako) Hosseini * henry/91133/refactor: Refactored, migrated to TS and added test cases (#7998) * refactor: Refactored, migrated to TS and added test cases * fix: circleCI issue * fix: circle CI issue * fix: codecov rerun commit * fix: all test cases covered * fix: make props reusable * Merge branch 'master' of github.com:binary-com/deriv-app into henry/91133/Refactor-ServicesErrorModal --------- Co-authored-by: Henry Hein Co-authored-by: Ali(Ako) Hosseini * chore: increase workers for jest (#8954) * chore: increase workers for jest * ci: change codecov command * ci: update codecov command * ci: update codecov command * ci: reduce number of workers for codecov * ci: change workers to 4 for codecov * ci: remove duplicate command * ci: update default value --------- Co-authored-by: Sandeep Rajput <90243468+sandeep-deriv@users.noreply.github.com> * farabi/bot-324/remove-security-and-privacy-from-dbot-footer (#8825) * fix: removed security and privacy from dbot footer * fix: removed icons commit --------- Co-authored-by: Sandeep Rajput <90243468+sandeep-deriv@users.noreply.github.com> * Jim/FEQ-147/e2e-test-cases-for-traders-hub-dashboard (#8649) * feat: initial e2e * chore: remove package-lock * chore: restore original code * chore: use chromium as the only browser for testing * docs: update description for the command to show the test report in dev mode * ci: add command to show the tests report in dev mode * test: update test case to use count * refactor: extract text to constant variables and add whitespace --------- Co-authored-by: Ali(Ako) Hosseini Co-authored-by: Sandeep Rajput <90243468+sandeep-deriv@users.noreply.github.com> * refactor 🔧: Aum / WALL-297 / deposit fiat module (#8938) * feat: created deposit-fiat-module * chore: applied changes from comments * refactor: moved dark-mode logic from deposit-fiat-iframe to useDepositFiatAddress hook * feat: added test for useDepositFiatAddress hook * feat: added test cases for light and dark theme for iframe url * chore: removed unused import for Real.tsx in Deposit.tsx * chore: removed test case for Real.tsx in deposit.spec.tsx * chore: minor change * chore: empty-commit --------- Co-authored-by: Ali(Ako) Hosseini * farabi/bot-124/update-stop-loss-and-take-profit-content (#8953) * fix: update content of take profit and stop loss * chore: redeploy * chore: redeploy * fix: adjust backward compatibility to support legacy bots (#8925) * chore: Rename the Other CFDS on Onboarding and Trader Hubs Homepage (#8975) * chore: Rename the Other CFDS on Onboarding and Trader Hubs Homepage * chore: empty commit * fix: update eu disclaimer percentage (#8361) * Suisin/chore: add thai language into deriv app (#8766) * chore: add thai language into deriv app * chore: added Thai language check for Thai --------- Co-authored-by: Jim Daniels Wasswa <104334373+jim-deriv@users.noreply.github.com> Co-authored-by: vinu-deriv <100689171+vinu-deriv@users.noreply.github.com> * farabi/bot-315/dbot-to-deriv-bot (#8955) * fix: rebrand dbot to deriv bot * chore: added test case for tour trigger dialog * feat: added wallets notifications to wallets (#8837) * feat: added wallets notifications to wallets * fix: remove commentted code * fix: show notifications for platforms only * fix: change label on CTA * fix: added hooks package * fix: update branch * translations: 📚 sync translations with crowdin (#9058) Co-authored-by: DerivFE <80095553+DerivFE@users.noreply.github.com> * henry/fix: dtrader translation issue (#9062) * fix: dtrader translation issue * fix: translation issues * translations: 📚 sync translations with crowdin (#9063) Co-authored-by: DerivFE <80095553+DerivFE@users.noreply.github.com> * translations: 📚 sync translations with crowdin (#9065) Co-authored-by: DerivFE <80095553+DerivFE@users.noreply.github.com> * translations: 📚 sync translations with crowdin (#9066) Co-authored-by: DerivFE <80095553+DerivFE@users.noreply.github.com> * test: add tests --------- Co-authored-by: Hamid Co-authored-by: yashim-deriv Co-authored-by: Farhan Ahmad Nurzi <125247833+farhan-nurzi-deriv@users.noreply.github.com> Co-authored-by: Nijil Nirmal Co-authored-by: adrienne-deriv <103016120+adrienne-deriv@users.noreply.github.com> Co-authored-by: henry-deriv <118344354+henry-deriv@users.noreply.github.com> Co-authored-by: Ali(Ako) Hosseini Co-authored-by: Henry Hein Co-authored-by: kate-deriv <121025168+kate-deriv@users.noreply.github.com> Co-authored-by: shontzu <108507236+shontzu-deriv@users.noreply.github.com> Co-authored-by: Sandeep Rajput <90243468+sandeep-deriv@users.noreply.github.com> Co-authored-by: Jim Daniels Wasswa <104334373+jim-deriv@users.noreply.github.com> Co-authored-by: Farabi <102643568+farabi-deriv@users.noreply.github.com> Co-authored-by: Aum Bhatt <125039206+aum-deriv@users.noreply.github.com> Co-authored-by: Shafin Al Karim <129021108+shafin-deriv@users.noreply.github.com> Co-authored-by: Muhammad Hamza <120543468+hamza-deriv@users.noreply.github.com> Co-authored-by: mahdiyeh-deriv <82078941+mahdiyeh-deriv@users.noreply.github.com> Co-authored-by: Sui Sin <103026762+suisin-deriv@users.noreply.github.com> Co-authored-by: vinu-deriv <100689171+vinu-deriv@users.noreply.github.com> Co-authored-by: Aizad Ridzo <103104395+aizad-deriv@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: DerivFE <80095553+DerivFE@users.noreply.github.com> --- .github/workflows/automationrc.json | 20 + .github/workflows/codecov.yml | 2 +- .../merge_and_release_to_staging.yml | 53 + end-to-end-test/.env.example | 6 +- end-to-end-test/README.md | 14 +- end-to-end-test/package-lock.json | 82 - end-to-end-test/package.json | 10 +- end-to-end-test/playwright.config.ts | 80 +- .../tests/change-endpoint/change-endpoint.tsx | 15 +- .../tests/onboarding/onboarding.ts | 31 +- .../tests/traders-hub/dashboard-demo.spec.tsx | 13 + .../tests/traders-hub/dashboard-real.spec.tsx | 43 + end-to-end-test/utils/constants.ts | 1 + end-to-end-test/utils/helpers.ts | 10 + end-to-end-test/utils/index.ts | 5 + package.json | 3 +- .../__tests__/leave-confirm.spec.tsx | 90 +- .../__tests__/unsupported-failed.spec.tsx | 21 + .../__tests__/not-required.spec.tsx | 24 + .../poo-rejected/__tests__/rejected.spec.tsx | 22 + .../poo/statuses/poo-rejected/index.js | 4 +- .../poo/statuses/poo-rejected/rejected.jsx | 2 +- .../__tests__/submitted.spec.tsx | 22 + .../poo-verified/__tests__/verified.spec.tsx | 19 + .../Security/Passwords/passwords-platform.jsx | 6 - packages/appstore/package.json | 1 + .../src/components/cfds-listing/index.tsx | 2 +- .../src/components/main-title-bar/index.tsx | 11 +- .../onboarding-new/static-dashboard.tsx | 2 +- .../wallet-transfer/wallet-transfer.scss | 13 +- .../wallet-transfer/wallet-transfer.tsx | 174 +- .../src/scratch/backward-compatibility.js | 14 +- .../Trade Definition/multiplier_stop_loss.js | 8 +- .../multiplier_take_profit.js | 8 +- packages/bot-web-ui/src/app/app-content.jsx | 10 +- .../bot-footer-extensions.scss | 18 - .../bot-footer-extensions.tsx | 36 - .../components/bot-footer-extensions/index.ts | 4 - .../security-and-privacy.tsx | 13 - .../dashboard/bot-builder/toolbar/toolbar.tsx | 4 +- .../dashboard/bot-builder/toolbox/toolbox.tsx | 2 +- .../__tests__/dashboard-component.spec.tsx | 1 + .../dashboard-component/constants.ts | 6 +- .../components/dashboard/joyride-config.tsx | 2 +- .../dashboard/tour-trigger-dialog.spec.tsx | 31 + .../dashboard/tour-trigger-dialog.tsx | 4 +- .../dashboard/tutorial-tab/guide-content.tsx | 2 +- .../tutorial-tab/tutorial-content.tsx | 36 +- packages/bot-web-ui/src/components/index.js | 1 - packages/bot-web-ui/src/stores/app-store.js | 6 +- .../bot-web-ui/src/stores/run-panel-store.js | 6 +- .../bot-web-ui/src/utils/bot-notifications.js | 2 +- .../help-strings/before_purchase.js | 2 +- .../help-strings/notify_telegram.js | 2 +- .../page-container/page-container.scss | 1 + .../__test__/deposit-fiat-iframe.spec.tsx | 55 + .../deposit-fiat-iframe.scss | 6 + .../deposit-fiat-iframe.tsx | 32 + .../components/deposit-fiat-iframe/index.ts | 1 + .../modules/deposit-fiat/components/index.ts | 1 + .../src/modules/deposit-fiat/deposit-fiat.tsx | 11 + .../cashier/src/modules/deposit-fiat/index.ts | 1 + .../pages/deposit/__tests__/deposit.spec.tsx | 49 - .../cashier/src/pages/deposit/deposit.tsx | 5 +- .../components/amount-input/amount-input.tsx | 4 + .../src/components/carousel/carousel.tsx | 4 +- .../acuity-download-modal.tsx | 4 +- .../Containers/app-notification-messages.jsx | 4 +- .../Stores/Helpers/client-notifications.js | 5 +- .../core/src/Stores/notification-store.js | 60 +- .../Utils/Language/__tests__/language.spec.js | 2 +- .../__tests__/useDepositFiatAddress.spec.tsx | 60 + packages/hooks/src/index.ts | 1 + packages/hooks/src/useDepositFiatAddress.ts | 25 + packages/shared/src/styles/themes.scss | 2 + packages/stores/src/mockStore.ts | 1 - packages/stores/types.ts | 1 - .../__tests__/services-error-modal.spec.tsx | 87 + .../Modals/ServicesErrorModal/index.js | 2 +- .../services-error-modal.jsx | 58 - .../services-error-modal.tsx | 72 + .../trade_explanations/img-accumulator.svg | 2 +- .../trade_explanations/img-even-odd.svg | 2 +- .../trade_explanations/img-high-low.svg | 2 +- .../trade_explanations/img-match-diff.svg | 2 +- .../trade_explanations/img-multiplier.svg | 2 +- .../trade_explanations/img-over-under.svg | 2 +- .../trade_explanations/img-rise-fall.svg | 2 +- .../trade_explanations/img-touch.svg | 2 +- .../trade_explanations/img-vanilla.svg | 2 +- .../accumulator-trade-description.jsx | 59 - .../accumulator-trade-description.tsx | 40 + .../Trading/Categories/trade-categories.jsx | 54 +- .../contract-type-glossary.tsx | 155 + .../ContractTypeInfo/contract-type-info.jsx | 124 +- .../ContractTypeMenu/contract-type-menu.jsx | 46 +- .../__tests__/contract-type-glossary.spec.tsx | 25 + .../ContractType/contract-type-dialog.jsx | 4 +- .../components/contract-type-dialog.scss | 15 +- .../components/contract-type-info.scss | 137 +- .../components/contract-type-list.scss | 3 - .../trader/src/sass/app/modules/trading.scss | 6 - packages/translations/crowdin/messages.json | 2 +- packages/translations/src/i18next/i18next.ts | 4 +- .../translations/src/translations/ach.json | 114 +- .../translations/src/translations/ar.json | 114 +- .../translations/src/translations/bn.json | 114 +- .../translations/src/translations/de.json | 114 +- .../translations/src/translations/es.json | 114 +- .../translations/src/translations/fr.json | 114 +- .../translations/src/translations/id.json | 114 +- .../translations/src/translations/it.json | 114 +- .../translations/src/translations/ko.json | 114 +- .../translations/src/translations/pl.json | 114 +- .../translations/src/translations/pt.json | 4870 +++++++++-------- .../translations/src/translations/ru.json | 128 +- .../translations/src/translations/si.json | 114 +- .../translations/src/translations/th.json | 156 +- .../translations/src/translations/tr.json | 214 +- .../translations/src/translations/vi.json | 1240 ++--- .../translations/src/translations/zh_cn.json | 114 +- .../translations/src/translations/zh_tw.json | 114 +- 122 files changed, 5835 insertions(+), 4520 deletions(-) create mode 100644 .github/workflows/automationrc.json create mode 100644 .github/workflows/merge_and_release_to_staging.yml delete mode 100644 end-to-end-test/package-lock.json create mode 100644 end-to-end-test/tests/traders-hub/dashboard-demo.spec.tsx create mode 100644 end-to-end-test/tests/traders-hub/dashboard-real.spec.tsx create mode 100644 end-to-end-test/utils/constants.ts create mode 100644 end-to-end-test/utils/helpers.ts create mode 100644 end-to-end-test/utils/index.ts create mode 100644 packages/account/src/Components/poi-unsupported-failed/__tests__/unsupported-failed.spec.tsx create mode 100644 packages/account/src/Components/poo/statuses/poo-not-required/__tests__/not-required.spec.tsx create mode 100644 packages/account/src/Components/poo/statuses/poo-rejected/__tests__/rejected.spec.tsx create mode 100644 packages/account/src/Components/poo/statuses/poo-submitted/__tests__/submitted.spec.tsx create mode 100644 packages/account/src/Components/poo/statuses/poo-verified/__tests__/verified.spec.tsx delete mode 100644 packages/bot-web-ui/src/components/bot-footer-extensions/bot-footer-extensions.scss delete mode 100644 packages/bot-web-ui/src/components/bot-footer-extensions/bot-footer-extensions.tsx delete mode 100644 packages/bot-web-ui/src/components/bot-footer-extensions/index.ts delete mode 100644 packages/bot-web-ui/src/components/bot-footer-extensions/security-and-privacy.tsx create mode 100644 packages/bot-web-ui/src/components/dashboard/tour-trigger-dialog.spec.tsx create mode 100644 packages/cashier/src/modules/deposit-fiat/components/deposit-fiat-iframe/__test__/deposit-fiat-iframe.spec.tsx create mode 100644 packages/cashier/src/modules/deposit-fiat/components/deposit-fiat-iframe/deposit-fiat-iframe.scss create mode 100644 packages/cashier/src/modules/deposit-fiat/components/deposit-fiat-iframe/deposit-fiat-iframe.tsx create mode 100644 packages/cashier/src/modules/deposit-fiat/components/deposit-fiat-iframe/index.ts create mode 100644 packages/cashier/src/modules/deposit-fiat/components/index.ts create mode 100644 packages/cashier/src/modules/deposit-fiat/deposit-fiat.tsx create mode 100644 packages/cashier/src/modules/deposit-fiat/index.ts create mode 100644 packages/hooks/src/__tests__/useDepositFiatAddress.spec.tsx create mode 100644 packages/hooks/src/useDepositFiatAddress.ts create mode 100644 packages/trader/src/App/Components/Elements/Modals/ServicesErrorModal/__tests__/services-error-modal.spec.tsx delete mode 100644 packages/trader/src/App/Components/Elements/Modals/ServicesErrorModal/services-error-modal.jsx create mode 100644 packages/trader/src/App/Components/Elements/Modals/ServicesErrorModal/services-error-modal.tsx delete mode 100644 packages/trader/src/Assets/Trading/Categories/accumulator-trade-description.jsx create mode 100644 packages/trader/src/Assets/Trading/Categories/accumulator-trade-description.tsx create mode 100644 packages/trader/src/Modules/Trading/Components/Form/ContractType/ContractTypeInfo/contract-type-glossary.tsx create mode 100644 packages/trader/src/Modules/Trading/Components/Form/ContractType/__tests__/contract-type-glossary.spec.tsx diff --git a/.github/workflows/automationrc.json b/.github/workflows/automationrc.json new file mode 100644 index 000000000000..e1d3421b3f1b --- /dev/null +++ b/.github/workflows/automationrc.json @@ -0,0 +1,20 @@ +{ + "skip_pending_checks": false, + "skip_slack_integration": false, + "skip_updating_branch": true, + "merge_delay": 120000, + "first_merge_delay": 1200000, + "max_task_count": 15, + "pull_request": { + "checks_timeout": 60000, + "refetch_timeout": 10000, + "refetch_limit": 20, + "checks_limit": 40 + }, + "circleci": { + "project_slug": "gh/binary-com/deriv-app", + "branch": "master", + "workflow_name": "release_staging" + }, + "checks_to_skip": ["/gitguardian/i"] +} diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index cdddb1e6ac62..bfad532d50de 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -21,7 +21,7 @@ jobs: npm install npm run bootstrap npm run build:all - npm run test:jest + npm run test:jest 4 - name: Upload coverage to Codecov uses: codecov/codecov-action@v3.1.1 diff --git a/.github/workflows/merge_and_release_to_staging.yml b/.github/workflows/merge_and_release_to_staging.yml new file mode 100644 index 000000000000..8530aac262df --- /dev/null +++ b/.github/workflows/merge_and_release_to_staging.yml @@ -0,0 +1,53 @@ +name: Merge and release to staging +permissions: + pull-requests: write +on: + workflow_dispatch: + inputs: + tag: + description: "Tag" + required: true + skip_circleci_checks: + description: "Skip checking CircleCI workflow" + type: boolean + required: false + skip_pending_checks: + description: "Skip waiting for pull request checks" + type: boolean + required: false + +concurrency: + group: release_automation_group + +jobs: + release_issues: + permissions: write-all + runs-on: ubuntu-latest + timeout-minutes: 600 + steps: + - name: Checkout to repo + uses: actions/checkout@v3 + with: + ref: master + - name: Setup node + uses: actions/setup-node@v2 + - name: Wait for logs to accumulate + run: | + sleep 10 + - name: Release issues in Deriv.app + uses: binary-com/fe-toolbox@production_V20230615_0 + with: + tag: ${{ inputs.tag }} + platform: 'Deriv.app' + list_id: ${{ secrets.LIST_ID }} + release_tags_list_id: ${{ secrets.RELEASE_TAGS_LIST_ID }} + regression_testing_template_id: ${{ secrets.REGRESSION_TESTING_TEMPLATE_ID }} + config_path: ./.github/workflows/automationrc.json + skip_circleci_checks: ${{ inputs.skip_circleci_checks }} + skip_pending_checks: ${{ inputs.skip_pending_checks }} + CIRCLECI_TOKEN: ${{ secrets.CIRCLECI_TOKEN }} + CLICKUP_API_TOKEN: ${{ secrets.CLICKUP_API_TOKEN }} + SLACK_APP_TOKEN: ${{ secrets.SLACK_APP_TOKEN }} + SLACK_USER_TOKEN: ${{ secrets.SLACK_USER_TOKEN }} + SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} + GITHUB_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} diff --git a/end-to-end-test/.env.example b/end-to-end-test/.env.example index fbde2a85f1bc..063f3a2b06a2 100644 --- a/end-to-end-test/.env.example +++ b/end-to-end-test/.env.example @@ -9,4 +9,8 @@ ENDPOINT= APP_URL= QA_EMAIL_INBOX_USER_NAME= QA_EMAIL_INBOX_PASSWORD = -ACCOUNT_RESIDENCE= +ACCOUNT_RESIDENCE_HIGH_RISK= +ACCOUNT_CITIZENSHIP_HIGH_RISK= +ACCOUNT_RESIDENCE_LOW_RISK= +ACCOUNT_CITIZENSHIP_LOW_RISK= +RISK_LEVEL=low_risk # This stores the risk level that can be either high_risk or low_risk diff --git a/end-to-end-test/README.md b/end-to-end-test/README.md index e3a5f4d520b8..8bd0390d5226 100644 --- a/end-to-end-test/README.md +++ b/end-to-end-test/README.md @@ -13,7 +13,11 @@ To run the End to End test you need to first install playwright on your machine then -`npx plauwright test` to start the tests to run. (You also can pass `--debug` option to this command to it to run them in headless browser and check them visually separated by different browsers eg. Chromium, Firefox or Edge) +`npx playwright test` to start the tests to run. (You also can pass `--debug` option to this command to it to run them in headless browser and check them visually separated by different browsers eg. Chromium, Firefox or Edge) + +`npm run test:e2e` to run the tests from within the root `directory`. + +`npm run test:e2e-dev` to run the tests from within the root `directory` with trace enabled and show the test report. ## Project structure @@ -30,8 +34,12 @@ It's mandatory to create this file to pass the needed environment variables to t | 5 | `ENDPOINT` | Endpoint of qabox server | String | \* | | 6 | `APP_URL` | App URL which tests should run on. Local machine URL is `localhost.binary.sx` | String | \* | | 7 | `QA_EMAIL_INBOX_USER_NAME` | Username of qabox events page to retrive the signup email and enable the created account (You can find it in the LP under shared-fe folder with the `QA emails login creds` entry name) | String | \* | -| 8 | QA_EMAIL_INBOX_PASSWORD | Password of qabox events page to retrive the signup email and enable the created account (You can find it in the LP under shared-fe folder with the `QA emails login creds` entry name) | String | \* | -| 9 | ACCOUNT_RESIDENCE | Account residence to create account using it | String | \* | +| 8 | `QA_EMAIL_INBOX_PASSWORD` | Password of qabox events page to retrive the signup email and enable the created account (You can find it in the LP under shared-fe folder with the `QA emails login creds` entry name) | String | \* | +| 9 | `ACCOUNT_RESIDENCE_HIGH_RISK` | Account residence to use when creating high risk accounts it | String | \* | +| 10 | `ACCOUNT_RESIDENCE_LOW_RISK` | Account residence to use when creating low risk accounts | String | \* | +| 11 | `ACCOUNT_CITIZENSHIP_HIGH_RISK` | Account citizenship to use when creating high risk accounts | String | \* | +| 12 | `ACCOUNT_CITIZENSHIP_LOW_RISK` | Account citizenship to use when creating low risk accounts | String | \* | +| 13 | `RISK_LEVEL` | Risk level for the particular account i.e. high risk or low risk | String | \* | ### `onboarding.ts` file diff --git a/end-to-end-test/package-lock.json b/end-to-end-test/package-lock.json deleted file mode 100644 index 0cc2edb9fb1d..000000000000 --- a/end-to-end-test/package-lock.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "name": "end-to-end-test", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "end-to-end-test", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "@playwright/test": "^1.29.2", - "dotenv": "^16.0.3" - } - }, - "node_modules/@playwright/test": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.29.2.tgz", - "integrity": "sha512-+3/GPwOgcoF0xLz/opTnahel1/y42PdcgZ4hs+BZGIUjtmEFSXGg+nFoaH3NSmuc7a6GSFwXDJ5L7VXpqzigNg==", - "dependencies": { - "@types/node": "*", - "playwright-core": "1.29.2" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@types/node": { - "version": "18.11.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", - "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" - }, - "node_modules/dotenv": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", - "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", - "engines": { - "node": ">=12" - } - }, - "node_modules/playwright-core": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.29.2.tgz", - "integrity": "sha512-94QXm4PMgFoHAhlCuoWyaBYKb92yOcGVHdQLoxQ7Wjlc7Flg4aC/jbFW7xMR52OfXMVkWicue4WXE7QEegbIRA==", - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=14" - } - } - }, - "dependencies": { - "@playwright/test": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.29.2.tgz", - "integrity": "sha512-+3/GPwOgcoF0xLz/opTnahel1/y42PdcgZ4hs+BZGIUjtmEFSXGg+nFoaH3NSmuc7a6GSFwXDJ5L7VXpqzigNg==", - "requires": { - "@types/node": "*", - "playwright-core": "1.29.2" - } - }, - "@types/node": { - "version": "18.11.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", - "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" - }, - "dotenv": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", - "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" - }, - "playwright-core": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.29.2.tgz", - "integrity": "sha512-94QXm4PMgFoHAhlCuoWyaBYKb92yOcGVHdQLoxQ7Wjlc7Flg4aC/jbFW7xMR52OfXMVkWicue4WXE7QEegbIRA==" - } - } -} diff --git a/end-to-end-test/package.json b/end-to-end-test/package.json index 28dc61792369..ca933001abb8 100644 --- a/end-to-end-test/package.json +++ b/end-to-end-test/package.json @@ -8,8 +8,14 @@ "keywords": [], "author": "", "license": "ISC", + "eslintConfig": { + "rules": { + "testing-library/no-await-sync-query": "off", + "testing-library/prefer-screen-queries": "off" + } + }, "dependencies": { - "@playwright/test": "^1.29.2", - "dotenv": "^16.0.3" + "dotenv": "^16.0.3", + "@playwright/test": "^1.33.0" } } diff --git a/end-to-end-test/playwright.config.ts b/end-to-end-test/playwright.config.ts index 2ac5de51acc4..01f7c939eb17 100644 --- a/end-to-end-test/playwright.config.ts +++ b/end-to-end-test/playwright.config.ts @@ -58,49 +58,49 @@ const config: PlaywrightTestConfig = { }, }, - { - name: 'firefox', - use: { - ignoreHTTPSErrors: true, - ...devices['Desktop Firefox'], - }, - }, + // { + // name: 'firefox', + // use: { + // ignoreHTTPSErrors: true, + // ...devices['Desktop Firefox'], + // }, + // }, - { - name: 'webkit', - use: { - ignoreHTTPSErrors: true, - ...devices['Desktop Safari'], - }, - }, + // { + // name: 'webkit', + // use: { + // ignoreHTTPSErrors: true, + // ...devices['Desktop Safari'], + // }, + // }, - /* Test against mobile viewports. */ - // { - // name: 'Mobile Chrome', - // use: { - // ...devices['Pixel 5'], - // }, - // }, - // { - // name: 'Mobile Safari', - // use: { - // ...devices['iPhone 12'], - // }, - // }, + // /* Test against mobile viewports. */ + // // { + // // name: 'Mobile Chrome', + // // use: { + // // ...devices['Pixel 5'], + // // }, + // // }, + // // { + // // name: 'Mobile Safari', + // // use: { + // // ...devices['iPhone 12'], + // // }, + // // }, - /* Test against branded browsers. */ - // { - // name: 'Microsoft Edge', - // use: { - // channel: 'msedge', - // }, - // }, - // { - // name: 'Google Chrome', - // use: { - // channel: 'chrome', - // }, - // }, + // /* Test against branded browsers. */ + // // { + // // name: 'Microsoft Edge', + // // use: { + // // channel: 'msedge', + // // }, + // // }, + // // { + // // name: 'Google Chrome', + // // use: { + // // channel: 'chrome', + // // }, + // // }, ], /* Folder for test artifacts such as screenshots, videos, traces, etc. */ diff --git a/end-to-end-test/tests/change-endpoint/change-endpoint.tsx b/end-to-end-test/tests/change-endpoint/change-endpoint.tsx index 026957bc237e..2ffea42a3803 100644 --- a/end-to-end-test/tests/change-endpoint/change-endpoint.tsx +++ b/end-to-end-test/tests/change-endpoint/change-endpoint.tsx @@ -17,26 +17,25 @@ export default class ChangeEndpoint { await expect(this.page).toHaveTitle('Trader | Deriv'); await this.cookieDialogHandler(); await this.page.goto(`${process.env.APP_URL!}/endpoint`); - await this.page.waitForSelector( - '#app_contents > .dc-themed-scrollbars > form > .dc-input:nth-child(2) > .dc-input__field' + '#app_contents > .dc-themed-scrollbars > form > .dc-input:nth-child(2) > .dc-input__container > .dc-input__field' ); await this.page.click( - '#app_contents > .dc-themed-scrollbars > form > .dc-input:nth-child(2) > .dc-input__field' + '#app_contents > .dc-themed-scrollbars > form > .dc-input:nth-child(2) > .dc-input__container > .dc-input__field' ); await this.page.waitForSelector( - '#app_contents > .dc-themed-scrollbars > form > .dc-input:nth-child(2) > .dc-input__field' + '#app_contents > .dc-themed-scrollbars > form > .dc-input:nth-child(2) > .dc-input__container > .dc-input__field' ); await this.page.click( - '#app_contents > .dc-themed-scrollbars > form > .dc-input:nth-child(2) > .dc-input__field' + '#app_contents > .dc-themed-scrollbars > form > .dc-input:nth-child(2) > .dc-input__container > .dc-input__field' ); await this.page.waitForSelector( - '#app_contents > .dc-themed-scrollbars > form > .dc-input:nth-child(2) > .dc-input__field' + '#app_contents > .dc-themed-scrollbars > form > .dc-input:nth-child(2) > .dc-input__container > .dc-input__field' ); const first_input_selector = - '#app_contents > .dc-themed-scrollbars > form > .dc-input:nth-child(2) > .dc-input__field'; + '#app_contents > .dc-themed-scrollbars > form > .dc-input:nth-child(2) > .dc-input__container > .dc-input__field'; await this.page.click(first_input_selector, { clickCount: 3 }); await this.page.keyboard.press('Backspace'); @@ -44,7 +43,7 @@ export default class ChangeEndpoint { await this.page.type(first_input_selector, process.env.ENDPOINT!); const second_input_selector = - '#app_contents > .dc-themed-scrollbars > form > .dc-input:nth-child(3) > .dc-input__field'; + '#app_contents > .dc-themed-scrollbars > form > .dc-input:nth-child(3) > .dc-input__container > .dc-input__field'; await this.page.click(second_input_selector, { clickCount: 3 }); await this.page.keyboard.press('Backspace'); await this.page.type(second_input_selector, process.env.APPID!); diff --git a/end-to-end-test/tests/onboarding/onboarding.ts b/end-to-end-test/tests/onboarding/onboarding.ts index e1c26513eb0d..0fa449f06e47 100644 --- a/end-to-end-test/tests/onboarding/onboarding.ts +++ b/end-to-end-test/tests/onboarding/onboarding.ts @@ -1,5 +1,5 @@ import { Page, expect, chromium } from '@playwright/test'; -import suspend from '../../utils/suspend/suspend'; +import { suspend } from '../../utils'; import ChangeEndpoint from '../change-endpoint/change-endpoint'; export default class OnboardingFlow { @@ -76,7 +76,7 @@ export default class OnboardingFlow { for await (const item of hrefs) { await mailPage.goto(item); if (await mailPage.getByText(this.email).isVisible()) { - const element = await mailPage.locator('a', { hasText: 'signup' }); + const element = await mailPage.locator('a', { hasText: 'redirect?action=signup' }); const val = await element.getAttribute('href'); if (val) await this.page.goto(val); await mailPage.close(); @@ -84,13 +84,28 @@ export default class OnboardingFlow { break; } } - await this.page.waitForSelector('#dt_core_set-residence-form_signup-residence-select'); - await this.page.click('#dt_core_set-residence-form_signup-residence-select'); - await expect(this.page.getByText(process.env.ACCOUNT_RESIDENCE!)).toBeVisible(); - await this.page.getByText(process.env.ACCOUNT_RESIDENCE!).click(); + + this.page.waitForSelector('#dt_core_set-residence-form_signup-residence-select').then(async () => { + await this.page.click('#dt_core_set-residence-form_signup-residence-select'); + }); + + const RISK_LEVEL = process.env.RISK_LEVEL; + + const ACCOUNT_CITIZENSHIP = + (RISK_LEVEL === 'low_risk' + ? process.env.ACCOUNT_CITIZENSHIP_LOW_RISK + : process.env.ACCOUNT_CITIZENSHIP_HIGH_RISK) || ''; + + const ACCOUNT_RESIDENCE = + (RISK_LEVEL === 'low_risk' + ? process.env.ACCOUNT_RESIDENCE_LOW_RISK + : process.env.ACCOUNT_RESIDENCE_HIGH_RISK) || ''; + + await expect(this.page.getByText(ACCOUNT_RESIDENCE)).toBeVisible(); + await this.page.getByText(ACCOUNT_RESIDENCE).click(); await this.page.click('#dt_core_set-citizenship-form_signup-citizenship-select'); - await expect(this.page.getByText(process.env.ACCOUNT_CITIZENSHIP!)).toBeVisible(); - await this.page.getByText(process.env.ACCOUNT_CITIZENSHIP!).click(); + await expect(this.page.getByText(ACCOUNT_CITIZENSHIP)).toBeVisible(); + await this.page.getByText(ACCOUNT_CITIZENSHIP).click(); await this.page.getByRole('dialog').getByRole('button', { name: 'Next' }).click(); await expect(this.page.getByText(/Keep your account secure/)).toBeVisible(); await this.page.locator('#dt_core_account-signup-modal_account-signup-password-field'); diff --git a/end-to-end-test/tests/traders-hub/dashboard-demo.spec.tsx b/end-to-end-test/tests/traders-hub/dashboard-demo.spec.tsx new file mode 100644 index 000000000000..101b39872100 --- /dev/null +++ b/end-to-end-test/tests/traders-hub/dashboard-demo.spec.tsx @@ -0,0 +1,13 @@ +import { test, expect } from '@playwright/test'; +import { TRADERS_HUB_URL, switchAccountType } from '../../utils'; + +test.describe("Trader's Hub Dashboard", () => { + test.beforeEach(async ({ page }) => { + await page.goto(TRADERS_HUB_URL); + }); + test('It should switch from Demo to Real', async ({ page }) => { + await switchAccountType(page, 'Demo', 'Real'); + const get_deriv_account_button = await page.getByText('Get a Deriv account').first(); + expect(get_deriv_account_button).toBeDefined(); + }); +}); diff --git a/end-to-end-test/tests/traders-hub/dashboard-real.spec.tsx b/end-to-end-test/tests/traders-hub/dashboard-real.spec.tsx new file mode 100644 index 000000000000..46e30c3ce60c --- /dev/null +++ b/end-to-end-test/tests/traders-hub/dashboard-real.spec.tsx @@ -0,0 +1,43 @@ +import { test, expect } from '@playwright/test'; +import { TRADERS_HUB_URL, switchAccountType } from '../../utils'; + +test.describe("Trader's Hub Dashboard", () => { + const HIGH_RISK_MULTIPLIER_TEXT = 'text=Multipliers trading platform.'; + const LOW_RISK_MULTIPLIER_TEXT = 'text=Options and multipliers trading platform.'; + test.beforeEach(async ({ page }) => { + await page.goto(TRADERS_HUB_URL); + await switchAccountType(page, 'Demo', 'Real'); + }); + + test('It should switch from Real to Demo', async ({ page }) => { + await switchAccountType(page, 'Real', 'Demo'); + const demo_count = await page.getByText(/Demo/).count(); + expect(demo_count).toBeGreaterThanOrEqual(1); + }); + + test('It should have the regulator switcher for low risk and no regulator switcher for high risk', async ({ + page, + }) => { + const regulator_switcher = await page.getByText('Regulation:').count(); + if (process.env.RISK_LEVEL === 'low_risk') { + expect(regulator_switcher).toEqual(1); + } else { + expect(regulator_switcher).toEqual(0); + } + }); + + test('It should switch to EU for low risk', async ({ page }) => { + if (process.env.RISK_LEVEL === 'low_risk') { + await page.getByText('EU', { exact: true }).first().click(); + expect(await page.locator(HIGH_RISK_MULTIPLIER_TEXT)).toBeDefined(); + } + }); + + test('It should show Non-EU content for low risk and EU content for high risk on load', async ({ page }) => { + if (process.env.RISK_LEVEL === 'low_risk') { + expect(await page.locator(LOW_RISK_MULTIPLIER_TEXT)).toBeDefined(); + } else { + expect(await page.locator(HIGH_RISK_MULTIPLIER_TEXT)).toBeDefined(); + } + }); +}); diff --git a/end-to-end-test/utils/constants.ts b/end-to-end-test/utils/constants.ts new file mode 100644 index 000000000000..18c6a8333b43 --- /dev/null +++ b/end-to-end-test/utils/constants.ts @@ -0,0 +1 @@ +export const TRADERS_HUB_URL = `${process.env.APP_URL}/appstore/traders-hub`; diff --git a/end-to-end-test/utils/helpers.ts b/end-to-end-test/utils/helpers.ts new file mode 100644 index 000000000000..dd264f3d51fe --- /dev/null +++ b/end-to-end-test/utils/helpers.ts @@ -0,0 +1,10 @@ +import { Page } from '@playwright/test'; + +export const switchAccountType = async (page: Page, from_type: string, to_type: string) => { + await page.waitForLoadState('domcontentloaded'); + await page.waitForSelector('.account-type-dropdown--parent', { timeout: 0 }); + await page.waitForSelector(`.account-type-dropdown--${from_type.toLowerCase()}`, { timeout: 0 }); + await page.getByTestId('dti_dropdown_display').getByText(from_type).click({ timeout: 0 }); + await page.locator(`#${to_type.toLocaleLowerCase()}`).click(); + await page.waitForLoadState('domcontentloaded'); +}; diff --git a/end-to-end-test/utils/index.ts b/end-to-end-test/utils/index.ts new file mode 100644 index 000000000000..0d04acd1c841 --- /dev/null +++ b/end-to-end-test/utils/index.ts @@ -0,0 +1,5 @@ +import { TRADERS_HUB_URL } from './constants'; +import { switchAccountType } from './helpers'; +import suspend from './suspend/suspend'; + +export { TRADERS_HUB_URL, switchAccountType, suspend }; diff --git a/package.json b/package.json index 3de21d5c4267..260f7ae54e7e 100644 --- a/package.json +++ b/package.json @@ -93,8 +93,9 @@ "start": "f () { lerna exec --scope=@deriv/${1:-'*'} -- npm run start ;}; f", "test": "f () { bash ./scripts/circleci-config.test.sh && npm run test:stylelint && npm run test:eslint-all && npm run test:jest ;}; f", "test:stylelint": "stylelint \"./packages/*/src/**/*.s(a|c)ss\"", - "test:jest": "jest --all --maxWorkers=2", + "test:jest": "jest --all --maxWorkers=${1:-8}", "test:e2e": "cd end-to-end-test && npx playwright test", + "test:e2e-dev": "cd end-to-end-test && npx playwright test --trace on && npx playwright show-report", "test:performance": "cd e2e_tests && jest -c ./jest.config.js --maxWorkers=2 --detectOpenHandles performance", "stylelint:fix": "stylelint \"./packages/*/src/**/*.s(a|c)ss\" --fix", "translate": "f () { lerna exec --scope @deriv/translations -- npm run translate ;}; f", diff --git a/packages/account/src/Components/leave-confirm/__tests__/leave-confirm.spec.tsx b/packages/account/src/Components/leave-confirm/__tests__/leave-confirm.spec.tsx index 8a7c750463e2..cd8ea5e0ead6 100644 --- a/packages/account/src/Components/leave-confirm/__tests__/leave-confirm.spec.tsx +++ b/packages/account/src/Components/leave-confirm/__tests__/leave-confirm.spec.tsx @@ -1,12 +1,13 @@ import React from 'react'; -import { render, screen, fireEvent, waitFor } from '@testing-library/react'; -import LeaveConfirm, { TransitionBlockerWithRouter } from '../leave-confirm'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { Formik } from 'formik'; -import { Router } from 'react-router'; import { createBrowserHistory } from 'history'; +import { Router } from 'react-router'; import { isMobile } from '@deriv/shared'; +import LeaveConfirm, { TransitionBlocker } from '../leave-confirm'; -let modal_root_el; +let modal_root_el: HTMLElement; jest.mock('@deriv/shared', () => ({ ...jest.requireActual('@deriv/shared'), @@ -23,12 +24,34 @@ afterAll(() => { document.body.removeChild(modal_root_el); }); +const mock_set_show = jest.fn(); +const mock_set_next_location = jest.fn(); + +const get_leave_confirm_states = () => { + return jest + .spyOn(React, 'useState') + .mockImplementationOnce(() => [null, jest.fn()]) + .mockImplementationOnce(() => [true, mock_set_show]) + .mockImplementationOnce(() => [{ pathname: '/' }, mock_set_next_location]); +}; + +const get_transition_blocker_states = ({ pathname }: { pathname: string | null }) => { + return jest + .spyOn(React, 'useState') + .mockImplementationOnce(() => [true, mock_set_show]) + .mockImplementationOnce(() => [pathname ? { pathname: '/' } : null, mock_set_next_location]); +}; + +const on_dirty = jest.fn(); + const LeaveConfirmComponent = () => { const history = createBrowserHistory(); return ( - - + + {() => { + return ; + }} ); @@ -45,25 +68,24 @@ const withRouter = Component => { return WrapperComponent; }; -const TransitionBlockerComponent = withRouter(TransitionBlockerWithRouter); +const TransitionBlockerComponent = withRouter(TransitionBlocker); describe('LeaveConfirm', () => { it('should render LeaveConfirm component in desktop mode', () => { - jest.spyOn(React, 'useState').mockReturnValueOnce([true, () => null]); + get_leave_confirm_states(); render(); expect( screen.getByText('You have unsaved changes. Are you sure you want to discard changes and leave this page?') ).toBeInTheDocument(); }); it('should render LeaveConfirm component in mobile mode', () => { - jest.spyOn(React, 'useState').mockImplementationOnce(() => React.useState(true)); - isMobile.mockReturnValueOnce(true); + get_leave_confirm_states(); + (isMobile as jest.Mock).mockReturnValueOnce(true); render(); expect(screen.getByText('Unsaved changes')).toBeInTheDocument(); }); it('should show proper icon', () => { - jest.spyOn(React, 'useState').mockImplementationOnce(() => React.useState(true)); - + get_leave_confirm_states(); render(); expect(screen.getByTestId('unsaved_changes_icon')).toBeInTheDocument(); expect(screen.getByText('Unsaved changes')).toBeInTheDocument(); @@ -74,28 +96,46 @@ describe('LeaveConfirm', () => { expect(screen.getByRole('button', { name: 'Leave Settings' })).toBeInTheDocument(); }); it('should trigger onclick callback when the user clicks cancel button', async () => { - jest.spyOn(React, 'useState').mockImplementationOnce(() => React.useState(true)); + get_leave_confirm_states(); render(); const el_cancel_btn = screen.getByRole('button', { name: 'Cancel' }); - fireEvent.click(el_cancel_btn); - await waitFor(() => { - expect(screen.queryByText('Unsaved changes')).not.toBeInTheDocument(); - }); + await userEvent.click(el_cancel_btn); + + expect(mock_set_show).toHaveBeenCalled(); + expect(mock_set_next_location).toHaveBeenCalled(); }); + it('should sehow modal when value is dirty and trigger unblock function', () => { + get_transition_blocker_states({ pathname: '/' }); + render(); + const el_leave_settings_btn = screen.getByRole('button', { name: 'Leave Settings' }); + userEvent.click(el_leave_settings_btn); + + expect(on_dirty).toHaveBeenCalled(); + expect(mock_set_show).toHaveBeenCalled(); + expect(mock_set_next_location).toHaveBeenCalled(); + }); + it('should set values as dirty when the user leaves modal', () => { - jest.spyOn(React, 'useState').mockImplementationOnce(() => React.useState(true)); - render(); + get_transition_blocker_states({ pathname: '/' }); + render(); const el_cancel_btn = screen.getByRole('button', { name: 'Cancel' }); - fireEvent.click(el_cancel_btn); - expect(screen.getByRole('button', { name: 'Leave Settings' })).toBeInTheDocument(); + userEvent.click(el_cancel_btn); + + expect(on_dirty).toHaveBeenCalled(); }); it('should change pathname when user leaves form', () => { - jest.spyOn(React, 'useState') - .mockReturnValueOnce([true, () => null]) - .mockReturnValueOnce([{ pathname: '/' }, () => null]); + get_transition_blocker_states({ pathname: '/' }); + render(); + const el_leave_settings_btn = screen.getByRole('button', { name: 'Leave Settings' }); + userEvent.click(el_leave_settings_btn); + expect(screen.getByRole('button', { name: 'Leave Settings' })).toBeInTheDocument(); + }); + + it('should not change pathname when user leaves form', () => { + get_transition_blocker_states({ pathname: null }); render(); const el_leave_settings_btn = screen.getByRole('button', { name: 'Leave Settings' }); - fireEvent.click(el_leave_settings_btn); + userEvent.click(el_leave_settings_btn); expect(screen.getByRole('button', { name: 'Leave Settings' })).toBeInTheDocument(); }); }); diff --git a/packages/account/src/Components/poi-unsupported-failed/__tests__/unsupported-failed.spec.tsx b/packages/account/src/Components/poi-unsupported-failed/__tests__/unsupported-failed.spec.tsx new file mode 100644 index 000000000000..5f3116cbed14 --- /dev/null +++ b/packages/account/src/Components/poi-unsupported-failed/__tests__/unsupported-failed.spec.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { screen, render } from '@testing-library/react'; +import UnsupportedFailed from '../unsupported-failed'; + +jest.mock('@deriv/components', () => { + const original_module = jest.requireActual('@deriv/components'); + return { + ...original_module, + Icon: jest.fn(() =>
Mocked Icon
), + }; +}); + +describe('', () => { + const error = 'error'; + it('should render component with its content', () => { + render(); + expect(screen.getByText('Proof of identity documents upload failed')).toBeInTheDocument(); + expect(screen.getByText('error')).toBeInTheDocument(); + expect(screen.getByText('Mocked Icon')).toBeInTheDocument(); + }); +}); diff --git a/packages/account/src/Components/poo/statuses/poo-not-required/__tests__/not-required.spec.tsx b/packages/account/src/Components/poo/statuses/poo-not-required/__tests__/not-required.spec.tsx new file mode 100644 index 000000000000..7c826826c3f3 --- /dev/null +++ b/packages/account/src/Components/poo/statuses/poo-not-required/__tests__/not-required.spec.tsx @@ -0,0 +1,24 @@ +import { screen, render } from '@testing-library/react'; +import React from 'react'; +import POONotRequired from '../index'; + +jest.mock('@deriv/components', () => { + const original_module = jest.requireActual('@deriv/components'); + return { + ...original_module, + Icon: jest.fn(() =>
Mocked Icon
), + }; +}); + +describe('', () => { + it('should render component with its content', () => { + render(); + expect(screen.getByText("Your proof of ownership isn't required.")).toBeInTheDocument(); + expect( + screen.getByText( + 'You are not required to submit proof of ownership at this time. We will inform you if proof of ownership is required in the future.' + ) + ).toBeInTheDocument(); + expect(screen.getByText('Mocked Icon')).toBeInTheDocument(); + }); +}); diff --git a/packages/account/src/Components/poo/statuses/poo-rejected/__tests__/rejected.spec.tsx b/packages/account/src/Components/poo/statuses/poo-rejected/__tests__/rejected.spec.tsx new file mode 100644 index 000000000000..09b591be281b --- /dev/null +++ b/packages/account/src/Components/poo/statuses/poo-rejected/__tests__/rejected.spec.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { screen, render } from '@testing-library/react'; +import POORejected from '../index'; + +jest.mock('@deriv/components', () => { + const original_module = jest.requireActual('@deriv/components'); + return { + ...original_module, + Icon: jest.fn(() =>
Mocked Icon
), + }; +}); + +describe('', () => { + it('Should render and its contents', () => { + const onTryAgain = jest.fn(); + render(); + expect(screen.getByText('Mocked Icon')).toBeInTheDocument(); + expect(screen.getByText('Proof of ownership verification failed')).toBeInTheDocument(); + expect(screen.getByText('We were unable to verify your proof of ownership.')).toBeInTheDocument(); + expect(screen.getByText('Try again')).toBeInTheDocument(); + }); +}); diff --git a/packages/account/src/Components/poo/statuses/poo-rejected/index.js b/packages/account/src/Components/poo/statuses/poo-rejected/index.js index 330c72aa62f3..164171243ff8 100644 --- a/packages/account/src/Components/poo/statuses/poo-rejected/index.js +++ b/packages/account/src/Components/poo/statuses/poo-rejected/index.js @@ -1,3 +1,3 @@ -import { POORejetced } from './rejected.jsx'; +import { POORejected } from './rejected.jsx'; -export default POORejetced; +export default POORejected; diff --git a/packages/account/src/Components/poo/statuses/poo-rejected/rejected.jsx b/packages/account/src/Components/poo/statuses/poo-rejected/rejected.jsx index 4bb39411a13a..8ce3e0735ada 100644 --- a/packages/account/src/Components/poo/statuses/poo-rejected/rejected.jsx +++ b/packages/account/src/Components/poo/statuses/poo-rejected/rejected.jsx @@ -2,7 +2,7 @@ import { Button, Text, Icon } from '@deriv/components'; import { localize } from '@deriv/translations'; import React from 'react'; -export const POORejetced = ({ onTryAgain }) => { +export const POORejected = ({ onTryAgain }) => { return (
diff --git a/packages/account/src/Components/poo/statuses/poo-submitted/__tests__/submitted.spec.tsx b/packages/account/src/Components/poo/statuses/poo-submitted/__tests__/submitted.spec.tsx new file mode 100644 index 000000000000..df1cdd3e3b59 --- /dev/null +++ b/packages/account/src/Components/poo/statuses/poo-submitted/__tests__/submitted.spec.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { screen, render } from '@testing-library/react'; +import POOSubmitted from '../index'; + +jest.mock('@deriv/components', () => { + const original_module = jest.requireActual('@deriv/components'); + return { + ...original_module, + Icon: jest.fn(() =>
Mocked Icon
), + }; +}); + +describe('', () => { + it('Should render { + render(); + expect(screen.getByText('We’ve received your proof of ownership.')).toBeInTheDocument(); + expect( + screen.getByText('We’ll review your documents and notify you of its status within 3 days.') + ).toBeInTheDocument(); + expect(screen.getByText('Mocked Icon')).toBeInTheDocument(); + }); +}); diff --git a/packages/account/src/Components/poo/statuses/poo-verified/__tests__/verified.spec.tsx b/packages/account/src/Components/poo/statuses/poo-verified/__tests__/verified.spec.tsx new file mode 100644 index 000000000000..db3bf3c77355 --- /dev/null +++ b/packages/account/src/Components/poo/statuses/poo-verified/__tests__/verified.spec.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { screen, render } from '@testing-library/react'; +import POOVerified from '../index'; + +jest.mock('@deriv/components', () => { + const original_module = jest.requireActual('@deriv/components'); + return { + ...original_module, + Icon: jest.fn(() =>
Mocked Icon
), + }; +}); + +describe('', () => { + it('Should render and its contents', () => { + render(); + expect(screen.getByText('Proof of ownership verification passed.')).toBeInTheDocument(); + expect(screen.getByText('Mocked Icon')).toBeInTheDocument(); + }); +}); diff --git a/packages/account/src/Sections/Security/Passwords/passwords-platform.jsx b/packages/account/src/Sections/Security/Passwords/passwords-platform.jsx index f4092bf78314..400884fb9178 100644 --- a/packages/account/src/Sections/Security/Passwords/passwords-platform.jsx +++ b/packages/account/src/Sections/Security/Passwords/passwords-platform.jsx @@ -46,12 +46,6 @@ const PasswordsPlatform = ({ email, has_dxtrade_accounts, has_mt5_accounts }) => - - ]} - /> -
diff --git a/packages/appstore/package.json b/packages/appstore/package.json index 72d7994e0062..643081961b83 100644 --- a/packages/appstore/package.json +++ b/packages/appstore/package.json @@ -27,6 +27,7 @@ "@deriv/account": "^1.0.0", "@deriv/api": "^1.0.0", "@deriv/api-types": "^1.0.94", + "@deriv/hooks": "^1.0.0", "@deriv/cashier": "^1.0.0", "@deriv/components": "^1.0.0", "@deriv/cfd": "^1.0.0", diff --git a/packages/appstore/src/components/cfds-listing/index.tsx b/packages/appstore/src/components/cfds-listing/index.tsx index eab8817cc0b4..ab42f5260e01 100644 --- a/packages/appstore/src/components/cfds-listing/index.tsx +++ b/packages/appstore/src/components/cfds-listing/index.tsx @@ -213,7 +213,7 @@ const CFDsListing = () => { {available_dxtrade_accounts?.length > 0 && (
- {localize('Other CFDs')} + {localize('Other CFD Platforms')}
)} diff --git a/packages/appstore/src/components/main-title-bar/index.tsx b/packages/appstore/src/components/main-title-bar/index.tsx index 29487b8e4736..fea8976bde79 100644 --- a/packages/appstore/src/components/main-title-bar/index.tsx +++ b/packages/appstore/src/components/main-title-bar/index.tsx @@ -12,7 +12,7 @@ import RegulationsSwitcherLoader from 'Components/pre-loader/regulations-switche import WalletsBanner from 'Components/wallets-banner'; const MainTitleBar = () => { - const { traders_hub, client } = useStores(); + const { traders_hub, client, notifications } = useStores(); const { selected_region, handleTabItemClick, @@ -21,11 +21,20 @@ const MainTitleBar = () => { setWalletsMigrationFailedPopup, } = traders_hub; const { is_landing_company_loaded, is_switching } = client; + const { removeAllNotificationMessages, filterNotificationMessages } = notifications; const is_low_risk_cr_real_account = content_flag === ContentFlag.LOW_RISK_CR_NON_EU || content_flag === ContentFlag.LOW_RISK_CR_EU; const [active_index, setActiveIndex] = React.useState(selected_region === 'Non-EU' ? 0 : 1); + // TODO: Remove this when we have BE API ready + removeAllNotificationMessages(); + + React.useEffect(() => { + filterNotificationMessages(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + return ( diff --git a/packages/appstore/src/components/onboarding-new/static-dashboard.tsx b/packages/appstore/src/components/onboarding-new/static-dashboard.tsx index 67f9aa7f40fc..bfa6ca3a324d 100644 --- a/packages/appstore/src/components/onboarding-new/static-dashboard.tsx +++ b/packages/appstore/src/components/onboarding-new/static-dashboard.tsx @@ -539,7 +539,7 @@ const StaticDashboard = ({ : 'prominent' } > - {localize('Other CFDs')} + {localize('Other CFD Platforms')}
diff --git a/packages/appstore/src/components/wallet-transfer/wallet-transfer.scss b/packages/appstore/src/components/wallet-transfer/wallet-transfer.scss index 67ecf58f9296..26ae40f30594 100644 --- a/packages/appstore/src/components/wallet-transfer/wallet-transfer.scss +++ b/packages/appstore/src/components/wallet-transfer/wallet-transfer.scss @@ -1,7 +1,6 @@ .wallet-transfer { display: flex; flex-direction: column; - row-gap: 3.2rem; max-width: 65rem; width: 100%; margin: 9.6rem auto 4.8rem; @@ -10,6 +9,12 @@ margin: 4.8rem auto; } + &__tiles-container { + display: flex; + flex-direction: column; + row-gap: 3.2rem; + } + &__divider { border: 0.5px solid $color-grey-5; margin: 0.8rem 0; @@ -39,4 +44,10 @@ } } } + + &__transfer-button { + margin-top: 4.8rem; + display: flex; + justify-content: flex-end; + } } diff --git a/packages/appstore/src/components/wallet-transfer/wallet-transfer.tsx b/packages/appstore/src/components/wallet-transfer/wallet-transfer.tsx index b3d431c8c420..96ca80fa4b66 100644 --- a/packages/appstore/src/components/wallet-transfer/wallet-transfer.tsx +++ b/packages/appstore/src/components/wallet-transfer/wallet-transfer.tsx @@ -1,5 +1,6 @@ import React from 'react'; -import { AmountInput, TransferAccountSelector } from '@deriv/components'; +import { Field, FieldProps, Formik, Form } from 'formik'; +import { AmountInput, TransferAccountSelector, Button } from '@deriv/components'; import { getDecimalPlaces } from '@deriv/shared'; import { useStore, observer } from '@deriv/stores'; import { localize, Localize } from '@deriv/translations'; @@ -22,8 +23,6 @@ const WalletTransfer = observer(({ is_wallet_name_visible, setIsWalletNameVisibl const [from_account, setFromAccount] = React.useState(transfer_accounts.wallets[0]); const [to_account, setToAccount] = React.useState(); - const [from_amount, setFromAmount] = React.useState(); - const [to_amount, setToAmount] = React.useState(); const portal_id = is_mobile ? 'mobile_list_modal_root' : 'modal_root'; @@ -52,55 +51,130 @@ const WalletTransfer = observer(({ is_wallet_name_visible, setIsWalletNameVisibl ); }, [from_account?.label, to_account?.label]); + const is_amount_to_input_disabled = !to_account; + return (
-
- - - -
-
- - - -
+ undefined} + validateOnBlur={false} + > + {({ setFieldValue, values }) => { + const onSelectToAccount = React.useCallback( + (account: TAccount) => { + setToAccount(account); + setFieldValue('to_amount', values.from_amount); + }, + [setFieldValue, values.from_amount] + ); + + const onSelectFromAccount = React.useCallback( + (account: TAccount) => { + setFromAccount(account); + if (account?.label === 'Demo USD Wallet') { + setFieldValue('to_amount', 0); + } + }, + [setFieldValue] + ); + + return ( +
+
+
+ + {({ field }: FieldProps) => ( + { + setFieldValue('from_amount', value); + if (!is_amount_to_input_disabled) { + setFieldValue('to_amount', value); + } + }} + /> + )} + + + +
+
+ + {({ field }: FieldProps) => ( + { + setFieldValue('from_amount', value); + setFieldValue('to_amount', value); + }} + /> + )} + + + +
+
+
+ +
+
+ ); + }} +
); }); diff --git a/packages/bot-skeleton/src/scratch/backward-compatibility.js b/packages/bot-skeleton/src/scratch/backward-compatibility.js index e242752f312b..a8ad81490eb7 100644 --- a/packages/bot-skeleton/src/scratch/backward-compatibility.js +++ b/packages/bot-skeleton/src/scratch/backward-compatibility.js @@ -8,6 +8,7 @@ export default class BlockConversion { this.blocks_pending_reconnect = {}; this.workspace = this.createWorkspace(); this.workspace_variables = {}; + this.has_market_block = false; } getConversions() { @@ -212,7 +213,10 @@ export default class BlockConversion { lists_create_with: block_node => generateGrowingListBlock(block_node, 'lists_create_with', localize('list'), 'VALUE'), macda: block_node => generateIndicatorBlock(block_node, 'macda_statement', 'macda'), - market: block_node => tradeOptions(block_node), + market: block_node => { + this.has_market_block = true; + return tradeOptions(block_node); + }, rsi: block_node => generateIndicatorBlock(block_node, 'rsi_statement', 'rsi'), rsia: block_node => generateIndicatorBlock(block_node, 'rsia_statement', 'rsia'), sma: block_node => generateIndicatorBlock(block_node, 'sma_statement', 'sma'), @@ -352,10 +356,14 @@ export default class BlockConversion { // to "Run once at start". Legacy "market" blocks had no such thing as "Run once at start" // not moving everything would kill Martingale strategies as they'd be reinitialised each run. const trade_definition_block = this.workspace.getTradeDefinitionBlock(); - + const has_initialization_block = trade_definition_block.getBlocksInStatement('INITIALIZATION').length > 0; if (trade_definition_block) { trade_definition_block.getBlocksInStatement('SUBMARKET').forEach(block => { - if (block.type !== 'trade_definition_tradeoptions') { + if ( + block.type !== 'trade_definition_tradeoptions' && + this.has_market_block && + !has_initialization_block + ) { const last_connection = trade_definition_block.getLastConnectionInStatement('INITIALIZATION'); block.unplug(true); last_connection.connect(block.previousConnection); diff --git a/packages/bot-skeleton/src/scratch/blocks/Binary/Trade Definition/multiplier_stop_loss.js b/packages/bot-skeleton/src/scratch/blocks/Binary/Trade Definition/multiplier_stop_loss.js index 7968a78aef7e..75b5f989fefd 100644 --- a/packages/bot-skeleton/src/scratch/blocks/Binary/Trade Definition/multiplier_stop_loss.js +++ b/packages/bot-skeleton/src/scratch/blocks/Binary/Trade Definition/multiplier_stop_loss.js @@ -29,14 +29,18 @@ Blockly.Blocks.multiplier_stop_loss = { colourTertiary: Blockly.Colours.Base.colourTertiary, previousStatement: null, nextStatement: null, - tooltip: localize('Your contract will be closed automatically if your loss reaches this amount.'), + tooltip: localize( + 'Your contract is closed automatically when your loss is more than or equals to this amount. This block can only be used with the multipliers trade type.' + ), category: Blockly.Categories.Trade_Definition, }; }, meta() { return { display_name: localize('Stop loss'), - description: localize('Your contract will be closed automatically if your loss reaches this amount.'), + description: localize( + 'Your contract is closed automatically when your loss is more than or equals to this amount. This block can only be used with the multipliers trade type.' + ), }; }, onchange(event) { diff --git a/packages/bot-skeleton/src/scratch/blocks/Binary/Trade Definition/multiplier_take_profit.js b/packages/bot-skeleton/src/scratch/blocks/Binary/Trade Definition/multiplier_take_profit.js index c9811fa26fdd..ad0c3c26a75f 100644 --- a/packages/bot-skeleton/src/scratch/blocks/Binary/Trade Definition/multiplier_take_profit.js +++ b/packages/bot-skeleton/src/scratch/blocks/Binary/Trade Definition/multiplier_take_profit.js @@ -29,14 +29,18 @@ Blockly.Blocks.multiplier_take_profit = { colourTertiary: Blockly.Colours.Base.colourTertiary, previousStatement: null, nextStatement: null, - tooltip: localize('Your contract will be closed automatically if your profit reaches this amount.'), + tooltip: localize( + 'Your contract is closed automatically when your profit is more than or equals to this amount. This block can only be used with the multipliers trade type.' + ), category: Blockly.Categories.Trade_Definition, }; }, meta() { return { display_name: localize('Take Profit'), - description: localize('Your contract will be closed automatically if your profit reaches this amount.'), + description: localize( + 'Your contract is closed automatically when your profit is more than or equals to this amount. This block can only be used with the multipliers trade type.' + ), }; }, onchange(event) { diff --git a/packages/bot-web-ui/src/app/app-content.jsx b/packages/bot-web-ui/src/app/app-content.jsx index d8444ab4bb5f..987e347c8d19 100644 --- a/packages/bot-web-ui/src/app/app-content.jsx +++ b/packages/bot-web-ui/src/app/app-content.jsx @@ -2,14 +2,7 @@ import React from 'react'; import { ApiHelpers, ServerTime, setColors } from '@deriv/bot-skeleton'; import { Loading } from '@deriv/components'; import { observer, useStore } from '@deriv/stores'; -import { - Audio, - BotFooterExtensions, - BotNotificationMessages, - Dashboard, - NetworkToastPopup, - RoutePromptDialog, -} from 'Components'; +import { Audio, BotNotificationMessages, Dashboard, NetworkToastPopup, RoutePromptDialog } from 'Components'; import BotBuilder from 'Components/dashboard/bot-builder'; import GTM from 'Utils/gtm'; import { MobxContentProvider } from 'Stores/connect'; @@ -95,7 +88,6 @@ const AppContent = observer(() => {