diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ea75bd8656..272b4c4162 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [14.x] + node-version: [20.x] steps: - name: Checkout uses: actions/checkout@v2 @@ -18,6 +18,14 @@ jobs: with: node-version: ${{ matrix.node-version }} - run: yarn + - run: yarn workspace @ringcentral-integration/babel-settings test --coverage + - name: Babel Settings Coveralls + uses: coverallsapp/github-action@1.1.3 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + path-to-lcov: ./packages/babel-settings/coverage/lcov.info + flag-name: babel-settings + parallel: true - run: yarn workspace @ringcentral-integration/phone-number test --coverage - name: Phone Number Coveralls uses: coverallsapp/github-action@1.1.3 @@ -58,6 +66,14 @@ jobs: path-to-lcov: ./packages/core/coverage/lcov.info flag-name: core parallel: true + - run: yarn workspace @ringcentral-integration/react-hooks test --coverage + - name: Core Coveralls + uses: coverallsapp/github-action@1.1.3 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + path-to-lcov: ./packages/react-hooks/coverage/lcov.info + flag-name: react-hooks + parallel: true - run: yarn workspace @ringcentral-integration/commons test --coverage - name: Commons Coveralls uses: coverallsapp/github-action@1.1.3 @@ -66,15 +82,15 @@ jobs: path-to-lcov: ./packages/ringcentral-integration/coverage/lcov.info flag-name: commons parallel: true - # - run: yarn workspace @ringcentral-integration/widgets test --coverage - # - name: Widgets Coveralls - # uses: coverallsapp/github-action@1.1.3 - # with: - # github-token: ${{ secrets.GITHUB_TOKEN }} - # path-to-lcov: ./packages/ringcentral-widgets/coverage/lcov.info - # flag-name: widgets - # parallel: true - - run: yarn workspace ringcentral-widgets-test test --ci --coverage + - run: yarn workspace @ringcentral-integration/widgets test --coverage + - name: Widgets Coveralls + uses: coverallsapp/github-action@1.1.3 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + path-to-lcov: ./packages/ringcentral-widgets/coverage/lcov.info + flag-name: widgets + parallel: true + - run: yarn workspace ringcentral-widgets-test test --ci --coverage --updateSnapshot env: NODE_OPTIONS: --max-old-space-size=4096 - name: Widgets Integration Coveralls @@ -100,10 +116,10 @@ jobs: uses: actions/checkout@v2 with: persist-credentials: false - - name: Use Node.js 14.x + - name: Use Node.js 20.x uses: actions/setup-node@v1 with: - node-version: 14.x + node-version: 20.x - run: yarn - run: yarn workspace @ringcentral-integration/utils release - name: Utils Branch Release @@ -129,6 +145,22 @@ jobs: BRANCH: i18n-release FOLDER: release/i18n CLEAN: true + - run: yarn workspace @ringcentral-integration/i18n-dayjs release + - name: I18n Dayjs Branch Release + uses: JamesIves/github-pages-deploy-action@3.7.1 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BRANCH: i18n-dayjs-release + FOLDER: release/i18n-dayjs + CLEAN: true + - run: yarn workspace @ringcentral-integration/react-hooks release + - name: React Hooks Branch Release + uses: JamesIves/github-pages-deploy-action@3.7.1 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BRANCH: react-hooks-release + FOLDER: release/react-hooks + CLEAN: true - run: yarn workspace @ringcentral-integration/locale-loader release - name: Locale Loader Branch Release uses: JamesIves/github-pages-deploy-action@3.7.1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 81b988019f..ae24f5cbd5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,7 +16,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v1 with: - node-version: '14.x' + node-version: '20.x' registry-url: 'https://registry.npmjs.org' - name: Get the version id: get_version @@ -29,6 +29,52 @@ jobs: working-directory: release/utils env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + i18n-dayjs-npm-release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + persist-credentials: false + - name: Setup Node.js + uses: actions/setup-node@v1 + with: + node-version: '20.x' + registry-url: 'https://registry.npmjs.org' + - name: Get the version + id: get_version + run: echo ::set-output name=SOURCE_TAG::${GITHUB_REF#refs/tags/} + - run: yarn + - run: yarn workspace @ringcentral-integration/i18n-dayjs release + env: + TRAVIS_TAG: ${{ steps.get_version.outputs.SOURCE_TAG }} + - run: npm publish + working-directory: release/i18n-dayjs + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + react-hooks-npm-release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + persist-credentials: false + - name: Setup Node.js + uses: actions/setup-node@v1 + with: + node-version: '20.x' + registry-url: 'https://registry.npmjs.org' + - name: Get the version + id: get_version + run: echo ::set-output name=SOURCE_TAG::${GITHUB_REF#refs/tags/} + - run: yarn + - run: yarn workspace @ringcentral-integration/react-hooks release + env: + TRAVIS_TAG: ${{ steps.get_version.outputs.SOURCE_TAG }} + - run: npm publish + working-directory: release/react-hooks + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} core-npm-release: runs-on: ubuntu-latest steps: @@ -39,7 +85,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v1 with: - node-version: '14.x' + node-version: '20.x' registry-url: 'https://registry.npmjs.org' - name: Get the version id: get_version @@ -62,7 +108,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v1 with: - node-version: '14.x' + node-version: '20.x' registry-url: 'https://registry.npmjs.org' - name: Get the version id: get_version @@ -85,7 +131,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v1 with: - node-version: '14.x' + node-version: '20.x' registry-url: 'https://registry.npmjs.org' - name: Get the version id: get_version @@ -108,7 +154,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v1 with: - node-version: '14.x' + node-version: '20.x' registry-url: 'https://registry.npmjs.org' - name: Get the version id: get_version diff --git a/.sync b/.sync index c877a86833..21539f4eb3 100644 --- a/.sync +++ b/.sync @@ -1 +1 @@ -dd3f20d7a363f451ee63d4fabd04e4b97dbea059 +f39b7a45cca0781ea4231df645cd9c97e6c641fa diff --git a/.vscode/settings.json b/.vscode/settings.json index dcf440b1a4..7b1fdefac2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -32,6 +32,6 @@ // Enable ESLint (with Prettier plugin) to format code on save "eslint.autoFixOnSave": true, "editor.codeActionsOnSave": { - "source.fixAll.eslint": true + "source.fixAll.eslint": "explicit" } } diff --git a/crius/packages/babel-preset-crius/package.json b/crius/packages/babel-preset-crius/package.json index 03dd95fa00..bedff8cecf 100644 --- a/crius/packages/babel-preset-crius/package.json +++ b/crius/packages/babel-preset-crius/package.json @@ -4,7 +4,7 @@ "description": "Babel preset for all Crius plugins.", "main": "lib/index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\"" }, "author": "unadlib", "license": "MIT", @@ -20,5 +20,10 @@ }, "peerDependencies": { "@babel/core": "^7.5.5" + }, + "nx": { + "tags": [ + "scope:coverage-ignore" + ] } } diff --git a/crius/packages/babel-preset-crius/src/index.js b/crius/packages/babel-preset-crius/src/index.js index af12bfdeac..a760cc84bb 100644 --- a/crius/packages/babel-preset-crius/src/index.js +++ b/crius/packages/babel-preset-crius/src/index.js @@ -1,8 +1,8 @@ import { declare } from '@babel/helper-plugin-utils'; -import transformReactJSX from '@babel/plugin-transform-react-jsx'; import transformReactDisplayName from '@babel/plugin-transform-react-display-name'; -import transformReactJSXSource from '@babel/plugin-transform-react-jsx-source'; +import transformReactJSX from '@babel/plugin-transform-react-jsx'; import transformReactJSXSelf from '@babel/plugin-transform-react-jsx-self'; +import transformReactJSXSource from '@babel/plugin-transform-react-jsx-source'; export default declare((api, opts) => { api.assertVersion(7); diff --git a/crius/packages/babel-preset-crius/test/index.test.js b/crius/packages/babel-preset-crius/test/index.test.js index ce4575f019..a17b691b8d 100644 --- a/crius/packages/babel-preset-crius/test/index.test.js +++ b/crius/packages/babel-preset-crius/test/index.test.js @@ -1,4 +1,5 @@ import { transform } from '@babel/core'; + import crius from '../lib'; describe('crius preset', () => { diff --git a/crius/packages/crius-is/package.json b/crius/packages/crius-is/package.json index 7d5bf21bc3..e0d3711699 100644 --- a/crius/packages/crius-is/package.json +++ b/crius/packages/crius-is/package.json @@ -2,11 +2,8 @@ "name": "crius-is", "version": "1.2.0", "description": "A tool for checking Crius element types", - "main": "dist/index.js", - "module": "dist/index.js", - "typings": "dist/index.d.ts", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\"" }, "dependencies": { "crius": "^1.2.0" diff --git a/crius/packages/crius-logger/package.json b/crius/packages/crius-logger/package.json index 8ff02dd3e8..71f48de6cd 100644 --- a/crius/packages/crius-logger/package.json +++ b/crius/packages/crius-logger/package.json @@ -6,7 +6,7 @@ "module": "dist/index.js", "typings": "dist/index.d.ts", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\"" }, "author": "unadlib", "license": "MIT", @@ -15,5 +15,10 @@ }, "peerDependencies": { "crius-test": "^1.0.0-alpha.1" + }, + "nx": { + "tags": [ + "scope:coverage-ignore" + ] } } diff --git a/crius/packages/crius-logger/src/index.ts b/crius/packages/crius-logger/src/index.ts index 47086e7a37..f0e33eeec5 100644 --- a/crius/packages/crius-logger/src/index.ts +++ b/crius/packages/crius-logger/src/index.ts @@ -1,6 +1,7 @@ +import { Plugins, StepType, Step } from 'crius-test'; import * as fs from 'fs'; import * as path from 'path'; -import { Plugins, StepType, Step } from 'crius-test'; + interface Log { key: string; desc: string | undefined | null; diff --git a/crius/packages/crius-logger/test/index.test.tsx b/crius/packages/crius-logger/test/index.test.tsx index f64ae884a1..5ec1507da6 100644 --- a/crius/packages/crius-logger/test/index.test.tsx +++ b/crius/packages/crius-logger/test/index.test.tsx @@ -9,6 +9,7 @@ import { Then, plugins, } from 'crius-test'; + import logger from '../'; @plugins([logger({ path: 'packages/crius-logger' })]) @@ -175,10 +176,10 @@ class Test extends Step<{}, { __logger: object[] }> { ]); } - @(examples` + @examples` | accountTag | contactType | smsMessage | | 'us' | 'personal' | 'aaa' | - `) + ` run() { return ( diff --git a/crius/packages/crius-runner/package.json b/crius/packages/crius-runner/package.json index 93758c971a..8d4d5bacd0 100644 --- a/crius/packages/crius-runner/package.json +++ b/crius/packages/crius-runner/package.json @@ -2,11 +2,8 @@ "name": "crius-runner", "version": "1.2.0", "description": "A Crius runner with Crius steps", - "main": "dist/index.js", - "module": "dist/index.js", - "typings": "dist/index.d.ts", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\"" }, "author": "unadlib", "license": "MIT", @@ -16,5 +13,10 @@ }, "peerDependencies": { "crius": "^1.0.0-alpha.1" + }, + "nx": { + "tags": [ + "scope:coverage-ignore" + ] } } diff --git a/crius/packages/crius-runner/src/index.ts b/crius/packages/crius-runner/src/index.ts index ef89749f34..16dd922a3d 100644 --- a/crius/packages/crius-runner/src/index.ts +++ b/crius/packages/crius-runner/src/index.ts @@ -1,5 +1,5 @@ -import { run } from './runner'; import { Context, Hooks } from './interface'; +import { run } from './runner'; const CriusRunner = { run, diff --git a/crius/packages/crius-runner/src/runner.ts b/crius/packages/crius-runner/src/runner.ts index 0388200429..24d9c8620b 100644 --- a/crius/packages/crius-runner/src/runner.ts +++ b/crius/packages/crius-runner/src/runner.ts @@ -6,8 +6,9 @@ import { isCriusStepClass, isCriusStepFunction, } from 'crius-is'; -import { runWithLifecycle } from './lifecycle'; + import { handleContext } from './context'; +import { runWithLifecycle } from './lifecycle'; async function iterateChildren( children: Children, diff --git a/crius/packages/crius-runner/test/context.test.ts b/crius/packages/crius-runner/test/context.test.ts index 882b5e542d..3e4d37fd0a 100644 --- a/crius/packages/crius-runner/test/context.test.ts +++ b/crius/packages/crius-runner/test/context.test.ts @@ -1,4 +1,5 @@ import { CriusElement, Step } from 'crius'; + import { run } from '../src'; import { handleContext } from '../src/context'; diff --git a/crius/packages/crius-runner/test/context.test.tsx b/crius/packages/crius-runner/test/context.test.tsx index 17cdd6ea61..9f761b11e6 100644 --- a/crius/packages/crius-runner/test/context.test.tsx +++ b/crius/packages/crius-runner/test/context.test.tsx @@ -1,4 +1,5 @@ import { Step, StepFunction } from 'crius'; + import criusRunner from '../src'; test('runner deep step with JSX', async () => { diff --git a/crius/packages/crius-runner/test/index.test.ts b/crius/packages/crius-runner/test/index.test.ts index 4c6c5f4442..f66039ab56 100644 --- a/crius/packages/crius-runner/test/index.test.ts +++ b/crius/packages/crius-runner/test/index.test.ts @@ -1,4 +1,5 @@ import Crius, { Step } from 'crius'; + import { run } from '../src'; test('test run for step', async () => { diff --git a/crius/packages/crius-runner/test/index.test.tsx b/crius/packages/crius-runner/test/index.test.tsx index c59c9f14da..13bf7db939 100644 --- a/crius/packages/crius-runner/test/index.test.tsx +++ b/crius/packages/crius-runner/test/index.test.tsx @@ -1,4 +1,5 @@ import Crius, { StepFunction } from 'crius'; + import { run } from '../src'; test('runner with JSX', async () => { @@ -172,11 +173,9 @@ test('runner deep step with function child', async () => { await new Promise((resolve) => setTimeout(resolve, 100)); return ; }} - { - await (async () => { - return ; - })() - } + {await (async () => { + return ; + })()} ); diff --git a/crius/packages/crius-runner/test/lifecycle.test.tsx b/crius/packages/crius-runner/test/lifecycle.test.tsx index d01506ac8a..23e3eba3b0 100644 --- a/crius/packages/crius-runner/test/lifecycle.test.tsx +++ b/crius/packages/crius-runner/test/lifecycle.test.tsx @@ -1,4 +1,5 @@ import Crius from 'crius'; + import { run } from '../src'; test('runner lifecycle with JSX', async () => { diff --git a/crius/packages/crius-runner/test/runner.test.ts b/crius/packages/crius-runner/test/runner.test.ts index 840d8d9ce8..675a92003a 100644 --- a/crius/packages/crius-runner/test/runner.test.ts +++ b/crius/packages/crius-runner/test/runner.test.ts @@ -1,4 +1,5 @@ import { Step, StepFunction, Props, StepType } from 'crius'; + import { run, Context } from '../src'; test('base runner without return value', async () => { diff --git a/crius/packages/crius-test/package.json b/crius/packages/crius-test/package.json index 6dda3b79a4..7fa8becb19 100644 --- a/crius/packages/crius-test/package.json +++ b/crius/packages/crius-test/package.json @@ -2,11 +2,8 @@ "name": "crius-test", "version": "1.2.0", "description": "A new generation testing tools for BDD", - "main": "dist/index.js", - "module": "dist/index.js", - "typings": "dist/index.d.ts", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\"" }, "engines": { "node": ">=0.8.0" @@ -22,5 +19,10 @@ "crius": "^1.2.0", "crius-is": "^1.2.0", "crius-runner": "^1.2.0" + }, + "nx": { + "tags": [ + "scope:coverage-ignore" + ] } } diff --git a/crius/packages/crius-test/src/builder.tsx b/crius/packages/crius-test/src/builder.tsx index 2d95c5afce..13d2e40ad3 100644 --- a/crius/packages/crius-test/src/builder.tsx +++ b/crius/packages/crius-test/src/builder.tsx @@ -1,5 +1,6 @@ -import { isCriusNode } from 'crius-is'; import { StepType, CriusNode } from 'crius'; +import { isCriusNode } from 'crius-is'; + import { Step } from './step'; import { compileString } from './utils'; diff --git a/crius/packages/crius-test/src/decorators.ts b/crius/packages/crius-test/src/decorators.ts index 5ed5b8c5d0..02c1c82c6b 100644 --- a/crius/packages/crius-test/src/decorators.ts +++ b/crius/packages/crius-test/src/decorators.ts @@ -1,6 +1,7 @@ -import { run, Context } from 'crius-runner'; import { Props, StepType } from 'crius'; import { isCriusNode } from 'crius-is'; +import { run, Context } from 'crius-runner'; + import { Step, BaseContext } from './step'; import { parserString, compileString } from './utils'; @@ -82,7 +83,9 @@ function title(title: string) { }; } -function examples(params: TemplateStringsArray | object[] | string | string[]) { +function examples( + params: T, +) { return function ( target: Object, name: string, diff --git a/crius/packages/crius-test/test/decorators.test.ts b/crius/packages/crius-test/test/decorators.test.ts index ad49fb4f2a..f555cd5d8a 100644 --- a/crius/packages/crius-test/test/decorators.test.ts +++ b/crius/packages/crius-test/test/decorators.test.ts @@ -44,10 +44,10 @@ test('test @title', () => { test('test @examples', () => { class Bar extends Step { - @(examples` + @examples` | accountTag | contactType | smsMessage | | 'us' | false | 1 | - `) + ` run() {} } diff --git a/crius/packages/crius-test/test/index.test.tsx b/crius/packages/crius-test/test/index.test.tsx index ca6d3c722b..8cdbf02f65 100644 --- a/crius/packages/crius-test/test/index.test.tsx +++ b/crius/packages/crius-test/test/index.test.tsx @@ -234,11 +234,11 @@ class SendSMS1 extends Step { ]); } - @(examples` + @examples` | accountTag | contactType | smsMessage | | 'us' | 'personal' | 'aaa' | | 'us_1' | 'personal_1' | 'aaa_1' | - `) + ` run() { return ( @@ -316,10 +316,10 @@ const Login: StepFunction = (props, context) => { @autorun(test) @title('run pure AC text') class Test extends Step { - @(examples` + @examples` | smsMessage | | 'testFoo' | - `) + ` run() { return ( @@ -342,10 +342,10 @@ autorun(test)(() => ( @autorun(test) @title('run pure AC text') class TestSkip extends Step { - @(examples` + @examples` | smsMessage | | 'testFoo' | - `) + ` run() { return ( @@ -385,10 +385,10 @@ const A1 = () => { @autorun(test) @title('test action with flow') class TestWithFlow extends Step { - @(examples` + @examples` | smsMessage | | 777 | - `) + ` run() { return ( = ( @autorun(test.skip) @title('run pure AC text') class Test1 extends Step { - @(examples` + @examples` | smsMessage | | 'testFoo' | - `) + ` run() { return ( diff --git a/crius/packages/crius/package.json b/crius/packages/crius/package.json index dbe4155508..c3856883a2 100644 --- a/crius/packages/crius/package.json +++ b/crius/packages/crius/package.json @@ -2,11 +2,8 @@ "name": "crius", "version": "1.2.0", "description": "A concise JavaScript library for building step processes", - "main": "dist/index.js", - "module": "dist/index.js", - "typings": "dist/index.d.ts", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\"" }, "keywords": [ "step", diff --git a/crius/packages/crius/src/index.ts b/crius/packages/crius/src/index.ts index 54037ed2b0..df513cdf70 100644 --- a/crius/packages/crius/src/index.ts +++ b/crius/packages/crius/src/index.ts @@ -1,6 +1,6 @@ -import { Step, StepClass } from './stepClass'; -import { Children, StepType, Props, CriusElement } from './step'; import { createFlow, CriusNode } from './flow'; +import { Children, StepType, Props, CriusElement } from './step'; +import { Step, StepClass } from './stepClass'; import { StepFunction } from './stepFunction'; const Crius = { diff --git a/package.json b/package.json index f13ef42274..92e7acdaf9 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,6 @@ { "name": "ringcentral-js-widgets", "private": true, - "devEngines": { - "node": "8.x || 9.x || 10.x" - }, "workspaces": [ "packages/*", "crius/packages/*" @@ -43,13 +40,18 @@ "cz-conventional-changelog": "^2.1.0", "eslint-settings": "*", "typescript-plugin-css-modules": "^1.0.5", - "typescript": "^4.9.4" + "ts-node": "^10.9.1", + "typescript": "^5.5.2", + "serialize-error": "^7.0.1" }, "config": { "commitizen": { "path": "cz-conventional-changelog" } }, + "engines": { + "node": ">=20.0.0" + }, "dependencies": { "execa": "^1.0.0", "fs-extra": "^7.0.1" diff --git a/packages/babel-settings/electron-babel.config.js b/packages/babel-settings/electron-babel.config.js index a27724a81e..fcb1f6854f 100644 --- a/packages/babel-settings/electron-babel.config.js +++ b/packages/babel-settings/electron-babel.config.js @@ -6,7 +6,7 @@ module.exports = function (api) { useBuiltIns: false, corejs: false, targets: { - electron: '22.0.0', + electron: '27.1.2', }, }, }); diff --git a/packages/babel-settings/jest.config.js b/packages/babel-settings/jest.config.js new file mode 100644 index 0000000000..a9c2a5e58c --- /dev/null +++ b/packages/babel-settings/jest.config.js @@ -0,0 +1,4 @@ +const merge = require('@ringcentral-integration/test-utils/lib/merge'); +const baseConfig = require('@ringcentral-integration/test-utils/config/jest.config'); + +module.exports = merge(baseConfig, {}); diff --git a/packages/babel-settings/lib/ignores.js b/packages/babel-settings/lib/ignores.js new file mode 100644 index 0000000000..f37cd3d89c --- /dev/null +++ b/packages/babel-settings/lib/ignores.js @@ -0,0 +1,6 @@ +const ignores = ['node_modules/(?!((@rjsf|culori)/|(@ringcentral/spring)))']; + +module.exports = { + ignores, + regexp: ignores.map((ignore) => new RegExp(ignore)), +}; diff --git a/packages/babel-settings/lib/jestTransform.js b/packages/babel-settings/lib/jestTransform.js index d45c01cfb5..dc6760ad03 100644 --- a/packages/babel-settings/lib/jestTransform.js +++ b/packages/babel-settings/lib/jestTransform.js @@ -1,7 +1,9 @@ const __CI__ = process.argv.includes('--ci'); +const { regexp } = require('./ignores'); module.exports = require('babel-jest').default.createTransformer({ - ignore: [/node_modules/], + // some package be esm module, need transform + ignore: regexp, rootMode: 'upward', // cancel sourceMap in CI environment sourceMaps: !__CI__, diff --git a/packages/babel-settings/lib/nextTransform.js b/packages/babel-settings/lib/nextTransform.js index 2a142de996..d010484866 100644 --- a/packages/babel-settings/lib/nextTransform.js +++ b/packages/babel-settings/lib/nextTransform.js @@ -1,8 +1,10 @@ const path = require('path'); const __CI__ = process.argv.includes('--ci'); +const { regexp } = require('./ignores'); module.exports = require('babel-jest').default.createTransformer({ - ignore: [/node_modules/], + // some package be esm module, need transform + ignore: regexp, rootMode: 'upward', configFile: path.resolve(__dirname, '../reactant-babel.config.js'), // cancel sourceMap in CI environment diff --git a/packages/babel-settings/package.json b/packages/babel-settings/package.json index fd02cb42bc..61e06af962 100644 --- a/packages/babel-settings/package.json +++ b/packages/babel-settings/package.json @@ -7,9 +7,11 @@ "url": "https://github.com/ringcentral/ringcentral-js-widget.git" }, "main": "index.js", - "scripts": {}, + "scripts": { + "test": "yarn run-test" + }, "dependencies": { - "@babel/core": "^7.20.12", + "@babel/core": "^7.24.0", "@babel/node": "^7.15.8", "@babel/plugin-proposal-class-properties": "^7.10.4", "@babel/plugin-proposal-decorators": "^7.10.5", @@ -18,25 +20,31 @@ "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.4", "@babel/plugin-proposal-object-rest-spread": "^7.11.0", "@babel/plugin-proposal-optional-chaining": "^7.11.0", - "@babel/plugin-transform-typescript": "^7.20.13", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/plugin-transform-typescript": "^7.24.6", "@babel/preset-env": "^7.11.0", "@babel/preset-react": "^7.10.4", - "@babel/preset-typescript": "^7.18.6", + "@babel/preset-typescript": "^7.24.6", "@babel/register": "^7.15.3", "babel-plugin-const-enum": "^1.0.1", "babel-plugin-direct-import": "^1.0.0", "babel-plugin-transform-typescript-metadata": "^0.3.2", - "core-js": "^3.9.1", - "typescript": "^4.9.4" + "core-js": "^3.37.1", + "typescript": "^5.5.2" }, "peerDependencies": { "@babel/polyfill": "^7.10.4", - "babel-jest": "^27.4.6" + "babel-jest": "^29.7.0" }, "engines": { "node": ">=14" }, "ci": { "ringcentral-widgets-test": "**" + }, + "nx": { + "tags": [ + "scope:ci-group-1" + ] } } diff --git a/packages/babel-settings/test/ignore.test.ts b/packages/babel-settings/test/ignore.test.ts new file mode 100644 index 0000000000..446040de31 --- /dev/null +++ b/packages/babel-settings/test/ignore.test.ts @@ -0,0 +1,31 @@ +import { regexp } from '../lib/ignores'; + +describe('ignore.test', () => { + it('should ignore node_modules except for specific packages', () => { + const notIgnorePackages = [ + 'node_modules/@rjsf/validator-ajv8', + 'node_modules/@ringcentral/spring', + 'node_modules/@ringcentral/spring-ui', + 'node_modules/@ringcentral/spring-theme', + 'node_modules/culori/css', + ]; + + const ignorePackages = [ + 'node_modules/@ringcentral/juno', + 'node_modules/@example/test', + 'node_modules/test', + ]; + + regexp.forEach((i) => { + ignorePackages.forEach((p) => { + const result = i.test(p); + expect(result).toBeTruthy(); + }); + + notIgnorePackages.forEach((p) => { + const result = i.test(p); + expect(result).toBeFalsy(); + }); + }); + }); +}); diff --git a/packages/core/lib/ObjectMap/ObjectMap.ts b/packages/core/lib/ObjectMap/ObjectMap.ts index 1032a1c5d4..b0bd6c1b3f 100644 --- a/packages/core/lib/ObjectMap/ObjectMap.ts +++ b/packages/core/lib/ObjectMap/ObjectMap.ts @@ -1,3 +1,5 @@ +import 'reflect-metadata'; + import { find } from 'ramda'; const sDefinition = Symbol('definition'); diff --git a/packages/core/lib/RcModule/RcModule.ts b/packages/core/lib/RcModule/RcModule.ts index 7cec384c7c..99bff1f963 100644 --- a/packages/core/lib/RcModule/RcModule.ts +++ b/packages/core/lib/RcModule/RcModule.ts @@ -1,5 +1,6 @@ import type { Reducer, ReducersMapObject } from 'redux'; import { combineReducers } from 'redux'; + import type { Action, Service, Store, Subscription } from '../usm-redux'; import { action, diff --git a/packages/core/lib/RcModule/createApp.ts b/packages/core/lib/RcModule/createApp.ts index e636a07f5e..39d6b3b680 100644 --- a/packages/core/lib/RcModule/createApp.ts +++ b/packages/core/lib/RcModule/createApp.ts @@ -1,5 +1,6 @@ import type { ReducersMapObject, StoreEnhancer } from 'redux'; import { combineReducers } from 'redux'; + import { createStore, RcModuleV2, spawnStorageReducersKey } from './RcModule'; /** diff --git a/packages/core/lib/RcUIModule/RcUIModuleType.ts b/packages/core/lib/RcUIModule/RcUIModuleType.ts index 3c3161bc0d..23c8cacb6e 100644 --- a/packages/core/lib/RcUIModule/RcUIModuleType.ts +++ b/packages/core/lib/RcUIModule/RcUIModuleType.ts @@ -1,4 +1,8 @@ +import type { OmitFunctions } from '@ringcentral-integration/utils/src/typeFunctions/OmitFunctions'; +import type { PickFunctions } from '@ringcentral-integration/utils/src/typeFunctions/PickFunctions'; + import type { RcModuleType } from '../RcModule'; + import type { RcUIModuleV2 } from './RcUIModule'; export type RcUIModuleType = RcModuleType< @@ -6,19 +10,6 @@ export type RcUIModuleType = RcModuleType< 'getUIProps' | 'getUIFunctions' >; -type PickFunctionKeys> = Exclude< - { - [K in keyof T]: Required extends Record ? K : never; - }[keyof T], - undefined ->; - -export type UIFunctions> = Pick< - T, - PickFunctionKeys ->; +export type UIFunctions> = PickFunctions; -export type UIProps> = Omit< - T, - PickFunctionKeys ->; +export type UIProps> = OmitFunctions; diff --git a/packages/core/lib/index.ts b/packages/core/lib/index.ts index 861b87cfdf..9f5f2309f5 100644 --- a/packages/core/lib/index.ts +++ b/packages/core/lib/index.ts @@ -1,3 +1,4 @@ export * from './RcModule'; export * from './RcUIModule'; +export * from './rxjs'; export * from './track'; diff --git a/packages/core/lib/logger/loggerV2.ts b/packages/core/lib/logger/loggerV2.ts new file mode 100644 index 0000000000..edb9409710 --- /dev/null +++ b/packages/core/lib/logger/loggerV2.ts @@ -0,0 +1,119 @@ +import { + ConsoleTransport, + StorageTransport, + useLogger, + type ITransport, + type SerializedMessage, + ScriptErrorIntegration, + ConsoleIntegration, +} from '@ringcentral/mfe-logger'; +import { type Transport, createTransport } from 'reactant-share'; + +const isSharedWorker = !!global.SharedWorkerGlobalScope; +interface SharedWorkerTransportOptions { + /** + * enable transport + */ + enabled?: boolean; +} + +export class SharedWorkerTransport implements ITransport { + type = 'storage'; + + private transport?: Transport; + + constructor(protected _options: SharedWorkerTransportOptions = {}) {} + + async init() { + if (isSharedWorker && this._options.enabled) { + this.transport = createTransport('SharedWorkerInternal', { + prefix: 'logger', + }); + } + } + + async write({ payload }: SerializedMessage) { + this.transport?.emit( + { + name: 'syncLog', + respond: false, + }, + { payload }, + ); + } +} + +export class MemoryStorage { + constructor(protected _data: Record = {}) {} + + getItem(key: string) { + return this._data[key]; + } + + setItem(key: string, value: unknown) { + this._data[key] = value; + } + + removeItem(key: string) { + delete this._data[key]; + } +} + +const toggleKey = 'RC_MFE_LOGGER'; + +export const loggerEnabled = global.localStorage?.getItem(toggleKey) === 'true'; + +export const toggleLogger = (enabled: boolean) => { + global.localStorage?.setItem(toggleKey, String(enabled)); +}; + +/** + * new logger + */ +// eslint-disable-next-line react-hooks/rules-of-hooks +export const loggerV2 = useLogger({ + name: isSharedWorker ? `worker-${Date.now()}` : 'root', + transports: isSharedWorker + ? [ + new ConsoleTransport({ + enabled: true, + storage: new MemoryStorage({ + ROARR_LOG: true, + }), + }), + new SharedWorkerTransport({ + enabled: true, + }), + ] + : [ + ...(process.env.NODE_ENV === 'test' + ? [] + : [ + new ConsoleTransport({ + enabled: true, + storage: new MemoryStorage({ + ROARR_LOG: true, + }), + }), + new StorageTransport({ + enabled: true, + }), + ]), + ], + integrations: + process.env.NODE_ENV === 'test' + ? [] + : [ + new ScriptErrorIntegration({ + enabled: true, + }), + ...(process.env.NODE_ENV === 'production' + ? [ + new ConsoleIntegration({ + enabled: true, + }), + ] + : []), + ], + enabled: isSharedWorker ? isSharedWorker : loggerEnabled, +}); diff --git a/packages/core/lib/rxjs/fromSubscribe.ts b/packages/core/lib/rxjs/fromSubscribe.ts new file mode 100644 index 0000000000..1f774de334 --- /dev/null +++ b/packages/core/lib/rxjs/fromSubscribe.ts @@ -0,0 +1,35 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { finalize, Observable, share } from 'rxjs'; + +import { subscribe } from '../usm-redux'; + +/** + * `subscribe` redux state change notifications to `Observable` flow + * + * that method for you can check event when any redux dispatch triggered. + * + * @example + * ```ts + * fromSubscribe(this) + * .pipe( + * tap((e) => { + * console.log(e); // trigger when any state change + * }), + * ) + * .subscribe(); + * ``` + * + * same as `subscribe`, but not support `awaitPromise` option, if you need wait one by one, can use `concatMap` to control flow by yourself. + */ +export const fromSubscribe = (target: any) => { + let destroy: () => void; + + const obs$ = new Observable((observer) => { + destroy = subscribe(target, () => observer.next()); + }); + + return obs$.pipe( + share(), + finalize(() => destroy()), + ); +}; diff --git a/packages/core/lib/rxjs/index.ts b/packages/core/lib/rxjs/index.ts new file mode 100644 index 0000000000..fbc1bd1c06 --- /dev/null +++ b/packages/core/lib/rxjs/index.ts @@ -0,0 +1 @@ +export * from './fromSubscribe'; diff --git a/packages/core/lib/usm-redux/createStore.ts b/packages/core/lib/usm-redux/createStore.ts index 8115d6fffc..d48f7fbdbf 100644 --- a/packages/core/lib/usm-redux/createStore.ts +++ b/packages/core/lib/usm-redux/createStore.ts @@ -8,6 +8,7 @@ import type { Store as ReduxStore, } from 'redux'; import { createStore as createStoreWithRedux, combineReducers } from 'redux'; + import { stateKey, storeKey, @@ -16,7 +17,6 @@ import { subscriptionsKey, usm, } from './constant'; -import { getStagedState } from './utils/index'; import type { Action, StoreOptions, @@ -25,6 +25,7 @@ import type { Config, Service, } from './interface'; +import { getStagedState } from './utils/index'; let enablePatches: boolean; diff --git a/packages/core/lib/usm-redux/decorators/action.ts b/packages/core/lib/usm-redux/decorators/action.ts index 314c70ddc9..9ce9311545 100644 --- a/packages/core/lib/usm-redux/decorators/action.ts +++ b/packages/core/lib/usm-redux/decorators/action.ts @@ -1,11 +1,17 @@ /* eslint-disable func-names */ import type { Patch } from 'immer'; import { produceWithPatches, produce } from 'immer'; -import type { Service, Action } from '../interface'; + +import { checkPatches } from '../checkPatches'; import { storeKey, identifierKey, usm } from '../constant'; import { getPatchesToggle } from '../createStore'; -import { getStagedState, setStagedState } from '../utils/index'; -import { checkPatches } from '../checkPatches'; +import type { Service, Action } from '../interface'; +import { + getStagedModule, + getStagedState, + setStagedModule, + setStagedState, +} from '../utils/index'; export const action = ( target: object, @@ -32,6 +38,9 @@ export const action = ( let inversePatches: Patch[] = []; const recipe = (draftState: Record) => { setStagedState(draftState); + if (process.env.NODE_ENV !== 'production') { + setStagedModule(this._modulePath); + } fn.apply(this, args); }; const enablePatches = getPatchesToggle(); @@ -44,6 +53,9 @@ export const action = ( state = produce(lastState, recipe); } setStagedState(undefined); + if (process.env.NODE_ENV !== 'production') { + setStagedModule(undefined); + } const changed = lastState !== state; if (process.env.NODE_ENV === 'development') { if (!changed) { @@ -94,8 +106,21 @@ export const action = ( } } finally { setStagedState(undefined); + if (process.env.NODE_ENV !== 'production') { + setStagedModule(undefined); + } } } else { + if ( + process.env.NODE_ENV !== 'production' && + getStagedModule() !== this._modulePath + ) { + throw new Error( + `The method '${ + this[identifierKey] + }.${key.toString()}' is not allowed to call other @action methods in the same module.`, + ); + } // enable staged state mode. fn.apply(this, args); } diff --git a/packages/core/lib/usm-redux/decorators/computed.ts b/packages/core/lib/usm-redux/decorators/computed.ts index d041eb1ac8..4742691d3a 100644 --- a/packages/core/lib/usm-redux/decorators/computed.ts +++ b/packages/core/lib/usm-redux/decorators/computed.ts @@ -1,6 +1,6 @@ -import { createSelectorWithArray } from '../utils/index'; import { storeKey } from '../constant'; import type { Service } from '../interface'; +import { createSelectorWithArray } from '../utils/index'; export const computed = (depsCallback: (instance: any) => any[]) => diff --git a/packages/core/lib/usm-redux/decorators/state.ts b/packages/core/lib/usm-redux/decorators/state.ts index 6720b1b3ed..da540b8bac 100644 --- a/packages/core/lib/usm-redux/decorators/state.ts +++ b/packages/core/lib/usm-redux/decorators/state.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import type { PropertyDescriptor, Service } from '../interface'; import { stateKey } from '../constant'; +import type { PropertyDescriptor, Service } from '../interface'; export const state = ( target: object, diff --git a/packages/core/lib/usm-redux/interface.ts b/packages/core/lib/usm-redux/interface.ts index 187c58f6f3..0deb53f3b2 100644 --- a/packages/core/lib/usm-redux/interface.ts +++ b/packages/core/lib/usm-redux/interface.ts @@ -1,5 +1,6 @@ import type { Patch } from 'immer'; import type { ReducersMapObject, Reducer, StoreEnhancer } from 'redux'; + import type { stateKey, storeKey, diff --git a/packages/core/lib/usm-redux/subscribe.ts b/packages/core/lib/usm-redux/subscribe.ts index 4081dc2405..30c130992f 100644 --- a/packages/core/lib/usm-redux/subscribe.ts +++ b/packages/core/lib/usm-redux/subscribe.ts @@ -1,3 +1,4 @@ +import { storeKey, subscriptionsKey } from './constant'; import type { Subscribe, Watch, @@ -5,7 +6,6 @@ import type { Subscription, Service, } from './interface'; -import { storeKey, subscriptionsKey } from './constant'; import { isEqual as defaultIsEqual } from './utils/index'; const subscribe: Subscribe = (module, listener) => { diff --git a/packages/core/lib/usm-redux/utils/selector.ts b/packages/core/lib/usm-redux/utils/selector.ts index 13605a42fe..412ee5ac50 100644 --- a/packages/core/lib/usm-redux/utils/selector.ts +++ b/packages/core/lib/usm-redux/utils/selector.ts @@ -1,4 +1,5 @@ /* eslint-disable func-names */ + /* eslint-disable prefer-rest-params */ import { areShallowEqualWithArray } from './isEqual'; diff --git a/packages/core/lib/usm-redux/utils/stagedState.ts b/packages/core/lib/usm-redux/utils/stagedState.ts index f2be6da494..a6bbe30d43 100644 --- a/packages/core/lib/usm-redux/utils/stagedState.ts +++ b/packages/core/lib/usm-redux/utils/stagedState.ts @@ -1,7 +1,14 @@ let stagedState: Record | undefined; +let stagedModule: string | undefined; export const getStagedState = () => stagedState; export const setStagedState = (state?: Record) => { stagedState = state; }; + +export const getStagedModule = () => stagedModule; + +export const setStagedModule = (module?: string) => { + stagedModule = module; +}; diff --git a/packages/core/package.json b/packages/core/package.json index 2f880fe2bf..d9fb4fed6a 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@ringcentral-integration/core", - "version": "0.14.0", + "version": "0.15.0", "description": "The foundation package for RingCentral Integration products.", "homepage": "https://github.com/ringcentral/ringcentral-js-widgets/tree/master/packages/core#readme", "bugs": { @@ -24,6 +24,9 @@ }, "dependencies": { "@ringcentral-integration/utils": "*", + "@ringcentral/mfe-logger": "^0.3.8", + "reactant-share": "^0.111.0", + "rxjs": "^7.8.0", "immer": "^9.0.12", "ramda": "^0.28.0", "redux": "^4.2.0" @@ -35,13 +38,21 @@ "gulp": "^4.0.2", "gulp-babel": "^8.0.0", "gulp-sourcemaps": "^2.6.5", - "jest-html-reporters": "^3.0.8" + "jest-html-reporters": "^3.1.7" }, "common": true, "ci": { "@ringcentral-integration/core": { "includes": "**", - "excludes": ["*.md", "**/*.md"] + "excludes": [ + "*.md", + "**/*.md" + ] } + }, + "nx": { + "tags": [ + "scope:ci-group-1" + ] } } diff --git a/packages/core/test/features/RcModule.test.tsx b/packages/core/test/features/RcModule.test.tsx index fbde981061..b1044850d4 100644 --- a/packages/core/test/features/RcModule.test.tsx +++ b/packages/core/test/features/RcModule.test.tsx @@ -294,7 +294,7 @@ class StorageUnexpectedUsageCheck extends Step { { - expect(createModule).toThrowError(); + expect(createModule).toThrow(); }} /> { - expect(createModule).toThrowError(); + expect(createModule).toThrow(); }} /> diff --git a/packages/core/test/features/checkPatches.test.tsx b/packages/core/test/features/checkPatches.test.tsx index c05a53db0f..a7db355ee1 100644 --- a/packages/core/test/features/checkPatches.test.tsx +++ b/packages/core/test/features/checkPatches.test.tsx @@ -9,6 +9,7 @@ import { examples, } from '@ringcentral-integration/test-utils'; import { produceWithPatches, enablePatches } from 'immer'; + import { checkPatches } from '../../lib/usm-redux/checkPatches'; enablePatches(); diff --git a/packages/core/test/features/loggerV2.test.tsx b/packages/core/test/features/loggerV2.test.tsx new file mode 100644 index 0000000000..574a11e7ff --- /dev/null +++ b/packages/core/test/features/loggerV2.test.tsx @@ -0,0 +1,166 @@ +describe('loggerV2', () => { + let MockUseLogger: jest.Mock; + let MockConsoleTransport: jest.Mock; + let MockStorageTransport: jest.Mock; + let MockScriptErrorIntegration: jest.Mock; + let MockConsoleIntegration: jest.Mock; + + beforeEach(() => { + MockUseLogger = jest.fn(); + MockConsoleTransport = jest.fn(); + MockStorageTransport = jest.fn(); + MockScriptErrorIntegration = jest.fn(); + MockConsoleIntegration = jest.fn(); + + jest.mock('@ringcentral/mfe-logger', () => ({ + ConsoleTransport: MockConsoleTransport, + StorageTransport: MockStorageTransport, + useLogger: MockUseLogger, + ScriptErrorIntegration: MockScriptErrorIntegration, + ConsoleIntegration: MockConsoleIntegration, + })); + }); + + afterEach(() => { + jest.clearAllMocks(); + jest.resetModules(); + }); + + it('should create a logger with the correct configuration when on sharedWorker mode', async () => { + process.env.NODE_ENV = 'development'; + global.SharedWorkerGlobalScope = true as any; + + await import('../../lib/logger/loggerV2'); + const { MemoryStorage } = await import('../../lib/logger/loggerV2'); + const { SharedWorkerTransport } = await import('../../lib/logger/loggerV2'); + + // Assertions + const { name, transports, integrations, enabled } = + MockUseLogger.mock.calls[0][0]; + + expect(name).toEqual(expect.stringContaining('worker-')); + expect(enabled).toBe(true); + expect(transports).toEqual([ + new MockConsoleTransport({ + enabled: true, + storage: new MemoryStorage({ + ROARR_LOG: true, + }), + }), + new SharedWorkerTransport({ + enabled: true, + }), + ]); + expect(integrations).toEqual([ + new MockScriptErrorIntegration({ + enabled: true, + }), + ]); + expect(MockScriptErrorIntegration).toHaveBeenCalledWith({ + enabled: true, + }); + }); + + it('should create a logger with the correct configuration when not on sharedWorker mode', async () => { + process.env.NODE_ENV = 'production'; + global.SharedWorkerGlobalScope = false as any; + + await import('../../lib/logger/loggerV2'); + const { MemoryStorage } = await import('../../lib/logger/loggerV2'); + + const { name, transports, integrations, enabled } = + MockUseLogger.mock.calls[0][0]; + + expect(name).toBe('root'); + expect(enabled).toBe(false); + expect(transports).toEqual([ + new MockConsoleTransport({ + enabled: true, + storage: new MemoryStorage({ + ROARR_LOG: true, + }), + }), + new MockStorageTransport({ + enabled: true, + }), + ]); + expect(integrations).toEqual([ + new MockScriptErrorIntegration({ + enabled: true, + }), + new MockConsoleIntegration({ + enabled: true, + }), + ]); + // Assertions + expect(MockConsoleTransport).toHaveBeenCalledWith({ + enabled: true, + storage: expect.any(MemoryStorage), + }); + expect(MockScriptErrorIntegration).toHaveBeenCalledWith({ + enabled: true, + }); + expect(MockConsoleIntegration).toHaveBeenCalledWith({ + enabled: true, + }); + }); +}); + +describe('MemoryStorage', () => { + afterEach(() => { + jest.clearAllMocks(); + jest.resetModules(); + }); + + it('check MemoryStorage module get/set/remove functionality', async () => { + const { MemoryStorage } = await import('../../lib/logger/loggerV2'); + const storage = new MemoryStorage({ + name: 'test-name', + }); + expect(storage.getItem('name')).toBe('test-name'); + expect(storage.getItem('nothing')).toBeUndefined(); + storage.setItem('name', 'new-name'); + expect(storage.getItem('name')).toBe('new-name'); + storage.removeItem('name'); + expect(storage.getItem('name')).toBeUndefined(); + }); +}); + +describe('SharedWorkerTransport', () => { + afterEach(() => { + jest.clearAllMocks(); + jest.resetModules(); + }); + + it('check SharedWorkerTransport module functionality', async () => { + const mockTransport = { + emit: jest.fn(), + }; + const payload = { version: '1.0.0' }; + const mockCreateTransport = jest.fn().mockReturnValue(mockTransport); + + global.SharedWorkerGlobalScope = true as any; + + jest.mock('reactant-share', () => ({ + __esModule: true, + ...jest.requireActual('reactant-share'), + createTransport: mockCreateTransport, + })); + + const { SharedWorkerTransport } = await import('../../lib/logger/loggerV2'); + const transport = new SharedWorkerTransport({ enabled: true }); + + transport.init(); + expect(mockCreateTransport).toHaveBeenCalledWith('SharedWorkerInternal', { + prefix: 'logger', + }); + transport.write({ payload, message: 'test' }); + expect(mockTransport.emit).toHaveBeenCalledWith( + { + name: 'syncLog', + respond: false, + }, + { payload }, + ); + }); +}); diff --git a/packages/core/test/features/usm-redux.test.tsx b/packages/core/test/features/usm-redux.test.tsx index 4a3f5c95ee..5aa11a2673 100644 --- a/packages/core/test/features/usm-redux.test.tsx +++ b/packages/core/test/features/usm-redux.test.tsx @@ -9,6 +9,7 @@ import { } from '@ringcentral-integration/test-utils'; import { applyPatches } from 'immer'; import { applyMiddleware } from 'redux'; + import type { Store } from '../../lib/usm-redux/index'; import { createStore, @@ -1088,3 +1089,73 @@ export class CreateStoreCross extends Step { ); } } + +@autorun(test) +@title('createStore::crossWithPath') +export class CreateStoreCrossWithPath extends Step { + run() { + class Counter0 { + _modulePath = 'counter0'; + + @state + count = { sum: 0 }; + + @action + increase() { + this.count.sum += 2; + } + } + + class Counter { + constructor(public counter: Counter0) {} + + _modulePath = 'counter'; + + @state + count = { sum: 0 }; + + @state + list: number[] = []; + + @action + increase() { + this.counter.increase(); + this.count.sum += 1; + this.list.push(this.count.sum); + } + } + let counter0: Counter0; + let counter: Counter; + let store: Store; + const fn = jest.fn(); + return ( + + { + counter0 = new Counter0(); + counter = new Counter(counter0); + + store = createStore({ + modules: [counter, counter0], + }); + + const oldState = Object.values(store.getState())[0] as Counter; + expect(oldState.count).toEqual({ sum: 0 }); + store.subscribe(() => { + fn(); + }); + }} + /> + { + expect(() => { + counter.increase(); + }).toThrow(); + }} + /> + + ); + } +} diff --git a/packages/core/test/index.ts b/packages/core/test/index.ts new file mode 100644 index 0000000000..f41a696fd2 --- /dev/null +++ b/packages/core/test/index.ts @@ -0,0 +1 @@ +export * from './lib'; diff --git a/packages/core/test/lib/index.ts b/packages/core/test/lib/index.ts new file mode 100644 index 0000000000..0018bf38d9 --- /dev/null +++ b/packages/core/test/lib/index.ts @@ -0,0 +1,2 @@ +export * from './waitTickFor'; +export * from './whenStateChange'; diff --git a/packages/core/test/lib/waitTickFor.ts b/packages/core/test/lib/waitTickFor.ts new file mode 100644 index 0000000000..1ce49c6c7b --- /dev/null +++ b/packages/core/test/lib/waitTickFor.ts @@ -0,0 +1,30 @@ +import { promisify } from 'util'; + +const nextTick = promisify(process.nextTick); + +/** + * Waits for the next tick and executes the provided callback function until it returns a result or the maximum number of attempts is reached. + * @param cb The callback function to execute on each tick. + * @param max The maximum number of attempts to execute the callback function. Defaults to 10. + * @returns The result returned by the callback function. + * @throws If the callback function throws an error on the last attempt. + */ +export const waitTickFor = async ( + cb: (i: number) => T, + max = 10, +) => { + let i = 0; + + while (i <= max) { + i++; + try { + await nextTick(); + const result = await cb(i); + return result; + } catch (e) { + if (i === max) { + throw e; + } + } + } +}; diff --git a/packages/core/test/lib/whenStateChange.ts b/packages/core/test/lib/whenStateChange.ts new file mode 100644 index 0000000000..e6ce3a9d1c --- /dev/null +++ b/packages/core/test/lib/whenStateChange.ts @@ -0,0 +1,227 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { + act, + jestFakeTimersAreEnabled, +} from '@ringcentral-integration/test-utils'; +import { waitUntilTo } from '@ringcentral-integration/utils'; +import { + concatAll, + defer, + filter, + finalize, + firstValueFrom, + last, + map, + merge, + Observable, + of, + switchMap, + timer, +} from 'rxjs'; + +import { fromSubscribe } from '../../lib/rxjs/fromSubscribe'; + +/** + * ### ⚠️⚠️⚠️ this method only be used in test environment + * + * wait subscribe emit one value until an timer + * if timer exceeded, throw timeout error + * + * and also support `jest fake timer`, that will run each timer in next tick with fake `100ms` with custom timeout + * + * @default timeout 5000ms + */ +const waitSubscribeSuccess$ = >( + obs$: T, + cb: (value?: R) => K, + options: { + timeout?: number; + } = {}, +) => { + let lastResult: K; + let lastErr: unknown; + + const { timeout = 5000 } = options; + + const usingJestFakeTimers = jestFakeTimersAreEnabled(); + + const timeout$ = usingJestFakeTimers + ? defer(async () => { + const eachPeriod = 100; + const times = timeout / eachPeriod; + // eslint-disable-next-line no-console + console.warn( + `waitSubscribeSuccess in fake timer mode will only take ${times} times of jest.advanceTimersByTime with ${eachPeriod}ms each`, + ); + + // when be in fake timer mode, we should use `jest.advanceTimersByTime` to trigger `setTimeout` callback + // and trigger each with next tick to let async event be executed + const array = Array.from({ length: times }, (_, index) => { + return defer(async () => { + jest.advanceTimersByTime(eachPeriod); + await act(() => Promise.resolve()); + return 0; + }); + }); + + return of(...array); + }).pipe( + switchMap((obs) => obs), + concatAll(), + // only get the last result, that means all timer be executed, timeout! + last(), + ) + : timer(timeout); + + return merge( + obs$.pipe( + map((value) => { + try { + lastResult = cb(value); + return true; + } catch (error) { + lastErr = error; + return false; + } + }), + filter((ready) => !!ready), + map(() => lastResult), + ), + timeout$.pipe( + map(() => { + try { + cb(); + } catch (error) { + console.error('waitSubscribeSuccess timeout', error); + lastErr = error; + } + + throw lastErr || new Error('waitSubscribeSuccess timeout'); + }), + ), + ); +}; + +/** + * ### ⚠️⚠️⚠️ this method only be used in test environment + * + * wait subscribe emit one value until an timer + * if timer exceeded, throw timeout error + * + * and also support `jest fake timer`, that will run each timer in next tick with fake `100ms` with custom timeout + * + * @default timeout 5000ms + */ +export const waitSubscribeSuccess = >( + obs$: T, + cb: (value?: R) => K, + options: { + timeout?: number; + } = {}, +) => { + return firstValueFrom(waitSubscribeSuccess$(obs$, cb, options)); +}; + +/** + * subscribe to redux to check value be expected + * + * ```ts + * try { + * const result = await subscribeWaitUntilTo( + * this, + * () => { + * if (this._home.enable) { + * return true; + * } + * throw new Error('EE'); + * }, + * { + * timeout: 1000, // wait timeout, default be 5000 + * }, + * ); + + * console.log(result); // to be true when `this._home.enable` be true + * } catch (error) { + * console.log(error); // to be latest error `new Error('EE')` + * } + * ``` + * + * that be useful in test environment, like + */ +export const subscribeWaitUntilTo = async ( + target: any, + cb: () => T, + options: { + timeout?: number; + } = {}, +) => { + try { + const value = cb(); + return Promise.resolve(value); + } catch (error) { + const obs$ = fromSubscribe(target); + + return waitSubscribeSuccess(obs$, (v) => cb(), options); + } +}; + +/** + * when redux state change, trigger `callback` to check value be expected + * + * with `subscribeWaitUntilTo` method + * + * ### ⚠️⚠️⚠️ this method only be used in test environment + */ +export const whenStateChange = ( + cb: () => T, + options?: { + timeout?: number; + }, +) => { + if (process.env.NODE_ENV !== 'test') { + throw new Error('waitStateChange can only be used in the test environment'); + } + + return subscribeWaitUntilTo( + // @ts-ignore + global.instance.phone, + cb, + options, + ); +}; + +/** + * combine with `waitUntilTo` method to polling check also `whenStateChange` method with redux state change + * + * ### ⚠️⚠️⚠️ this method only be used in test environment + */ +export const whenStateOrTimerChange = async ( + cb: () => T, + options?: { + timeout?: number; + }, +) => { + if (process.env.NODE_ENV !== 'test') { + throw new Error('waitStateChange can only be used in the test environment'); + } + + const pollingCheck = waitUntilTo(cb, options); + + const obs$ = fromSubscribe( + // @ts-ignore + global.instance.phone, + ); + + const result = await firstValueFrom( + merge( + waitSubscribeSuccess$(obs$, (v) => cb(), options), + pollingCheck, + ).pipe( + finalize(() => { + pollingCheck.cancel(true); + }), + ), + ); + + return result; +}; diff --git a/packages/engage-voice-widget/agentScript/agentScript.ts b/packages/engage-voice-widget/agentScript/agentScript.ts index cb53d815df..a30f5fabcc 100644 --- a/packages/engage-voice-widget/agentScript/agentScript.ts +++ b/packages/engage-voice-widget/agentScript/agentScript.ts @@ -1,6 +1,5 @@ -import { EventEmitter } from 'events'; - import { SingleTabBroadcastChannel } from '@ringcentral-integration/commons/lib/SingleTabBroadcastChannel'; +import { EventEmitter } from 'events'; import { agentScriptEvents, diff --git a/packages/engage-voice-widget/components/ActiveCallListPanel/ActiveCallListPanel.tsx b/packages/engage-voice-widget/components/ActiveCallListPanel/ActiveCallListPanel.tsx index af0f539266..2bdc8afcc7 100644 --- a/packages/engage-voice-widget/components/ActiveCallListPanel/ActiveCallListPanel.tsx +++ b/packages/engage-voice-widget/components/ActiveCallListPanel/ActiveCallListPanel.tsx @@ -1,8 +1,7 @@ +import clsx from 'clsx'; import type { FunctionComponent } from 'react'; import React from 'react'; -import classNames from 'classnames'; - import type { EvActiveCallListUIFunctions, EvActiveCallListUIProps, @@ -14,6 +13,7 @@ import { HoldCallButton, MuteCallButton, } from '../SmallCallControl'; + import i18n from './i18n'; import styles from './styles.scss'; @@ -53,7 +53,7 @@ const ActiveCallListPanel: FunctionComponent = ({
- + {formatPhoneNumber({ phoneNumber: everyoneCaller.session.phone, currentLocale, @@ -81,9 +81,7 @@ const ActiveCallListPanel: FunctionComponent = ({
- + {`${ownCall.agentName || userName || ''}(${i18n.getString( 'me', currentLocale, @@ -114,9 +112,7 @@ const ActiveCallListPanel: FunctionComponent = ({ ?.destination; return (
- + {formatPhoneNumber({ phoneNumber: destination, currentLocale, diff --git a/packages/engage-voice-widget/components/ActiveCallListPanel/ActiveCallListPanel.ut.tsx b/packages/engage-voice-widget/components/ActiveCallListPanel/ActiveCallListPanel.ut.tsx index 1cd7d2ac15..f464987915 100644 --- a/packages/engage-voice-widget/components/ActiveCallListPanel/ActiveCallListPanel.ut.tsx +++ b/packages/engage-voice-widget/components/ActiveCallListPanel/ActiveCallListPanel.ut.tsx @@ -1,13 +1,12 @@ -import React from 'react'; - -import { mount } from 'enzyme'; - import type { StepFunction } from '@ringcentral-integration/test-utils'; import type { DeepPartial } from '@ringcentral/juno'; import { RcThemeProvider } from '@ringcentral/juno'; +import { mount } from 'enzyme'; +import React from 'react'; import type { EvCallData } from '../../interfaces'; import i18n from '../SmallCallControl/i18n'; + import { ActiveCallListPanel } from './ActiveCallListPanel'; let wrapper; @@ -87,7 +86,7 @@ export const UTGoBackPage: StepFunction = async () => { .at(0) .find('button') .simulate('click'); - expect(goBack).toBeCalled(); + expect(goBack).toHaveBeenCalled(); wrapperUnmount(); }; @@ -115,8 +114,8 @@ export const UTHoldRender: StepFunction = () => { }); expect(holdButton.title).toBe(i18n.getString('onHold')); holdButton.click(); - expect(onHold).not.toBeCalled(); - expect(onUnHold).toBeCalledWith(defaultCallList[itemIndex]); + expect(onHold).not.toHaveBeenCalled(); + expect(onUnHold).toHaveBeenCalledWith(defaultCallList[itemIndex]); wrapperUnmount(); }; @@ -132,8 +131,8 @@ export const UTUnholdRender: StepFunction = () => { }); expect(holdButton.title).toBe(i18n.getString('hold')); holdButton.click(); - expect(onHold).toBeCalledWith(defaultCallList[itemIndex]); - expect(onUnHold).not.toBeCalled(); + expect(onHold).toHaveBeenCalledWith(defaultCallList[itemIndex]); + expect(onUnHold).not.toHaveBeenCalled(); wrapperUnmount(); }; @@ -148,7 +147,7 @@ export const UTHangUpRender: StepFunction = () => { }); expect(HangUpButton.title).toBe(i18n.getString('hangup')); HangUpButton.click(); - expect(onHangup).toBeCalledWith(defaultCallList[itemIndex]); + expect(onHangup).toHaveBeenCalledWith(defaultCallList[itemIndex]); wrapperUnmount(); }; @@ -162,7 +161,7 @@ export const UTMuteRender: StepFunction = () => { }); expect(muteButton.title).toBe(i18n.getString('mute')); muteButton.click(); - expect(onMute).toBeCalledTimes(1); + expect(onMute).toHaveBeenCalledTimes(1); wrapperUnmount(); }; @@ -176,6 +175,6 @@ export const UTUnMuteRender: StepFunction = () => { }); expect(unMuteButton.title).toBe(i18n.getString('unmute')); unMuteButton.click(); - expect(onUnmute).toBeCalledTimes(1); + expect(onUnmute).toHaveBeenCalledTimes(1); wrapperUnmount(); }; diff --git a/packages/engage-voice-widget/components/ActiveCallListPanel/i18n/en-US.ts b/packages/engage-voice-widget/components/ActiveCallListPanel/i18n/en-US.ts index 8f2b45cd61..5f9f3a6bc4 100644 --- a/packages/engage-voice-widget/components/ActiveCallListPanel/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/ActiveCallListPanel/i18n/en-US.ts @@ -5,4 +5,4 @@ export default { caller: 'Caller', callee: 'Callee', unknown: 'Unknown', -}; +} as const; diff --git a/packages/engage-voice-widget/components/ActiveCallListPanel/i18n/index.ts b/packages/engage-voice-widget/components/ActiveCallListPanel/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/ActiveCallListPanel/i18n/index.ts +++ b/packages/engage-voice-widget/components/ActiveCallListPanel/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/ActivityCallLogPanel/ActivityCallLogPanel.spec.tsx b/packages/engage-voice-widget/components/ActivityCallLogPanel/ActivityCallLogPanel.spec.tsx index c1ea1fcf8e..d536874485 100644 --- a/packages/engage-voice-widget/components/ActivityCallLogPanel/ActivityCallLogPanel.spec.tsx +++ b/packages/engage-voice-widget/components/ActivityCallLogPanel/ActivityCallLogPanel.spec.tsx @@ -1,15 +1,14 @@ -import React from 'react'; - -import { mount } from 'enzyme'; - import { RcAccordion, RcAccordionSummary, RcThemeProvider, } from '@ringcentral/juno'; +import { mount } from 'enzyme'; +import React from 'react'; import type { EvCallData, EvCurrentLog, EvIvrData } from '../../interfaces'; import i18n from '../SmallCallControl/i18n'; + import type { ActivityCallLogPanelProps } from './ActivityCallLogPanel'; import { ActivityCallLogPanel } from './ActivityCallLogPanel'; @@ -153,7 +152,7 @@ describe(':: Call Disposition', () => { const dispositionButton = getDispositionButton(); expect(dispositionButton.isExist).toBe(true); dispositionButton.click(); - expect(disposeCall).toBeCalled(); + expect(disposeCall).toHaveBeenCalled(); }); it('When User click the Disposition Button, Submit Button is in loading status and cannot be clicked', () => { @@ -168,7 +167,7 @@ describe(':: Call Disposition', () => { expect(dispositionButton.isInLoadingStatus).toBe(true); expect(dispositionButton.isDisabled).toBe(true); dispositionButton.click(); - expect(disposeCall).not.toBeCalled(); + expect(disposeCall).not.toHaveBeenCalled(); }); it('When disposition is saved, Submit Button is back in normal state: enabled and can be clicked', () => { @@ -182,7 +181,7 @@ describe(':: Call Disposition', () => { expect(dispositionButton.isInLoadingStatus).toBe(false); expect(dispositionButton.isDisabled).toBe(false); dispositionButton.click(); - expect(disposeCall).toBeCalled(); + expect(disposeCall).toHaveBeenCalled(); }); it('When disableDispose, Disposition Button should be disabled and cannot be clicked', () => { @@ -195,7 +194,7 @@ describe(':: Call Disposition', () => { const dispositionButton = getDispositionButton(); expect(dispositionButton.isDisabled).toBe(true); dispositionButton.click(); - expect(disposeCall).not.toBeCalled(); + expect(disposeCall).not.toHaveBeenCalled(); }); }); @@ -213,8 +212,8 @@ describe('', () => { const holdButton = getControlButton('HoldCallButton'); holdButton.click(); expect(holdButton.title).toBe(i18n.getString('onHold')); - expect(onUnHold).toBeCalled(); - expect(onHold).not.toBeCalled(); + expect(onUnHold).toHaveBeenCalled(); + expect(onHold).not.toHaveBeenCalled(); }); it('When call is unHold, HoldCallButton should display and work correctly', () => { @@ -231,8 +230,8 @@ describe('', () => { holdButton.click(); expect(holdButton.title).toBe(i18n.getString('hold')); - expect(onUnHold).not.toBeCalled(); - expect(onHold).toBeCalled(); + expect(onUnHold).not.toHaveBeenCalled(); + expect(onHold).toHaveBeenCalled(); }); [ @@ -268,8 +267,8 @@ describe('', () => { muteButton.click(); expect(muteButton.title).toBe(i18n.getString('unmute')); - expect(onUnmute).toBeCalled(); - expect(onMute).not.toBeCalled(); + expect(onUnmute).toHaveBeenCalled(); + expect(onMute).not.toHaveBeenCalled(); }); it('When call is unMute, MuteCallButton should display and work correctly', () => { @@ -286,8 +285,8 @@ describe('', () => { const muteButton = getControlButton('MuteCallButton'); muteButton.click(); expect(muteButton.title).toBe(i18n.getString('mute')); - expect(onUnmute).not.toBeCalled(); - expect(onMute).toBeCalled(); + expect(onUnmute).not.toHaveBeenCalled(); + expect(onMute).toHaveBeenCalled(); }); it('User can transfer an Call', () => { @@ -364,7 +363,7 @@ describe('', () => { expect(getControlButton('HangUpButton').isExist).toBe(false); wrapper.find('ActiveCallButton').find('button').simulate('click'); - expect(onActive).toBeCalled(); + expect(onActive).toHaveBeenCalled(); }); it('When user on the InComing Call, can see the Reject Button', () => { @@ -378,7 +377,7 @@ describe('', () => { const hangupButton = getControlButton('HangUpButton'); expect(hangupButton.title).toBe(i18n.getString('reject')); hangupButton.click(); - expect(onReject).toBeCalled(); + expect(onReject).toHaveBeenCalled(); }); it('When the call is not InComing Call, can see the Hangup button', () => { @@ -392,7 +391,7 @@ describe('', () => { const hangupButton = getControlButton('HangUpButton'); expect(hangupButton.title).toBe(i18n.getString('hangup')); hangupButton.click(); - expect(onHangup).toBeCalled(); + expect(onHangup).toHaveBeenCalled(); }); [ diff --git a/packages/engage-voice-widget/components/ActivityCallLogPanel/ActivityCallLogPanel.tsx b/packages/engage-voice-widget/components/ActivityCallLogPanel/ActivityCallLogPanel.tsx index e5531517f0..b93072276b 100644 --- a/packages/engage-voice-widget/components/ActivityCallLogPanel/ActivityCallLogPanel.tsx +++ b/packages/engage-voice-widget/components/ActivityCallLogPanel/ActivityCallLogPanel.tsx @@ -1,13 +1,11 @@ -import type { FunctionComponent } from 'react'; -import React, { useCallback, useRef, useState } from 'react'; - -import classNames from 'classnames'; - import { BasicCallInfo } from '@ringcentral-integration/widgets/components/BasicCallInfo'; import type { CallLogPanelProps } from '@ringcentral-integration/widgets/components/CallLogPanel'; import CallLogPanel from '@ringcentral-integration/widgets/components/CallLogPanel'; import { RcButton, RcIconButton, RcMenu, RcMenuItem } from '@ringcentral/juno'; import { Transcription } from '@ringcentral/juno-icon'; +import clsx from 'clsx'; +import type { FunctionComponent } from 'react'; +import React, { useCallback, useRef, useState } from 'react'; import { transferTypes } from '../../enums'; import type { @@ -15,283 +13,283 @@ import type { EvActivityCallUIProps, } from '../../interfaces/EvActivityCallUI.interface'; import { EvSmallCallControl } from '../EvSmallCallControl'; -import i18n from './i18n'; -import { IvrInfo } from './IvrInfo'; -import { KeypadCollapse } from './KeypadCollapse'; -import styles from './styles.scss'; -import { EditLogSection, getButtonText } from './utils'; + import { StyledAgentScriptIcon, SubmitButtonWrapper, } from './ActivityCallLogWrapper'; +import { IvrInfo } from './IvrInfo'; +import { KeypadCollapse } from './KeypadCollapse'; +import i18n from './i18n'; +import styles from './styles.scss'; +import { EditLogSection, getButtonText } from './utils'; export type ActivityCallLogPanelProps = EvActivityCallUIProps & EvActivityCallUIFunctions & Pick; -export const ActivityCallLogPanel: FunctionComponent = - ({ - currentLocale, - currentLog, - basicInfo, - isInbound, - disposeCall, - status, - saveStatus, - goToRequeueCallPage, - goToTransferCallPage, - onMute, - onUnmute, - onHangup, - onReject, - onHold, - onUnHold, - isOnMute, - isOnHold, - smallCallControlSize, - isInComingCall, - currentCallControlPermission: { allowTransferCall, allowRequeueCall }, - disableDispose, - disableTransfer, - disableInternalTransfer, - disableHold, - disableHangup, - disableMute, - disableActive, - isOnActive, - onActive, - isWide, - showMuteButton, - ivrAlertData, - agentScriptData, - onCopySuccess, - scrollTo, - referenceFieldOptions, - showRecordCall, - recordPauseCount, - disableRecordControl, - isRecording, - onResumeRecord, - timeStamp, - onRecord, - onPauseRecord, - onRestartTimer, - onStopRecord, - disablePauseRecord, - isKeypadOpen, - keypadValue, - setKeypadIsOpen, - setKeypadValue, - ...rest - }) => { - const transferRef = useRef(null); - const rootRef = useRef(null); - const [transferEl, setTransferRef] = useState(null); - const isActivity = status === 'active'; - const isCallEnd = status === 'callEnd'; - const isLoading = saveStatus === 'saving'; - - const onTransfer = () => { - setTransferRef(transferRef.current); - }; +export const ActivityCallLogPanel: FunctionComponent< + ActivityCallLogPanelProps +> = ({ + currentLocale, + currentLog, + basicInfo, + isInbound, + disposeCall, + status, + saveStatus, + goToRequeueCallPage, + goToTransferCallPage, + onMute, + onUnmute, + onHangup, + onReject, + onHold, + onUnHold, + isOnMute, + isOnHold, + smallCallControlSize, + isInComingCall, + currentCallControlPermission: { allowTransferCall, allowRequeueCall }, + disableDispose, + disableTransfer, + disableInternalTransfer, + disableHold, + disableHangup, + disableMute, + disableActive, + isOnActive, + onActive, + isWide, + showMuteButton, + ivrAlertData, + agentScriptData, + onCopySuccess, + scrollTo, + referenceFieldOptions, + showRecordCall, + recordPauseCount, + disableRecordControl, + isRecording, + onResumeRecord, + timeStamp, + onRecord, + onPauseRecord, + onRestartTimer, + onStopRecord, + disablePauseRecord, + isKeypadOpen, + keypadValue, + setKeypadIsOpen, + setKeypadValue, + ...rest +}) => { + const transferRef = useRef(null); + const rootRef = useRef(null); + const [transferEl, setTransferRef] = useState(null); + const isActivity = status === 'active'; + const isCallEnd = status === 'callEnd'; + const isLoading = saveStatus === 'saving'; - const handleTransferClose = () => { - setTransferRef(null); - }; + const onTransfer = () => { + setTransferRef(transferRef.current); + }; - const callControlRef = useRef(null); + const handleTransferClose = () => { + setTransferRef(null); + }; - const editLogSection = useCallback( - (props) => ( - - ), - [isWide, scrollTo, referenceFieldOptions], - ); + const callControlRef = useRef(null); - return ( - ( + { - return ( + {...props} + scrollTo={scrollTo} + rootRef={rootRef.current?.editSectionRef} + referenceFieldOptions={referenceFieldOptions} + /> + ), + [isWide, scrollTo, referenceFieldOptions], + ); + + return ( + { + return ( + <> + + {ivrAlertData?.length > 0 && ( + + )} + {agentScriptData && ( + + )} + + ); + }} + renderKeypadPanel={() => { + return ( + !isCallEnd && ( <> - - {ivrAlertData?.length > 0 && ( - - )} - {agentScriptData && ( - - )} - ); - }} - renderKeypadPanel={() => { - return ( - !isCallEnd && ( - <> - - - ) - ); - }} - renderCallLogCallControl={() => { - const isOnTransfer = Boolean(transferEl); - return ( - !isCallEnd && ( - <> - { + const isOnTransfer = Boolean(transferEl); + return ( + !isCallEnd && ( + <> + + goToTransferCallPage(transferTypes.internal)} + disabled={!allowTransferCall || disableInternalTransfer} + data-sign="transferItem-internalTransfer" > - goToTransferCallPage(transferTypes.internal)} - disabled={!allowTransferCall || disableInternalTransfer} - data-sign="transferItem-internalTransfer" - > - {i18n.getString('internalTransfer', currentLocale)} - - - goToTransferCallPage(transferTypes.phoneBook) - } - disabled={!allowTransferCall} - data-sign="transferItem-phoneBookTransfer" - > - {i18n.getString('phoneBookTransfer', currentLocale)} - - goToRequeueCallPage()} - disabled={!allowRequeueCall} - data-sign="transferItem-queueTransfer" - > - {i18n.getString('queueTransfer', currentLocale)} - - - goToTransferCallPage(transferTypes.manualEntry) - } - disabled={!allowTransferCall} - data-sign="transferItem-enterANumber" - > - {i18n.getString('enterANumber', currentLocale)} - - - - - ) - ); - }} - > - {isCallEnd && ( - - disposeCall()} - > - {getButtonText(saveStatus, currentLocale)} - - - )} - - ); - }; + {i18n.getString('internalTransfer', currentLocale)} + + goToTransferCallPage(transferTypes.phoneBook)} + disabled={!allowTransferCall} + data-sign="transferItem-phoneBookTransfer" + > + {i18n.getString('phoneBookTransfer', currentLocale)} + + goToRequeueCallPage()} + disabled={!allowRequeueCall} + data-sign="transferItem-queueTransfer" + > + {i18n.getString('queueTransfer', currentLocale)} + + + goToTransferCallPage(transferTypes.manualEntry) + } + disabled={!allowTransferCall} + data-sign="transferItem-enterANumber" + > + {i18n.getString('enterANumber', currentLocale)} + + + + + ) + ); + }} + > + {isCallEnd && ( + + disposeCall()} + > + {getButtonText(saveStatus, currentLocale)} + + + )} + + ); +}; ActivityCallLogPanel.defaultProps = { basicInfo: {}, diff --git a/packages/engage-voice-widget/components/ActivityCallLogPanel/ActivityCallLogWrapper.tsx b/packages/engage-voice-widget/components/ActivityCallLogPanel/ActivityCallLogWrapper.tsx index 141550b58b..1a22a91f8b 100644 --- a/packages/engage-voice-widget/components/ActivityCallLogPanel/ActivityCallLogWrapper.tsx +++ b/packages/engage-voice-widget/components/ActivityCallLogPanel/ActivityCallLogWrapper.tsx @@ -1,6 +1,6 @@ +import { SubmitButtonHeight } from '@ringcentral-integration/widgets/components/BasicCallInfo/BasicCallInfo'; import { styled, px, RcIconButton } from '@ringcentral/juno'; -import { SubmitButtonHeight } from '@ringcentral-integration/widgets/components/BasicCallInfo/BasicCallInfo'; import { pageSpace } from '../../scss/variables'; export const StyledAgentScriptIcon = styled(RcIconButton)` diff --git a/packages/engage-voice-widget/components/ActivityCallLogPanel/IvrInfo/IvrInfo.tsx b/packages/engage-voice-widget/components/ActivityCallLogPanel/IvrInfo/IvrInfo.tsx index 98c42a5ff8..a85d83e204 100644 --- a/packages/engage-voice-widget/components/ActivityCallLogPanel/IvrInfo/IvrInfo.tsx +++ b/packages/engage-voice-widget/components/ActivityCallLogPanel/IvrInfo/IvrInfo.tsx @@ -1,19 +1,18 @@ -import type { FunctionComponent } from 'react'; -import React, { useEffect, useState } from 'react'; - -import classNames from 'classnames'; - import { RcAccordion, RcAccordionDetails, RcAccordionSummary, } from '@ringcentral/juno'; import { ArrowDown2 } from '@ringcentral/juno-icon'; +import clsx from 'clsx'; +import type { FunctionComponent } from 'react'; +import React, { useEffect, useState } from 'react'; import type { EvActivityCallUIFunctions, EvActivityCallUIProps, } from '../../../interfaces'; + import styles from './styles.scss'; export type IvrInfoProps = { isCallEnd: boolean } & Pick< @@ -41,17 +40,17 @@ export const IvrInfo: FunctionComponent = ({ onChange={() => setExpanded(!expanded)} expanded={expanded} classes={{ - root: classNames(styles.panelRoot, isCallEnd && styles.endCall), + root: clsx(styles.panelRoot, isCallEnd && styles.endCall), expanded: styles.expanded, }} > = ({ expandIcon={ArrowDown2} > = ({ }} /> )} - + diff --git a/packages/engage-voice-widget/components/ActivityCallLogPanel/KeypadCollapse/i18n/en-US.ts b/packages/engage-voice-widget/components/ActivityCallLogPanel/KeypadCollapse/i18n/en-US.ts index 65e495bdd5..68f3dbe76a 100644 --- a/packages/engage-voice-widget/components/ActivityCallLogPanel/KeypadCollapse/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/ActivityCallLogPanel/KeypadCollapse/i18n/en-US.ts @@ -1,4 +1,4 @@ export default { close: 'Close', keypad: 'Keypad', -}; +} as const; diff --git a/packages/engage-voice-widget/components/ActivityCallLogPanel/KeypadCollapse/i18n/index.ts b/packages/engage-voice-widget/components/ActivityCallLogPanel/KeypadCollapse/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/ActivityCallLogPanel/KeypadCollapse/i18n/index.ts +++ b/packages/engage-voice-widget/components/ActivityCallLogPanel/KeypadCollapse/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/ActivityCallLogPanel/KeypadCollapse/styles/KeyPadWrapper.tsx b/packages/engage-voice-widget/components/ActivityCallLogPanel/KeypadCollapse/styles/KeyPadWrapper.tsx index af515263a4..b477a436f1 100644 --- a/packages/engage-voice-widget/components/ActivityCallLogPanel/KeypadCollapse/styles/KeyPadWrapper.tsx +++ b/packages/engage-voice-widget/components/ActivityCallLogPanel/KeypadCollapse/styles/KeyPadWrapper.tsx @@ -1,3 +1,4 @@ +import { KeyPadHeight } from '@ringcentral-integration/widgets/components/BasicCallInfo/BasicCallInfo'; import { css, flexCenterStyle, @@ -12,7 +13,6 @@ import { px, } from '@ringcentral/juno'; -import { KeyPadHeight } from '@ringcentral-integration/widgets/components/BasicCallInfo/BasicCallInfo'; import { pageSpace } from '../../../../scss/variables'; export const Wrapper = styled.div<{ open?: boolean }>` diff --git a/packages/engage-voice-widget/components/ActivityCallLogPanel/i18n/en-US.ts b/packages/engage-voice-widget/components/ActivityCallLogPanel/i18n/en-US.ts index 99578ab2ca..11393449fd 100644 --- a/packages/engage-voice-widget/components/ActivityCallLogPanel/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/ActivityCallLogPanel/i18n/en-US.ts @@ -8,4 +8,4 @@ export default { queueTransfer: 'Queue transfer', enterANumber: 'Enter a number', engageScript: 'Engage script', -}; +} as const; diff --git a/packages/engage-voice-widget/components/ActivityCallLogPanel/i18n/index.ts b/packages/engage-voice-widget/components/ActivityCallLogPanel/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/ActivityCallLogPanel/i18n/index.ts +++ b/packages/engage-voice-widget/components/ActivityCallLogPanel/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/ActivityCallLogPanel/styles.scss b/packages/engage-voice-widget/components/ActivityCallLogPanel/styles.scss index e96205c298..863dfaa2e4 100644 --- a/packages/engage-voice-widget/components/ActivityCallLogPanel/styles.scss +++ b/packages/engage-voice-widget/components/ActivityCallLogPanel/styles.scss @@ -38,4 +38,3 @@ $classic-content-width: 144px; left: 48px !important; } } - diff --git a/packages/engage-voice-widget/components/ActivityCallLogPanel/utils/EditLogSection.tsx b/packages/engage-voice-widget/components/ActivityCallLogPanel/utils/EditLogSection.tsx index 7b147f7cf8..ba028f335c 100644 --- a/packages/engage-voice-widget/components/ActivityCallLogPanel/utils/EditLogSection.tsx +++ b/packages/engage-voice-widget/components/ActivityCallLogPanel/utils/EditLogSection.tsx @@ -1,9 +1,8 @@ -import type { FunctionComponent, MutableRefObject } from 'react'; -import React, { useEffect, useRef } from 'react'; - import type { CallLogFieldsProps } from '@ringcentral-integration/widgets/components/CallLogFields'; import CallLogFields from '@ringcentral-integration/widgets/components/CallLogFields'; import type { CallLogPanelProps } from '@ringcentral-integration/widgets/components/CallLogPanel'; +import type { FunctionComponent, MutableRefObject } from 'react'; +import React, { useEffect, useRef } from 'react'; import styles from './styles.scss'; diff --git a/packages/engage-voice-widget/components/ActivityCallLogPanel/utils/getButtonText.tsx b/packages/engage-voice-widget/components/ActivityCallLogPanel/utils/getButtonText.tsx index 2fbba43e4b..c8eb2b4f70 100644 --- a/packages/engage-voice-widget/components/ActivityCallLogPanel/utils/getButtonText.tsx +++ b/packages/engage-voice-widget/components/ActivityCallLogPanel/utils/getButtonText.tsx @@ -1,7 +1,6 @@ -import React from 'react'; - import { RcIcon } from '@ringcentral/juno'; import { Check } from '@ringcentral/juno-icon'; +import React from 'react'; import type { EvActivityCallUIProps } from '../../../interfaces'; import i18n from '../i18n'; diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvAuthAlert/EvAuthAlert.tsx b/packages/engage-voice-widget/components/AlertRenderer/EvAuthAlert/EvAuthAlert.tsx index 2c1fe5e449..f5b2297875 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvAuthAlert/EvAuthAlert.tsx +++ b/packages/engage-voice-widget/components/AlertRenderer/EvAuthAlert/EvAuthAlert.tsx @@ -1,6 +1,7 @@ import { includes } from 'ramda'; import { messageTypes } from '../../../enums'; + import i18n from './i18n'; interface EvAuthAlertProps { diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvAuthAlert/i18n/en-US.ts b/packages/engage-voice-widget/components/AlertRenderer/EvAuthAlert/i18n/en-US.ts index e96a4a32fc..a0cb8edf02 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvAuthAlert/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/AlertRenderer/EvAuthAlert/i18n/en-US.ts @@ -11,4 +11,4 @@ export default { [messageTypes.OPEN_SOCKET_ERROR]: 'Connect socket error. Please retry later.', [messageTypes.EXISTING_LOGIN_ENGAGED]: 'Existing login engaged', [messageTypes.FORCE_LOGOUT]: 'Your logon session has been terminated', -}; +} as const; diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvAuthAlert/i18n/index.ts b/packages/engage-voice-widget/components/AlertRenderer/EvAuthAlert/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvAuthAlert/i18n/index.ts +++ b/packages/engage-voice-widget/components/AlertRenderer/EvAuthAlert/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvCallAlert/EvCallAlert.tsx b/packages/engage-voice-widget/components/AlertRenderer/EvCallAlert/EvCallAlert.tsx index cd1a39bbb3..42e448685f 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvCallAlert/EvCallAlert.tsx +++ b/packages/engage-voice-widget/components/AlertRenderer/EvCallAlert/EvCallAlert.tsx @@ -1,6 +1,7 @@ import { includes } from 'ramda'; import { messageTypes } from '../../../enums'; + import i18n from './i18n'; interface EvCallAlertProps { diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvCallAlert/i18n/en-US.ts b/packages/engage-voice-widget/components/AlertRenderer/EvCallAlert/i18n/en-US.ts index 312befa0d3..a26b473479 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvCallAlert/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/AlertRenderer/EvCallAlert/i18n/en-US.ts @@ -18,4 +18,4 @@ export default { [messageTypes.RECORD_RESUME]: 'Call recording resumed.', [messageTypes.INTERCEPT]: 'The dial result for your manual outbound call was INTERCEPT.', -}; +} as const; diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvCallAlert/i18n/index.ts b/packages/engage-voice-widget/components/AlertRenderer/EvCallAlert/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvCallAlert/i18n/index.ts +++ b/packages/engage-voice-widget/components/AlertRenderer/EvCallAlert/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvCallDispositionAlert/EvCallDispositionAlert.tsx b/packages/engage-voice-widget/components/AlertRenderer/EvCallDispositionAlert/EvCallDispositionAlert.tsx index 3bec8fd704..44bf4d4657 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvCallDispositionAlert/EvCallDispositionAlert.tsx +++ b/packages/engage-voice-widget/components/AlertRenderer/EvCallDispositionAlert/EvCallDispositionAlert.tsx @@ -1,6 +1,7 @@ import { includes } from 'ramda'; import { logTypes } from '../../../enums/logTypes'; + import i18n from './i18n'; interface EvCallDispositionAlertProps { diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvCallDispositionAlert/i18n/en-US.ts b/packages/engage-voice-widget/components/AlertRenderer/EvCallDispositionAlert/i18n/en-US.ts index a3b491d80f..f2f8c3d54b 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvCallDispositionAlert/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/AlertRenderer/EvCallDispositionAlert/i18n/en-US.ts @@ -7,4 +7,4 @@ export default { [logTypes.CALL_LOG_CREATE_FAILURE]: 'Failed to create log. Try again later.', [logTypes.CALL_LOG_UPDATE_SUCCESS]: 'Call log updated.', [logTypes.CALL_LOG_UPDATE_FAILURE]: 'Failed to update log. Try again later.', -}; +} as const; diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvCallDispositionAlert/i18n/index.ts b/packages/engage-voice-widget/components/AlertRenderer/EvCallDispositionAlert/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvCallDispositionAlert/i18n/index.ts +++ b/packages/engage-voice-widget/components/AlertRenderer/EvCallDispositionAlert/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvCallInfoAlert/EvCallInfoAlert.tsx b/packages/engage-voice-widget/components/AlertRenderer/EvCallInfoAlert/EvCallInfoAlert.tsx index fb1695702d..48ba3a9727 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvCallInfoAlert/EvCallInfoAlert.tsx +++ b/packages/engage-voice-widget/components/AlertRenderer/EvCallInfoAlert/EvCallInfoAlert.tsx @@ -1,6 +1,7 @@ import { includes } from 'ramda'; import { messageTypes } from '../../../enums/messageTypes'; + import i18n from './i18n'; interface EvCallInfoAlertProps { diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvCallInfoAlert/i18n/en-US.ts b/packages/engage-voice-widget/components/AlertRenderer/EvCallInfoAlert/i18n/en-US.ts index 161da6a60c..0b47f8c656 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvCallInfoAlert/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/AlertRenderer/EvCallInfoAlert/i18n/en-US.ts @@ -2,4 +2,4 @@ import { messageTypes } from '../../../../enums/messageTypes'; export default { [messageTypes.COPY_UII_SUCCESS]: 'Call ID copied', -}; +} as const; diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvCallInfoAlert/i18n/index.ts b/packages/engage-voice-widget/components/AlertRenderer/EvCallInfoAlert/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvCallInfoAlert/i18n/index.ts +++ b/packages/engage-voice-widget/components/AlertRenderer/EvCallInfoAlert/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvIntegratedSoftphoneAlert/EvIntegratedSoftphoneAlert.tsx b/packages/engage-voice-widget/components/AlertRenderer/EvIntegratedSoftphoneAlert/EvIntegratedSoftphoneAlert.tsx index be99c264c6..6d99d28788 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvIntegratedSoftphoneAlert/EvIntegratedSoftphoneAlert.tsx +++ b/packages/engage-voice-widget/components/AlertRenderer/EvIntegratedSoftphoneAlert/EvIntegratedSoftphoneAlert.tsx @@ -2,6 +2,7 @@ import { includes } from 'ramda'; import { EvSoftphoneEvents, tabManagerEvents } from '../../../enums'; import { EvCallbackTypes } from '../../../lib/EvClient/enums'; + import i18n from './i18n'; interface EvIntegratedSoftphoneAlertProps { diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvIntegratedSoftphoneAlert/i18n/en-US.ts b/packages/engage-voice-widget/components/AlertRenderer/EvIntegratedSoftphoneAlert/i18n/en-US.ts index 434433e21b..3bf26d98fb 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvIntegratedSoftphoneAlert/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/AlertRenderer/EvIntegratedSoftphoneAlert/i18n/en-US.ts @@ -16,4 +16,4 @@ export default { 'You have an incoming call. Switch to the browser tab with the blue flashing dot to answer the call', // Attempt to dequeue call to agent failed! Outdial to destination [16503990023*106] failed after [2] seconds with disposition [INTERCEPT] -}; +} as const; diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvIntegratedSoftphoneAlert/i18n/index.ts b/packages/engage-voice-widget/components/AlertRenderer/EvIntegratedSoftphoneAlert/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvIntegratedSoftphoneAlert/i18n/index.ts +++ b/packages/engage-voice-widget/components/AlertRenderer/EvIntegratedSoftphoneAlert/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvRequeueCallAlert/EvRequeueCallAlert.tsx b/packages/engage-voice-widget/components/AlertRenderer/EvRequeueCallAlert/EvRequeueCallAlert.tsx index fb0076f1a9..1abe4b05c2 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvRequeueCallAlert/EvRequeueCallAlert.tsx +++ b/packages/engage-voice-widget/components/AlertRenderer/EvRequeueCallAlert/EvRequeueCallAlert.tsx @@ -1,6 +1,7 @@ import { includes } from 'ramda'; import { requeueEvents } from '../../../enums'; + import i18n from './i18n'; interface EvRequeueCallAlertProps { diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvRequeueCallAlert/i18n/en-US.ts b/packages/engage-voice-widget/components/AlertRenderer/EvRequeueCallAlert/i18n/en-US.ts index b79958db99..21a7b42730 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvRequeueCallAlert/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/AlertRenderer/EvRequeueCallAlert/i18n/en-US.ts @@ -4,4 +4,4 @@ export default { [requeueEvents.FAILURE]: 'Call queue transfer is failed', [requeueEvents.START]: 'Call queue transfer in progress', [requeueEvents.SUCCESS]: 'Call queue transfer is completed', -}; +} as const; diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvRequeueCallAlert/i18n/index.ts b/packages/engage-voice-widget/components/AlertRenderer/EvRequeueCallAlert/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvRequeueCallAlert/i18n/index.ts +++ b/packages/engage-voice-widget/components/AlertRenderer/EvRequeueCallAlert/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvSessionConfigAlert/EvSessionConfigAlert.tsx b/packages/engage-voice-widget/components/AlertRenderer/EvSessionConfigAlert/EvSessionConfigAlert.tsx index 1b093300ce..f042654119 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvSessionConfigAlert/EvSessionConfigAlert.tsx +++ b/packages/engage-voice-widget/components/AlertRenderer/EvSessionConfigAlert/EvSessionConfigAlert.tsx @@ -1,6 +1,7 @@ import { includes } from 'ramda'; import { messageTypes } from '../../../enums'; + import i18n from './i18n'; interface EvSessionConfigAlertProps { diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvSessionConfigAlert/i18n/en-US.ts b/packages/engage-voice-widget/components/AlertRenderer/EvSessionConfigAlert/i18n/en-US.ts index 4b97cab3b0..ccdb123d25 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvSessionConfigAlert/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/AlertRenderer/EvSessionConfigAlert/i18n/en-US.ts @@ -9,4 +9,4 @@ export default { 'Sorry, no inbound queues selected.', [messageTypes.UPDATE_AGENT_ERROR]: 'Session update failed', [messageTypes.UPDATE_AGENT_SUCCESS]: 'Session updated', -}; +} as const; diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvSessionConfigAlert/i18n/index.ts b/packages/engage-voice-widget/components/AlertRenderer/EvSessionConfigAlert/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvSessionConfigAlert/i18n/index.ts +++ b/packages/engage-voice-widget/components/AlertRenderer/EvSessionConfigAlert/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvTransferCallAlert/EvTransferCallAlert.tsx b/packages/engage-voice-widget/components/AlertRenderer/EvTransferCallAlert/EvTransferCallAlert.tsx index 75379c999d..cc2b507924 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvTransferCallAlert/EvTransferCallAlert.tsx +++ b/packages/engage-voice-widget/components/AlertRenderer/EvTransferCallAlert/EvTransferCallAlert.tsx @@ -5,6 +5,7 @@ import { transferEvents, transferSuccesses, } from '../../../enums'; + import i18n from './i18n'; interface EvTransferCallAlertProps { diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvTransferCallAlert/i18n/en-US.ts b/packages/engage-voice-widget/components/AlertRenderer/EvTransferCallAlert/i18n/en-US.ts index c75597fdff..26625b55f2 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvTransferCallAlert/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/AlertRenderer/EvTransferCallAlert/i18n/en-US.ts @@ -10,4 +10,4 @@ export default { [transferSuccesses.TRANSFER_CONNECTED]: 'Call transfer is connected', [transferSuccesses.SEND_VOICEMAIL_SUCCESS]: 'Send voicemail is succeed', [transferErrors.SEND_VOICEMAIL_ERROR]: 'Send voicemail is failed', -}; +} as const; diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvTransferCallAlert/i18n/index.ts b/packages/engage-voice-widget/components/AlertRenderer/EvTransferCallAlert/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvTransferCallAlert/i18n/index.ts +++ b/packages/engage-voice-widget/components/AlertRenderer/EvTransferCallAlert/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvWorkingStateAlert/EvWorkingStateAlert.tsx b/packages/engage-voice-widget/components/AlertRenderer/EvWorkingStateAlert/EvWorkingStateAlert.tsx index 430ac109b7..30e683ad9d 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvWorkingStateAlert/EvWorkingStateAlert.tsx +++ b/packages/engage-voice-widget/components/AlertRenderer/EvWorkingStateAlert/EvWorkingStateAlert.tsx @@ -1,6 +1,7 @@ import { includes } from 'ramda'; import { messageTypes } from '../../../enums'; + import i18n from './i18n'; interface EvWorkingStateAlertProps { diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvWorkingStateAlert/i18n/en-US.ts b/packages/engage-voice-widget/components/AlertRenderer/EvWorkingStateAlert/i18n/en-US.ts index 031305fc81..758f75f760 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvWorkingStateAlert/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/AlertRenderer/EvWorkingStateAlert/i18n/en-US.ts @@ -4,4 +4,4 @@ export default { [messageTypes.OVER_BREAK_TIME]: 'Your break time is over', [messageTypes.INVALID_STATE_CHANGE]: 'Unable to process state change event. Invalid transition specified. Manual transition from OFFLINE, ENGAGED, CHAT-ENGAGED or TRANSITION is not currently allowed.', -}; +} as const; diff --git a/packages/engage-voice-widget/components/AlertRenderer/EvWorkingStateAlert/i18n/index.ts b/packages/engage-voice-widget/components/AlertRenderer/EvWorkingStateAlert/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/AlertRenderer/EvWorkingStateAlert/i18n/index.ts +++ b/packages/engage-voice-widget/components/AlertRenderer/EvWorkingStateAlert/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/BasicSessionPanel/BasicSessionPanel.spec.tsx b/packages/engage-voice-widget/components/BasicSessionPanel/BasicSessionPanel.spec.tsx index d0b124916f..a3a92a3b03 100644 --- a/packages/engage-voice-widget/components/BasicSessionPanel/BasicSessionPanel.spec.tsx +++ b/packages/engage-voice-widget/components/BasicSessionPanel/BasicSessionPanel.spec.tsx @@ -1,8 +1,6 @@ -import React from 'react'; - -import { mount } from 'enzyme'; - import { RcThemeProvider } from '@ringcentral/juno'; +import { mount } from 'enzyme'; +import React from 'react'; import type { BasicSessionPanelProps } from './BasicSessionPanel'; import { BasicSessionPanel } from './BasicSessionPanel'; @@ -123,7 +121,7 @@ describe('', () => { // expect(inboundQueuesField.prop('value')).toEqual(inboundQueuesFieldText); // inboundQueuesField.simulate('click'); - // expect(gotoInboundQueuesPage).toBeCalled(); + // expect(gotoInboundQueuesPage).toHaveBeenCalled(); // }); it('Can display extensionNumber correctly, and can be changed.', () => { @@ -146,7 +144,7 @@ describe('', () => { extensionNumberField.simulate('change', { target: { value: newExtensionNumber }, }); - expect(setExtensionNumber).toBeCalledWith(newExtensionNumber); + expect(setExtensionNumber).toHaveBeenCalledWith(newExtensionNumber); wrapper = setup({ setExtensionNumber, @@ -172,7 +170,7 @@ describe('', () => { // .at(0); // expect(takingCallToggle.prop('checked')).toEqual(takingCall); // takingCallToggle.simulate('change', { target: { value: !takingCall } }); - // expect(setTakingCall).toBeCalledWith(!takingCall); + // expect(setTakingCall).toHaveBeenCalledWith(!takingCall); // }); // }); @@ -190,7 +188,7 @@ describe('', () => { // .at(0); // expect(autoAnswerToggle.prop('checked')).toEqual(autoAnswer); // autoAnswerToggle.simulate('change', { target: { value: !autoAnswer } }); - // expect(setAutoAnswer).toBeCalledWith(!autoAnswer); + // expect(setAutoAnswer).toHaveBeenCalledWith(!autoAnswer); // }); // }); @@ -243,6 +241,6 @@ describe('', () => { document.body .querySelector(`li[data-value="${changeLoginType}"]`) .click(); - expect(setLoginType).toBeCalledWith(changeLoginType); + expect(setLoginType).toHaveBeenCalledWith(changeLoginType); }); }); diff --git a/packages/engage-voice-widget/components/BasicSessionPanel/BasicSessionPanel.tsx b/packages/engage-voice-widget/components/BasicSessionPanel/BasicSessionPanel.tsx index 6af3d25cf1..156200b391 100644 --- a/packages/engage-voice-widget/components/BasicSessionPanel/BasicSessionPanel.tsx +++ b/packages/engage-voice-widget/components/BasicSessionPanel/BasicSessionPanel.tsx @@ -1,14 +1,14 @@ -import type { FunctionComponent } from 'react'; -import React, { useState } from 'react'; - import { AnimationPanel } from '@ringcentral-integration/widgets/components/AnimationPanel'; import { CustomArrowButton } from '@ringcentral-integration/widgets/components/Rcui/CustomArrowButton'; import { RcSwitch, RcTextField } from '@ringcentral/juno'; +import type { FunctionComponent } from 'react'; +import React, { useState } from 'react'; import type { BasicSessionProps } from '../../interfaces/EvAgentSessionUI.interface'; import type { InboundQueuesPanelProps } from '../InboundQueuesPanel'; import { InboundQueuesPanel } from '../InboundQueuesPanel'; import { PickList } from '../PickList'; + import i18n from './i18n'; import styles from './styles.scss'; diff --git a/packages/engage-voice-widget/components/BasicSessionPanel/Warning.tsx b/packages/engage-voice-widget/components/BasicSessionPanel/Warning.tsx index adb7f091ef..f3361afaf2 100644 --- a/packages/engage-voice-widget/components/BasicSessionPanel/Warning.tsx +++ b/packages/engage-voice-widget/components/BasicSessionPanel/Warning.tsx @@ -1,8 +1,7 @@ +import { RcTypography } from '@ringcentral/juno'; import type { FunctionComponent } from 'react'; import React from 'react'; -import { RcTypography } from '@ringcentral/juno'; - import styles from './styles.scss'; export interface WarningProps { diff --git a/packages/engage-voice-widget/components/BasicSessionPanel/i18n/en-US.ts b/packages/engage-voice-widget/components/BasicSessionPanel/i18n/en-US.ts index 5b4479a44b..a9bdb73bbf 100644 --- a/packages/engage-voice-widget/components/BasicSessionPanel/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/BasicSessionPanel/i18n/en-US.ts @@ -8,4 +8,4 @@ export default { continue: 'Continue', enterYourPhoneNumber: 'Enter your phone number', warning: 'It may take a while to update the change.', -}; +} as const; diff --git a/packages/engage-voice-widget/components/BasicSessionPanel/i18n/index.ts b/packages/engage-voice-widget/components/BasicSessionPanel/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/BasicSessionPanel/i18n/index.ts +++ b/packages/engage-voice-widget/components/BasicSessionPanel/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/CallHistoryCallLogPanel/CallHistoryCallLogPanel.tsx b/packages/engage-voice-widget/components/CallHistoryCallLogPanel/CallHistoryCallLogPanel.tsx index 56152703a5..a08d49d31b 100644 --- a/packages/engage-voice-widget/components/CallHistoryCallLogPanel/CallHistoryCallLogPanel.tsx +++ b/packages/engage-voice-widget/components/CallHistoryCallLogPanel/CallHistoryCallLogPanel.tsx @@ -1,13 +1,11 @@ -import type { FunctionComponent } from 'react'; -import React, { useCallback, useRef } from 'react'; - -import classNames from 'classnames'; - import { BasicCallInfo } from '@ringcentral-integration/widgets/components/BasicCallInfo'; import type { CallLogPanelProps } from '@ringcentral-integration/widgets/components/CallLogPanel'; import CallLogPanel from '@ringcentral-integration/widgets/components/CallLogPanel'; import type { CallLogTitle } from '@ringcentral-integration/widgets/components/CallLogPanel/CallLog.interface'; import { RcButton } from '@ringcentral/juno'; +import clsx from 'clsx'; +import type { FunctionComponent } from 'react'; +import React, { useCallback, useRef } from 'react'; import type { CallLogMethods, @@ -18,10 +16,10 @@ import { callLogMethods, saveStatus as saveStatusValue, } from '../../interfaces/EvActivityCallUI.interface'; +import { SubmitButtonWrapper } from '../ActivityCallLogPanel/ActivityCallLogWrapper'; import { IvrInfo } from '../ActivityCallLogPanel/IvrInfo'; import styles from '../ActivityCallLogPanel/styles.scss'; import { EditLogSection, getButtonText } from '../ActivityCallLogPanel/utils'; -import { SubmitButtonWrapper } from '../ActivityCallLogPanel/ActivityCallLogWrapper'; export type CallHistoryCallLogPanelProps = EvActivityCallUIProps & EvActivityCallUIFunctions & @@ -29,111 +27,109 @@ export type CallHistoryCallLogPanelProps = EvActivityCallUIProps & method: CallLogMethods; }; -export const CallHistoryCallLogPanel: FunctionComponent = - ({ - currentLocale, - currentLog, - basicInfo, - isInbound, - disposeCall, - status, - saveStatus, - disableDispose, - isWide, - ivrAlertData, - onCopySuccess, - scrollTo, - referenceFieldOptions, - method, - ...rest - }) => { - const rootRef = useRef(null); - const isLoading = saveStatus === saveStatusValue.saving; - const headerTitle = `${method}CallLog` as CallLogTitle; +export const CallHistoryCallLogPanel: FunctionComponent< + CallHistoryCallLogPanelProps +> = ({ + currentLocale, + currentLog, + basicInfo, + isInbound, + disposeCall, + status, + saveStatus, + disableDispose, + isWide, + ivrAlertData, + onCopySuccess, + scrollTo, + referenceFieldOptions, + method, + ...rest +}) => { + const rootRef = useRef(null); + const isLoading = saveStatus === saveStatusValue.saving; + const headerTitle = `${method}CallLog` as CallLogTitle; - const editLogSection = useCallback( - (props) => ( - - ), - [isWide, scrollTo, referenceFieldOptions], - ); + const editLogSection = useCallback( + (props) => ( + + ), + [isWide, scrollTo, referenceFieldOptions], + ); - let buttonText; - if ( - saveStatus === callLogMethods.create || - saveStatus === saveStatusValue.submit - ) { - buttonText = callLogMethods.create; - } else { - buttonText = saveStatus; - } + let buttonText; + if ( + saveStatus === callLogMethods.create || + saveStatus === saveStatusValue.submit + ) { + buttonText = callLogMethods.create; + } else { + buttonText = saveStatus; + } - return ( - null} - showSpinner={false} - isInTransferPage={false} - // TODO: that need refactor CallLogPanel and then can remove that - currentIdentify="123" - renderEditLogSection={editLogSection} - renderBasicInfo={() => { - return ( - <> - - {ivrAlertData?.length > 0 && ( - - )} - - ); - }} - > - - - {getButtonText(buttonText, currentLocale)} - - - - ); - }; + return ( + null} + showSpinner={false} + isInTransferPage={false} + // TODO: that need refactor CallLogPanel and then can remove that + currentIdentify="123" + renderEditLogSection={editLogSection} + renderBasicInfo={() => { + return ( + <> + + {ivrAlertData?.length > 0 && ( + + )} + + ); + }} + > + + + {getButtonText(buttonText, currentLocale)} + + + + ); +}; diff --git a/packages/engage-voice-widget/components/ChooseAccountPanel/ChooseAccountPanel.tsx b/packages/engage-voice-widget/components/ChooseAccountPanel/ChooseAccountPanel.tsx index fed4b0d140..2887732117 100644 --- a/packages/engage-voice-widget/components/ChooseAccountPanel/ChooseAccountPanel.tsx +++ b/packages/engage-voice-widget/components/ChooseAccountPanel/ChooseAccountPanel.tsx @@ -1,8 +1,7 @@ -import type { FunctionComponent } from 'react'; -import React from 'react'; - import { CustomArrowButton } from '@ringcentral-integration/widgets/components/Rcui/CustomArrowButton'; import { palette2, RcTypography, spacing, styled } from '@ringcentral/juno'; +import type { FunctionComponent } from 'react'; +import React from 'react'; import type { EvChooseAccountUIFunctions, @@ -10,6 +9,7 @@ import type { } from '../../interfaces/EvChooseAccountUI.interface'; import { EvLoginHeader } from '../EvLoginHeader'; import { ListItem } from '../SelectList'; + import i18n from './i18n'; import styles from './styles.scss'; diff --git a/packages/engage-voice-widget/components/ChooseAccountPanel/i18n/en-US.ts b/packages/engage-voice-widget/components/ChooseAccountPanel/i18n/en-US.ts index a56bcfe9c9..2ecbff2f5d 100644 --- a/packages/engage-voice-widget/components/ChooseAccountPanel/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/ChooseAccountPanel/i18n/en-US.ts @@ -2,4 +2,4 @@ export default { chooseAccount: 'Choose account', agent: 'Agent', supervisor: 'Supervisor', -}; +} as const; diff --git a/packages/engage-voice-widget/components/ChooseAccountPanel/i18n/index.ts b/packages/engage-voice-widget/components/ChooseAccountPanel/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/ChooseAccountPanel/i18n/index.ts +++ b/packages/engage-voice-widget/components/ChooseAccountPanel/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/DialerPanel/Dialer/Dialer.tsx b/packages/engage-voice-widget/components/DialerPanel/Dialer/Dialer.tsx index 9745d062b5..1f7b6883a4 100644 --- a/packages/engage-voice-widget/components/DialerPanel/Dialer/Dialer.tsx +++ b/packages/engage-voice-widget/components/DialerPanel/Dialer/Dialer.tsx @@ -1,6 +1,3 @@ -import type { FunctionComponent } from 'react'; -import React from 'react'; - import type { RcDialTextFieldProps } from '@ringcentral/juno'; import { RcDialDelete, @@ -9,6 +6,8 @@ import { RcIconButton, } from '@ringcentral/juno'; import { Deletenumber } from '@ringcentral/juno-icon'; +import type { FunctionComponent } from 'react'; +import React from 'react'; import { DialerWrapper, TextFieldWrapper } from './styles'; diff --git a/packages/engage-voice-widget/components/DialerPanel/DialerPanel.tsx b/packages/engage-voice-widget/components/DialerPanel/DialerPanel.tsx index ac94034f68..23402f7938 100644 --- a/packages/engage-voice-widget/components/DialerPanel/DialerPanel.tsx +++ b/packages/engage-voice-widget/components/DialerPanel/DialerPanel.tsx @@ -1,13 +1,13 @@ -import type { FunctionComponent } from 'react'; -import React from 'react'; - import type { RcLinkSize, RcTypographyVariant } from '@ringcentral/juno'; import { RcButton, RcLink, RcText } from '@ringcentral/juno'; +import type { FunctionComponent } from 'react'; +import React from 'react'; import type { EvDialerUIFunctions, EvDialerUIProps, } from '../../interfaces/EvDialerUI.interface'; + import { Dialer } from './Dialer'; import { RcLinkWrapper, RcTextWrapper } from './DialerPanelWrapper'; import i18n from './i18n'; diff --git a/packages/engage-voice-widget/components/DialerPanel/i18n/en-US.ts b/packages/engage-voice-widget/components/DialerPanel/i18n/en-US.ts index 6273702eb6..7cf982d779 100644 --- a/packages/engage-voice-widget/components/DialerPanel/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/DialerPanel/i18n/en-US.ts @@ -1,7 +1,7 @@ export default { manualDialSettings: 'Manual dial settings', dialPlaceholder: 'Enter number', - callButtonTip:"To make a call, enter a phone number in the field above", - callButton:"Request a call", - callButtonEmergencyTip:"Emergency calling is not available", -}; + callButtonTip: 'To make a call, enter a phone number in the field above', + callButton: 'Request a call', + callButtonEmergencyTip: 'Emergency calling is not available', +} as const; diff --git a/packages/engage-voice-widget/components/DialerPanel/i18n/index.ts b/packages/engage-voice-widget/components/DialerPanel/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/DialerPanel/i18n/index.ts +++ b/packages/engage-voice-widget/components/DialerPanel/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/DialerPanel/tests/DialerPanel.spec.tsx b/packages/engage-voice-widget/components/DialerPanel/tests/DialerPanel.spec.tsx index f784e00e9c..ffd8d50285 100644 --- a/packages/engage-voice-widget/components/DialerPanel/tests/DialerPanel.spec.tsx +++ b/packages/engage-voice-widget/components/DialerPanel/tests/DialerPanel.spec.tsx @@ -1,8 +1,8 @@ -import type { ReactWrapper } from 'enzyme'; - import { RcDialTextField } from '@ringcentral/juno'; +import type { ReactWrapper } from 'enzyme'; import type { DialoutStatusesType } from '../../../enums/dialoutStatus'; + import { createDialerPanel } from './createDialerPanel'; const mockAudio = () => { @@ -32,7 +32,7 @@ describe('', () => { const recipientsInput = wrapper.find(RcDialTextField).at(0); const eventObj = { target: { value: '1243' } }; recipientsInput.find('input').at(0).simulate('change', eventObj); - expect(setToNumber).toBeCalledWith('1243'); + expect(setToNumber).toHaveBeenCalledWith('1243'); }); it('Delete button show switch', async () => { @@ -51,7 +51,7 @@ describe('', () => { wrapper = createDialerPanel({ toNumber, dialout, dialoutStatus }); const callButton = getCallButton(); callButton.simulate('click'); - expect(dialout).not.toBeCalled(); + expect(dialout).not.toHaveBeenCalled(); }); it('User clicks manualDialSettings', () => { @@ -61,7 +61,7 @@ describe('', () => { .find('[data-sign="manualDialSettings"]') .at(0); manualDialSettings.simulate('click'); - expect(goToManualDialSettings).toBeCalled(); + expect(goToManualDialSettings).toHaveBeenCalled(); }); it('Check Disabled Allow Manual Calls', () => { diff --git a/packages/engage-voice-widget/components/DialerPanel/tests/DialerPanel.ut.tsx b/packages/engage-voice-widget/components/DialerPanel/tests/DialerPanel.ut.tsx index fa4ef8a5bd..5325d30626 100644 --- a/packages/engage-voice-widget/components/DialerPanel/tests/DialerPanel.ut.tsx +++ b/packages/engage-voice-widget/components/DialerPanel/tests/DialerPanel.ut.tsx @@ -1,6 +1,7 @@ import type { StepFunction } from '@ringcentral-integration/test-utils'; import { Dialer } from '../Dialer'; + import { createDialerPanel } from './createDialerPanel'; interface DialerPanelProps { diff --git a/packages/engage-voice-widget/components/DialerPanel/tests/createDialerPanel.tsx b/packages/engage-voice-widget/components/DialerPanel/tests/createDialerPanel.tsx index c90ba9a0eb..250dc01736 100644 --- a/packages/engage-voice-widget/components/DialerPanel/tests/createDialerPanel.tsx +++ b/packages/engage-voice-widget/components/DialerPanel/tests/createDialerPanel.tsx @@ -1,8 +1,6 @@ -import React from 'react'; - -import { mount } from 'enzyme'; - import { RcThemeProvider } from '@ringcentral/juno'; +import { mount } from 'enzyme'; +import React from 'react'; import type { DialoutStatusesType } from '../../../enums/dialoutStatus'; import { DialerPanel } from '../DialerPanel'; diff --git a/packages/engage-voice-widget/components/EvLoginHeader/EvLoginHeader.tsx b/packages/engage-voice-widget/components/EvLoginHeader/EvLoginHeader.tsx index 4ba62e13f1..5ea2a44c1c 100644 --- a/packages/engage-voice-widget/components/EvLoginHeader/EvLoginHeader.tsx +++ b/packages/engage-voice-widget/components/EvLoginHeader/EvLoginHeader.tsx @@ -1,9 +1,9 @@ +import clsx from 'clsx'; import type { FunctionComponent } from 'react'; import React from 'react'; -import classNames from 'classnames'; - import EngageVoiceLogo from '../../assets/icons/engageVoiceLogo.svg'; + import styles from './styles.scss'; export interface EvLoginHeaderProps { @@ -16,7 +16,7 @@ export const EvLoginHeader: FunctionComponent = ({ svgStyle, }) => { return ( -
+
); diff --git a/packages/engage-voice-widget/components/EvSmallCallControl/EvSmallCallControl.tsx b/packages/engage-voice-widget/components/EvSmallCallControl/EvSmallCallControl.tsx index 9155f0f943..35bf99a94c 100644 --- a/packages/engage-voice-widget/components/EvSmallCallControl/EvSmallCallControl.tsx +++ b/packages/engage-voice-widget/components/EvSmallCallControl/EvSmallCallControl.tsx @@ -9,6 +9,7 @@ import { SmallCallControl, TransferCallButton, } from '../SmallCallControl'; + import type { ActiveCallButtonProps, CountDownButtonProps, diff --git a/packages/engage-voice-widget/components/EvSmallCallControl/components/ActiveCallButton.tsx b/packages/engage-voice-widget/components/EvSmallCallControl/components/ActiveCallButton.tsx index 08f8924e3c..1ae6f63152 100644 --- a/packages/engage-voice-widget/components/EvSmallCallControl/components/ActiveCallButton.tsx +++ b/packages/engage-voice-widget/components/EvSmallCallControl/components/ActiveCallButton.tsx @@ -1,8 +1,7 @@ -import type { FunctionComponent } from 'react'; -import React from 'react'; - import { RcIconButton } from '@ringcentral/juno'; import { ActiveCall } from '@ringcentral/juno-icon'; +import type { FunctionComponent } from 'react'; +import React from 'react'; import type { CallButtonsProps } from '../../SmallCallControl'; import i18n from '../i18n'; diff --git a/packages/engage-voice-widget/components/EvSmallCallControl/components/CountDownButton.tsx b/packages/engage-voice-widget/components/EvSmallCallControl/components/CountDownButton.tsx index f03c64ee12..1d6043851a 100644 --- a/packages/engage-voice-widget/components/EvSmallCallControl/components/CountDownButton.tsx +++ b/packages/engage-voice-widget/components/EvSmallCallControl/components/CountDownButton.tsx @@ -1,8 +1,7 @@ -import type { FunctionComponent } from 'react'; -import React, { useEffect, useState } from 'react'; - import { sleep } from '@ringcentral-integration/commons/utils'; import { RcIconButton, RcText } from '@ringcentral/juno'; +import type { FunctionComponent } from 'react'; +import React, { useEffect, useState } from 'react'; import type { CallButtonsProps } from '../../SmallCallControl'; import i18n from '../i18n'; diff --git a/packages/engage-voice-widget/components/EvSmallCallControl/components/CountDownButton.ut.tsx b/packages/engage-voice-widget/components/EvSmallCallControl/components/CountDownButton.ut.tsx index 2df5d5bfc6..921257a477 100644 --- a/packages/engage-voice-widget/components/EvSmallCallControl/components/CountDownButton.ut.tsx +++ b/packages/engage-voice-widget/components/EvSmallCallControl/components/CountDownButton.ut.tsx @@ -1,11 +1,9 @@ -import React from 'react'; - +import type { CountDownButtonProps } from '.'; +import { CountDown, CountDownButton } from '.'; import type { StepFunction } from '@ringcentral-integration/test-utils'; import { RcThemeProvider } from '@ringcentral/juno'; import { render } from '@testing-library/react'; - -import type { CountDownButtonProps } from '.'; -import { CountDown, CountDownButton } from '.'; +import React from 'react'; function setup({ currentLocale = 'en-US', diff --git a/packages/engage-voice-widget/components/EvSmallCallControl/components/RecordControlButton.tsx b/packages/engage-voice-widget/components/EvSmallCallControl/components/RecordControlButton.tsx index aa8a107432..e37d9f3178 100644 --- a/packages/engage-voice-widget/components/EvSmallCallControl/components/RecordControlButton.tsx +++ b/packages/engage-voice-widget/components/EvSmallCallControl/components/RecordControlButton.tsx @@ -1,8 +1,7 @@ -import type { FunctionComponent } from 'react'; -import React from 'react'; - import { RcIconButton } from '@ringcentral/juno'; import { StopRecord } from '@ringcentral/juno-icon'; +import type { FunctionComponent } from 'react'; +import React from 'react'; import RecordControlSvg from '../../../assets/icons/icon-record.svg'; import type { CallButtonsProps } from '../../SmallCallControl'; @@ -16,44 +15,45 @@ export type RecordControlButtonProps = CallButtonsProps & { disablePauseRecord?: boolean; }; -export const RecordControlButton: FunctionComponent = - ({ - currentLocale, - isRecording, - onRecord, - onStopRecord, - disablePauseRecord, - size, - className, - onPauseRecord, - }) => { - return isRecording ? ( - - ) : ( - - ); - }; +export const RecordControlButton: FunctionComponent< + RecordControlButtonProps +> = ({ + currentLocale, + isRecording, + onRecord, + onStopRecord, + disablePauseRecord, + size, + className, + onPauseRecord, +}) => { + return isRecording ? ( + + ) : ( + + ); +}; RecordControlButton.defaultProps = { currentLocale: 'en-US', diff --git a/packages/engage-voice-widget/components/EvSmallCallControl/components/RecordControlButton.ut.tsx b/packages/engage-voice-widget/components/EvSmallCallControl/components/RecordControlButton.ut.tsx index 25859891d1..594e4ee87d 100644 --- a/packages/engage-voice-widget/components/EvSmallCallControl/components/RecordControlButton.ut.tsx +++ b/packages/engage-voice-widget/components/EvSmallCallControl/components/RecordControlButton.ut.tsx @@ -1,11 +1,9 @@ -import React from 'react'; - +import type { RecordControlButtonProps } from '.'; +import { RecordControlButton } from '.'; import type { StepFunction } from '@ringcentral-integration/test-utils'; import { RcThemeProvider } from '@ringcentral/juno'; import { render } from '@testing-library/react'; - -import type { RecordControlButtonProps } from '.'; -import { RecordControlButton } from '.'; +import React from 'react'; function setup({ currentLocale = 'en-US', diff --git a/packages/engage-voice-widget/components/EvSmallCallControl/components/RecordingButton.tsx b/packages/engage-voice-widget/components/EvSmallCallControl/components/RecordingButton.tsx index 43a909fa1d..a6f88baf9e 100644 --- a/packages/engage-voice-widget/components/EvSmallCallControl/components/RecordingButton.tsx +++ b/packages/engage-voice-widget/components/EvSmallCallControl/components/RecordingButton.tsx @@ -1,11 +1,11 @@ +import { RcIconButton } from '@ringcentral/juno'; import type { FunctionComponent } from 'react'; import React from 'react'; -import { RcIconButton } from '@ringcentral/juno'; - import RecordingSvg from '../../../assets/icons/icon-recording.svg'; import type { CallButtonsProps } from '../../SmallCallControl'; import i18n from '../i18n'; + import styles from './styles.scss'; export type RecordingButtonProps = CallButtonsProps & { diff --git a/packages/engage-voice-widget/components/EvSmallCallControl/components/styles.scss b/packages/engage-voice-widget/components/EvSmallCallControl/components/styles.scss index bb0fddc918..bb0d948f37 100644 --- a/packages/engage-voice-widget/components/EvSmallCallControl/components/styles.scss +++ b/packages/engage-voice-widget/components/EvSmallCallControl/components/styles.scss @@ -1,4 +1,3 @@ - .disableRecordingBtn { cursor: not-allowed !important; } diff --git a/packages/engage-voice-widget/components/EvSmallCallControl/i18n/en-US.ts b/packages/engage-voice-widget/components/EvSmallCallControl/i18n/en-US.ts index 0fea43e4b8..fa70b3eb5e 100644 --- a/packages/engage-voice-widget/components/EvSmallCallControl/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/EvSmallCallControl/i18n/en-US.ts @@ -6,4 +6,4 @@ export default { pauseRecording: 'Pause recording', stopRecording: 'Stop recording', recording: 'Recording', -}; +} as const; diff --git a/packages/engage-voice-widget/components/EvSmallCallControl/i18n/index.ts b/packages/engage-voice-widget/components/EvSmallCallControl/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/EvSmallCallControl/i18n/index.ts +++ b/packages/engage-voice-widget/components/EvSmallCallControl/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/InboundQueuesPanel/InboundQueuesPanel.tsx b/packages/engage-voice-widget/components/InboundQueuesPanel/InboundQueuesPanel.tsx index a79af5e5f7..e4f2c2dacc 100644 --- a/packages/engage-voice-widget/components/InboundQueuesPanel/InboundQueuesPanel.tsx +++ b/packages/engage-voice-widget/components/InboundQueuesPanel/InboundQueuesPanel.tsx @@ -1,8 +1,7 @@ -import type { FunctionComponent } from 'react'; -import React, { useState } from 'react'; - import { format } from '@ringcentral-integration/utils'; import { RcButton, RcCheckbox, RcListItem } from '@ringcentral/juno'; +import type { FunctionComponent } from 'react'; +import React, { useState } from 'react'; import type { AvailableQueue, @@ -10,6 +9,7 @@ import type { EvAgentSessionUIProps, } from '../../interfaces'; import { SelectList } from '../SelectList'; + import i18n from './i18n'; import styles from './styles.scss'; diff --git a/packages/engage-voice-widget/components/InboundQueuesPanel/InboundQueuesPanel.ut.tsx b/packages/engage-voice-widget/components/InboundQueuesPanel/InboundQueuesPanel.ut.tsx index 01aed102c5..228a4ada0e 100644 --- a/packages/engage-voice-widget/components/InboundQueuesPanel/InboundQueuesPanel.ut.tsx +++ b/packages/engage-voice-widget/components/InboundQueuesPanel/InboundQueuesPanel.ut.tsx @@ -1,9 +1,7 @@ -import React from 'react'; - -import { mount } from 'enzyme'; - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { RcThemeProvider } from '@ringcentral/juno'; +import { mount } from 'enzyme'; +import React from 'react'; import { InboundQueuesPanel } from './index'; diff --git a/packages/engage-voice-widget/components/InboundQueuesPanel/i18n/en-US.ts b/packages/engage-voice-widget/components/InboundQueuesPanel/i18n/en-US.ts index ab7e78346d..da446e07fe 100644 --- a/packages/engage-voice-widget/components/InboundQueuesPanel/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/InboundQueuesPanel/i18n/en-US.ts @@ -9,4 +9,4 @@ export default { selectedTips: '{assignedInboundQueuesNumber} of {totalInboundQueuesNumber} Selected', selectAll: 'Select all', -}; +} as const; diff --git a/packages/engage-voice-widget/components/InboundQueuesPanel/i18n/index.ts b/packages/engage-voice-widget/components/InboundQueuesPanel/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/InboundQueuesPanel/i18n/index.ts +++ b/packages/engage-voice-widget/components/InboundQueuesPanel/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/ListItemWithScrollCheck/ListItemWithScrollCheck.tsx b/packages/engage-voice-widget/components/ListItemWithScrollCheck/ListItemWithScrollCheck.tsx index d4e7e4bf9f..4d23bbcc47 100644 --- a/packages/engage-voice-widget/components/ListItemWithScrollCheck/ListItemWithScrollCheck.tsx +++ b/packages/engage-voice-widget/components/ListItemWithScrollCheck/ListItemWithScrollCheck.tsx @@ -1,10 +1,8 @@ +import { RcListItem } from '@ringcentral/juno'; +import clsx from 'clsx'; import type { FunctionComponent } from 'react'; import React, { useEffect, useRef } from 'react'; -import classNames from 'classnames'; - -import { RcListItem } from '@ringcentral/juno'; - import styles from './styles.scss'; export interface ListItemWithScrollCheckProps { @@ -14,31 +12,32 @@ export interface ListItemWithScrollCheckProps { className?: string; } -export const ListItemWithScrollCheck: FunctionComponent = - ({ selected, onClick, children, scrollCheck, className, ...rest }) => { - const selectElm = useRef(); +export const ListItemWithScrollCheck: FunctionComponent< + ListItemWithScrollCheckProps +> = ({ selected, onClick, children, scrollCheck, className, ...rest }) => { + const selectElm = useRef(); - useEffect(() => { - if (selected && scrollCheck) { - scrollCheck(selectElm.current); - } - }); + useEffect(() => { + if (selected && scrollCheck) { + scrollCheck(selectElm.current); + } + }); - return ( - { - e.preventDefault(); - onClick(); - }} - > - {children} - - ); - }; + return ( + { + e.preventDefault(); + onClick(); + }} + > + {children} + + ); +}; diff --git a/packages/engage-voice-widget/components/LoginPanel/LoginPanel.tsx b/packages/engage-voice-widget/components/LoginPanel/LoginPanel.tsx index 6050f05e2b..ed5e64cb86 100644 --- a/packages/engage-voice-widget/components/LoginPanel/LoginPanel.tsx +++ b/packages/engage-voice-widget/components/LoginPanel/LoginPanel.tsx @@ -1,10 +1,8 @@ -import type { FunctionComponent } from 'react'; -import React, { useEffect } from 'react'; - -import classNames from 'classnames'; - import type { RcButtonSize } from '@ringcentral/juno'; import { RcButton } from '@ringcentral/juno'; +import clsx from 'clsx'; +import type { FunctionComponent } from 'react'; +import React, { useEffect } from 'react'; import i18n from './i18n'; import styles from './styles.scss'; @@ -70,11 +68,11 @@ export const LoginPanel: FunctionComponent = ({ ) : null; return ( -
+
(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/MainViewPanel/MainViewPanel.tsx b/packages/engage-voice-widget/components/MainViewPanel/MainViewPanel.tsx index 1491ca7ca7..c65359ab3b 100644 --- a/packages/engage-voice-widget/components/MainViewPanel/MainViewPanel.tsx +++ b/packages/engage-voice-widget/components/MainViewPanel/MainViewPanel.tsx @@ -1,8 +1,3 @@ -import type { FunctionComponent } from 'react'; -import React, { useMemo } from 'react'; - -import classNames from 'classnames'; - import type { TabPropTypes } from '@ringcentral-integration/widgets/components/NavigationBar'; import { Tooltip } from '@ringcentral-integration/widgets/components/Rcui/Tooltip'; import TabNavigationView from '@ringcentral-integration/widgets/components/TabNavigationView'; @@ -16,6 +11,9 @@ import { Time as TimeSvg, TimeBorder as TimeBorderSvg, } from '@ringcentral/juno-icon'; +import clsx from 'clsx'; +import type { FunctionComponent } from 'react'; +import React, { useMemo } from 'react'; import offHookSvgConnecting from '../../assets/icons/icon-pvc-connecting.svg'; import offHookSvgDisabled from '../../assets/icons/icon-pvc-disabled.svg'; @@ -26,9 +24,10 @@ import type { EvMainViewUIFunctions, EvMainViewUIProps, } from '../../interfaces/EvMainViewUI.interface'; + +import { WorkingStateSelect } from './WorkingStateSelect'; import i18n from './i18n'; import styles from './styles.scss'; -import { WorkingStateSelect } from './WorkingStateSelect'; export type MainViewPanelProps = EvMainViewUIProps & EvMainViewUIFunctions; @@ -149,7 +148,7 @@ const MainViewPanel: FunctionComponent = ({ /> {!hideOffHookBtn && (
= ({
= ({ selected={selected} >
{state.agentAuxState}
diff --git a/packages/engage-voice-widget/components/MainViewPanel/WorkingStateSelect/WorkingStateSelect.spec.tsx b/packages/engage-voice-widget/components/MainViewPanel/WorkingStateSelect/WorkingStateSelect.spec.tsx index 188e5ed74e..37173997df 100644 --- a/packages/engage-voice-widget/components/MainViewPanel/WorkingStateSelect/WorkingStateSelect.spec.tsx +++ b/packages/engage-voice-widget/components/MainViewPanel/WorkingStateSelect/WorkingStateSelect.spec.tsx @@ -1,11 +1,10 @@ -import React from 'react'; - +import { RcThemeProvider } from '@ringcentral/juno'; import { mount } from 'enzyme'; +import React from 'react'; import { act } from 'react-dom/test-utils'; -import { RcThemeProvider } from '@ringcentral/juno'; - import type { EvCustomAvailableAgentState } from '../../../interfaces/EvMainViewUI.interface'; + import { WorkingStateSelect } from './WorkingStateSelect'; let wrapper; @@ -155,7 +154,7 @@ describe('', () => { getAgentStateList().at(currentStateIndex).simulate('click'); const currentState = agentStates[currentStateIndex]; - expect(changeWorkingState).toBeCalledWith(currentState); + expect(changeWorkingState).toHaveBeenCalledWith(currentState); }); it('Time label will update itself in every minute', async () => { diff --git a/packages/engage-voice-widget/components/MainViewPanel/WorkingStateSelect/WorkingStateSelect.tsx b/packages/engage-voice-widget/components/MainViewPanel/WorkingStateSelect/WorkingStateSelect.tsx index e06024c887..37b800ee05 100644 --- a/packages/engage-voice-widget/components/MainViewPanel/WorkingStateSelect/WorkingStateSelect.tsx +++ b/packages/engage-voice-widget/components/MainViewPanel/WorkingStateSelect/WorkingStateSelect.tsx @@ -1,14 +1,14 @@ +import clsx from 'clsx'; import type { FunctionComponent } from 'react'; import React, { useEffect, useState } from 'react'; -import classNames from 'classnames'; - import type { EvMainViewUIFunctions, EvMainViewUIProps, } from '../../../interfaces/EvMainViewUI.interface'; -import styles from './styles.scss'; + import { WorkingStateButton } from './WorkingStateButton'; +import styles from './styles.scss'; type WorkingStateSelectProps = Pick< EvMainViewUIProps & EvMainViewUIFunctions, @@ -64,7 +64,7 @@ const WorkingStateSelect: FunctionComponent = ({ isWide={isWide} timerText={getTimerText(intervalTime)} classes={{ - paper: classNames(styles.paper, hideOffHookBtn && styles.wider), + paper: clsx(styles.paper, hideOffHookBtn && styles.wider), }} />
diff --git a/packages/engage-voice-widget/components/MainViewPanel/WorkingStateSelect/styles.scss b/packages/engage-voice-widget/components/MainViewPanel/WorkingStateSelect/styles.scss index 382565ae93..7175502648 100644 --- a/packages/engage-voice-widget/components/MainViewPanel/WorkingStateSelect/styles.scss +++ b/packages/engage-voice-widget/components/MainViewPanel/WorkingStateSelect/styles.scss @@ -24,5 +24,3 @@ div.paper { div.wider { min-width: calc(100% - 33px) !important; } - - diff --git a/packages/engage-voice-widget/components/MainViewPanel/i18n/en-US.ts b/packages/engage-voice-widget/components/MainViewPanel/i18n/en-US.ts index d2d68bc529..52468d437e 100644 --- a/packages/engage-voice-widget/components/MainViewPanel/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/MainViewPanel/i18n/en-US.ts @@ -5,4 +5,4 @@ export default { dialpadLabel: 'Dial Pad', historyLabel: 'Call History', settingsLabel: 'Settings', -}; +} as const; diff --git a/packages/engage-voice-widget/components/MainViewPanel/i18n/index.ts b/packages/engage-voice-widget/components/MainViewPanel/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/MainViewPanel/i18n/index.ts +++ b/packages/engage-voice-widget/components/MainViewPanel/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/ManualDialSettingsPanel/CallerIdLabel/CallerIdLabel.tsx b/packages/engage-voice-widget/components/ManualDialSettingsPanel/CallerIdLabel/CallerIdLabel.tsx index 2314cb3fc5..fc5fb18c07 100644 --- a/packages/engage-voice-widget/components/ManualDialSettingsPanel/CallerIdLabel/CallerIdLabel.tsx +++ b/packages/engage-voice-widget/components/ManualDialSettingsPanel/CallerIdLabel/CallerIdLabel.tsx @@ -1,8 +1,7 @@ +import { RcText } from '@ringcentral/juno'; import type { FunctionComponent } from 'react'; import React from 'react'; -import { RcText } from '@ringcentral/juno'; - import styles from './styles.scss'; export interface CallerIdLabelProps { diff --git a/packages/engage-voice-widget/components/ManualDialSettingsPanel/ManualDialSettingsPanel.spec.tsx b/packages/engage-voice-widget/components/ManualDialSettingsPanel/ManualDialSettingsPanel.spec.tsx index b1f8b5c2c5..34e5e1dae3 100644 --- a/packages/engage-voice-widget/components/ManualDialSettingsPanel/ManualDialSettingsPanel.spec.tsx +++ b/packages/engage-voice-widget/components/ManualDialSettingsPanel/ManualDialSettingsPanel.spec.tsx @@ -1,10 +1,9 @@ -import React from 'react'; - -import { mount } from 'enzyme'; - import { RcThemeProvider } from '@ringcentral/juno'; +import { mount } from 'enzyme'; +import React from 'react'; import i18n from '../../modules/EvManualDialSettingsUI/i18n'; + import type { ManualDialSettingsPanelProps } from './ManualDialSettingsPanel'; import { ManualDialSettingsPanel } from './ManualDialSettingsPanel'; @@ -83,7 +82,7 @@ describe('', () => { .at(0) .find('button') .simulate('click'); - expect(goBack).toBeCalled(); + expect(goBack).toHaveBeenCalled(); }); // Declan: I thinks that is not need, that is too detail about render, just using snapshot is ok @@ -147,7 +146,7 @@ describe('', () => { // const menuItems = document.body.querySelectorAll( // '[role="presentation"] li[role="option"]', // ); - // expect(customSelect.select.renderItem).toBeCalledTimes( + // expect(customSelect.select.renderItem).toHaveBeenCalledTimes( // pickListItems.length, // ); // expect(menuItems).toHaveLength(pickListItems.length); @@ -161,7 +160,7 @@ describe('', () => { // }); // const selectIndex = 1; // menuItems[selectIndex].click(); - // expect(customSelect.onChange).toBeCalledWith( + // expect(customSelect.onChange).toHaveBeenCalledWith( // pickListItems[selectIndex][customSelect.select.optionValueKey], // ); // }); @@ -199,10 +198,10 @@ describe('', () => { expect(input.prop('max')).toBe(customInput.input.max); input.simulate('blur'); - expect(customInput.onBlur).toBeCalled(); + expect(customInput.onBlur).toHaveBeenCalled(); const changeValue = 35; input.simulate('change', { target: { value: changeValue } }); - expect(customInput.onChange).toBeCalledWith(changeValue); + expect(customInput.onChange).toHaveBeenCalledWith(changeValue); }); }); diff --git a/packages/engage-voice-widget/components/ManualDialSettingsPanel/ManualDialSettingsPanel.tsx b/packages/engage-voice-widget/components/ManualDialSettingsPanel/ManualDialSettingsPanel.tsx index c2c627e8ca..d6700e81ec 100644 --- a/packages/engage-voice-widget/components/ManualDialSettingsPanel/ManualDialSettingsPanel.tsx +++ b/packages/engage-voice-widget/components/ManualDialSettingsPanel/ManualDialSettingsPanel.tsx @@ -1,8 +1,7 @@ +import { RcButton, RcTextField } from '@ringcentral/juno'; import type { FunctionComponent } from 'react'; import React, { useEffect } from 'react'; -import { RcButton, RcTextField } from '@ringcentral/juno'; - import type { EvManualDialSettingsUIFunctions, EvManualDialSettingsUIProps, @@ -11,120 +10,122 @@ import i18n from '../../modules/EvManualDialSettingsUI/i18n'; import { ListItemWithScrollCheck } from '../ListItemWithScrollCheck'; import { SearchSelectField } from '../SearchSelectField'; import { BackHeader } from '../SelectList'; + import ManualDialSettingsPanelI18n from './i18n'; import styles from './styles.scss'; export type ManualDialSettingsPanelProps = EvManualDialSettingsUIProps & EvManualDialSettingsUIFunctions; -export const ManualDialSettingsPanel: FunctionComponent = - ({ currentLocale, goBack, init, settingFields, save }) => { - useEffect(() => { - init(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); +export const ManualDialSettingsPanel: FunctionComponent< + ManualDialSettingsPanelProps +> = ({ currentLocale, goBack, init, settingFields, save }) => { + useEffect(() => { + init(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); - return ( - <> - -
- {settingFields.map( - ({ select, input, dataSign, onChange, onBlur, value }, key) => { - if (select) { - return ( - { - return ( - <> - {transferPhoneBook.map((obj, i) => { - const thisValue = select.getItemValue(obj); - return ( - { - onChange(thisValue); - toggleOpen(); - }} - key={i} - selected={thisValue === value} - scrollCheck={scrollCheck} - className={styles.listItem} - data-sign={`${dataSign}-${thisValue}`} - > - {select.itemRenderer(obj)} - - ); - })} - - ); - }} - /> - ); - } - if (input) { - return ( - onBlur()} - onChange={(e) => onChange(e.target.value)} - fullWidth - gutterBottom - InputProps={{ - endAdornment: ( - - {ManualDialSettingsPanelI18n.getString( - 'second', - currentLocale, - )} - - ), - }} - /> - ); - } - return null; - }, - )} -
-
- save()} - > - {ManualDialSettingsPanelI18n.getString('save', currentLocale)} - -
- - ); - }; + return ( + <> + +
+ {settingFields.map( + ({ select, input, dataSign, onChange, onBlur, value }, key) => { + if (select) { + return ( + { + return ( + <> + {transferPhoneBook.map((obj, i) => { + const thisValue = select.getItemValue(obj); + return ( + { + onChange(thisValue); + toggleOpen(); + }} + key={i} + selected={thisValue === value} + scrollCheck={scrollCheck} + className={styles.listItem} + data-sign={`${dataSign}-${thisValue}`} + > + {select.itemRenderer(obj)} + + ); + })} + + ); + }} + /> + ); + } + if (input) { + return ( + onBlur()} + onChange={(e) => onChange(e.target.value)} + fullWidth + gutterBottom + InputProps={{ + endAdornment: ( + + {ManualDialSettingsPanelI18n.getString( + 'second', + currentLocale, + )} + + ), + }} + /> + ); + } + return null; + }, + )} +
+
+ save()} + > + {ManualDialSettingsPanelI18n.getString('save', currentLocale)} + +
+ + ); +}; diff --git a/packages/engage-voice-widget/components/ManualDialSettingsPanel/QueueLabel/QueueLabel.tsx b/packages/engage-voice-widget/components/ManualDialSettingsPanel/QueueLabel/QueueLabel.tsx index f94d2a1ba4..7a42fdd4ec 100644 --- a/packages/engage-voice-widget/components/ManualDialSettingsPanel/QueueLabel/QueueLabel.tsx +++ b/packages/engage-voice-widget/components/ManualDialSettingsPanel/QueueLabel/QueueLabel.tsx @@ -1,9 +1,9 @@ +import { RcText } from '@ringcentral/juno'; import type { FunctionComponent } from 'react'; import React from 'react'; -import { RcText } from '@ringcentral/juno'; - import i18n from '../i18n'; + import styles from './styles.scss'; export interface QueueLabelProps { diff --git a/packages/engage-voice-widget/components/ManualDialSettingsPanel/i18n/en-US.ts b/packages/engage-voice-widget/components/ManualDialSettingsPanel/i18n/en-US.ts index 4bd0e3d799..f6e0292417 100644 --- a/packages/engage-voice-widget/components/ManualDialSettingsPanel/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/ManualDialSettingsPanel/i18n/en-US.ts @@ -3,4 +3,4 @@ export default { queueID: 'Queue ID', save: 'Save', second: 'Seconds', -}; +} as const; diff --git a/packages/engage-voice-widget/components/ManualDialSettingsPanel/i18n/index.ts b/packages/engage-voice-widget/components/ManualDialSettingsPanel/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/ManualDialSettingsPanel/i18n/index.ts +++ b/packages/engage-voice-widget/components/ManualDialSettingsPanel/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/PickList/PickList.spec.tsx b/packages/engage-voice-widget/components/PickList/PickList.spec.tsx index ef5c1ceab4..fe3e8f4af5 100644 --- a/packages/engage-voice-widget/components/PickList/PickList.spec.tsx +++ b/packages/engage-voice-widget/components/PickList/PickList.spec.tsx @@ -1,8 +1,6 @@ -import React from 'react'; - -import { mount } from 'enzyme'; - import { RcThemeProvider } from '@ringcentral/juno'; +import { mount } from 'enzyme'; +import React from 'react'; import type { PickListProps } from './PickList'; import { PickList } from './PickList'; @@ -98,7 +96,7 @@ describe('', () => { // }); // const selectIndex = 2; // menuItems[selectIndex].click(); - // expect(onChange).toBeCalledWith(defaultOptions[selectIndex].id); + // expect(onChange).toHaveBeenCalledWith(defaultOptions[selectIndex].id); }); it('PickList can render correctly without selection, and can be selected to change.', () => { @@ -122,7 +120,7 @@ describe('', () => { // expect(menuItems).toHaveLength(defaultOptions.length); // const selectIndex = 2; // menuItems[selectIndex].click(); - // expect(onChange).toBeCalledWith(defaultOptions[selectIndex].id); + // expect(onChange).toHaveBeenCalledWith(defaultOptions[selectIndex].id); }); it('PickList can display by using renderValue', () => { @@ -137,7 +135,7 @@ describe('', () => { value, }); expect(wrapper.find('input').prop('value')).toBe(value); - expect(renderValue).toBeCalledWith(value); + expect(renderValue).toHaveBeenCalledWith(value); const baseButton = wrapper.find('[role="button"]'); expect(baseButton.text()).toBe(renderValue(value)); // baseButton.simulate('click'); @@ -169,7 +167,7 @@ describe('', () => { // const menuItems = document.body.querySelectorAll( // '[role="presentation"] li[role="option"]', // ); - // expect(renderItem).toBeCalledTimes(defaultOptions.length); + // expect(renderItem).toHaveBeenCalledTimes(defaultOptions.length); // expect(menuItems).toHaveLength(defaultOptions.length); // menuItems.forEach((el, index) => { // expect(el.textContent).toBe(defaultOptions[index].wholeName); @@ -232,7 +230,7 @@ describe('', () => { // const menuItems = document.body.querySelectorAll( // '[role="presentation"] li[role="option"]', // ); - // expect(renderItem).toBeCalledTimes(customOptions.length); + // expect(renderItem).toHaveBeenCalledTimes(customOptions.length); // expect(menuItems).toHaveLength(customOptions.length); // menuItems.forEach((el, index) => { // expect(el.textContent).toBe(customOptions[index].wholeName); diff --git a/packages/engage-voice-widget/components/PickList/PickList.tsx b/packages/engage-voice-widget/components/PickList/PickList.tsx index c634d132e7..04fa79fee0 100644 --- a/packages/engage-voice-widget/components/PickList/PickList.tsx +++ b/packages/engage-voice-widget/components/PickList/PickList.tsx @@ -1,8 +1,7 @@ -import type { FunctionComponent, ReactNode } from 'react'; -import React from 'react'; - import type { RcSelectProps } from '@ringcentral/juno'; import { RcMenuItem, RcSelect } from '@ringcentral/juno'; +import type { FunctionComponent, ReactNode } from 'react'; +import React from 'react'; import styles from './styles.scss'; diff --git a/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/RequeueCallGroupDetailPanel/RequeueCallGroupDetailPanel.spec.tsx b/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/RequeueCallGroupDetailPanel/RequeueCallGroupDetailPanel.spec.tsx index f8b4efcafe..52039cd4c9 100644 --- a/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/RequeueCallGroupDetailPanel/RequeueCallGroupDetailPanel.spec.tsx +++ b/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/RequeueCallGroupDetailPanel/RequeueCallGroupDetailPanel.spec.tsx @@ -1,8 +1,6 @@ -import React from 'react'; - -import { mount } from 'enzyme'; - import { RcThemeProvider } from '@ringcentral/juno'; +import { mount } from 'enzyme'; +import React from 'react'; import { RequeueCallGroupDetailPanel } from './RequeueCallGroupDetailPanel'; @@ -78,7 +76,7 @@ describe('', () => { .at(0) .find('button') .simulate('click'); - expect(goBack).toBeCalled(); + expect(goBack).toHaveBeenCalled(); }); it('When user select a queue, the queue will be highlighted, then user click the select button, submitSelection will be called', () => { @@ -105,7 +103,7 @@ describe('', () => { const selectedGateId = defalutSelectedQueueGroup.gates[selectedGateIndex].gateId; - expect(submitSelection).toBeCalledWith(selectedGateId); + expect(submitSelection).toHaveBeenCalledWith(selectedGateId); }); it("When user select no queue, the Select Button should be disabled and submitSelection shouldn't be called", () => { @@ -124,7 +122,7 @@ describe('', () => { expect(submitButton.prop('disabled')).toBe(true); submitButton.simulate('click'); - expect(submitSelection).not.toBeCalled(); + expect(submitSelection).not.toHaveBeenCalled(); }); it('Can search Requeue Queue', () => { diff --git a/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/RequeueCallGroupDetailPanel/RequeueCallGroupDetailPanel.tsx b/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/RequeueCallGroupDetailPanel/RequeueCallGroupDetailPanel.tsx index ccc6b438d7..b9c09d308c 100644 --- a/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/RequeueCallGroupDetailPanel/RequeueCallGroupDetailPanel.tsx +++ b/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/RequeueCallGroupDetailPanel/RequeueCallGroupDetailPanel.tsx @@ -1,73 +1,74 @@ -import type { FunctionComponent } from 'react'; -import React, { useState } from 'react'; - import { Tooltip } from '@ringcentral-integration/widgets/components/Rcui/Tooltip'; import { TOOLTIP_LONG_DELAY_TIME } from '@ringcentral-integration/widgets/lib/toolTipDelayTime'; import { RcButton } from '@ringcentral/juno'; +import type { FunctionComponent } from 'react'; +import React, { useState } from 'react'; import type { EvGate } from '../../../../lib/EvClient'; import { ListItem, SelectList } from '../../../SelectList'; import type { RequeueCallGroupPanelProps } from '../RequeueCallGroupPanel'; import styles from '../styles.scss'; + import i18n from './i18n'; -export const RequeueCallGroupDetailPanel: FunctionComponent = - ({ - currentLocale, - goBack, - searchGate, - selectedQueueGroup, - selectedGateId, - submitSelection, - }) => { - const selectText = i18n.getString('selectCheck', currentLocale); - const [queueId, setQueueId] = useState(selectedGateId); +export const RequeueCallGroupDetailPanel: FunctionComponent< + RequeueCallGroupPanelProps +> = ({ + currentLocale, + goBack, + searchGate, + selectedQueueGroup, + selectedGateId, + submitSelection, +}) => { + const selectText = i18n.getString('selectCheck', currentLocale); + const [queueId, setQueueId] = useState(selectedGateId); - return ( - { - const selected = option.gateId === queueId; - return ( - { - setQueueId(selected ? null : option.gateId); - }} - selected={selected} - key={index} - > - - {option.gateName} - - - ); - }} - > -
- submitSelection(queueId)} - fullWidth - size="medium" - data-sign="select-group-item" + return ( + { + const selected = option.gateId === queueId; + return ( + { + setQueueId(selected ? null : option.gateId); + }} + selected={selected} + key={index} > - {selectText} - -
-
- ); - }; + + {option.gateName} + + + ); + }} + > +
+ submitSelection(queueId)} + fullWidth + size="medium" + data-sign="select-group-item" + > + {selectText} + +
+ + ); +}; diff --git a/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/RequeueCallGroupDetailPanel/i18n/en-US.ts b/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/RequeueCallGroupDetailPanel/i18n/en-US.ts index 71aa4a31b6..3402b23b46 100644 --- a/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/RequeueCallGroupDetailPanel/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/RequeueCallGroupDetailPanel/i18n/en-US.ts @@ -1,3 +1,3 @@ export default { selectCheck: 'Select', -}; +} as const; diff --git a/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/RequeueCallGroupDetailPanel/i18n/index.ts b/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/RequeueCallGroupDetailPanel/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/RequeueCallGroupDetailPanel/i18n/index.ts +++ b/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/RequeueCallGroupDetailPanel/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/RequeueCallGroupPanel.spec.tsx b/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/RequeueCallGroupPanel.spec.tsx index 7417148390..b93ba3dd92 100644 --- a/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/RequeueCallGroupPanel.spec.tsx +++ b/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/RequeueCallGroupPanel.spec.tsx @@ -1,8 +1,6 @@ -import React from 'react'; - -import { mount } from 'enzyme'; - import { RcThemeProvider } from '@ringcentral/juno'; +import { mount } from 'enzyme'; +import React from 'react'; import { RequeueCallGroupPanel } from './RequeueCallGroupPanel'; @@ -85,7 +83,7 @@ describe('', () => { .at(0) .find('button') .simulate('click'); - expect(goToRequeueCallPage).toBeCalled(); + expect(goToRequeueCallPage).toHaveBeenCalled(); }); it('When click Requeue Group, can redirct to RequeueGroupDetailPage', () => { @@ -94,7 +92,7 @@ describe('', () => { const selectIndex = 1; getGroupItems().at(selectIndex).find('[role="button"]').simulate('click'); - expect(goToRequeueGroupDetailPage).toBeCalledWith({ + expect(goToRequeueGroupDetailPage).toHaveBeenCalledWith({ groupId: defaultQueueGroups[selectIndex].gateGroupId, }); }); diff --git a/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/RequeueCallGroupPanel.tsx b/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/RequeueCallGroupPanel.tsx index 0ae531367d..7d475c627f 100644 --- a/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/RequeueCallGroupPanel.tsx +++ b/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/RequeueCallGroupPanel.tsx @@ -1,57 +1,58 @@ +import { CustomArrowButton } from '@ringcentral-integration/widgets/components/Rcui/CustomArrowButton'; import type { FunctionComponent } from 'react'; import React from 'react'; -import { CustomArrowButton } from '@ringcentral-integration/widgets/components/Rcui/CustomArrowButton'; - import type { EvTransferCallUIFunctions, EvTransferCallUIProps, } from '../../../interfaces'; import type { EvAvailableRequeueQueue } from '../../../lib/EvClient'; import { ListItem, SelectList } from '../../SelectList'; + import i18n from './i18n'; export type RequeueCallGroupPanelProps = Partial< EvTransferCallUIProps & EvTransferCallUIFunctions >; -export const RequeueCallGroupPanel: FunctionComponent = - ({ - currentLocale, - goToRequeueCallPage, - searchGroup, - queueGroups, - selectedQueueGroupId, - goToRequeueGroupDetailPage, - }) => { - return ( - { - return ( - - goToRequeueGroupDetailPage({ - groupId: option.gateGroupId, - }) - } - selected={option.gateGroupId === selectedQueueGroupId} - key={index} - > - {option.groupName} - - - ); - }} - /> - ); - }; +export const RequeueCallGroupPanel: FunctionComponent< + RequeueCallGroupPanelProps +> = ({ + currentLocale, + goToRequeueCallPage, + searchGroup, + queueGroups, + selectedQueueGroupId, + goToRequeueGroupDetailPage, +}) => { + return ( + { + return ( + + goToRequeueGroupDetailPage({ + groupId: option.gateGroupId, + }) + } + selected={option.gateGroupId === selectedQueueGroupId} + key={index} + > + {option.groupName} + + + ); + }} + /> + ); +}; diff --git a/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/i18n/en-US.ts b/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/i18n/en-US.ts index 2a2b7a6769..dd6b8d8ef8 100644 --- a/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/i18n/en-US.ts @@ -1,4 +1,4 @@ export default { queueGroup: 'Queue Group', search: 'Search ', -}; +} as const; diff --git a/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/i18n/index.ts b/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/i18n/index.ts +++ b/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/styles.scss b/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/styles.scss index b4ca5c7e60..7b34a50590 100644 --- a/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/styles.scss +++ b/packages/engage-voice-widget/components/RequeueCallPanel/RequeueCallGroupPanel/styles.scss @@ -15,7 +15,6 @@ padding: 12px 16px; } - .gateName { overflow: hidden; text-overflow: ellipsis; diff --git a/packages/engage-voice-widget/components/SearchSelectField/SearchSelectField.tsx b/packages/engage-voice-widget/components/SearchSelectField/SearchSelectField.tsx index 69047e5c5e..e1bb8be72a 100644 --- a/packages/engage-voice-widget/components/SearchSelectField/SearchSelectField.tsx +++ b/packages/engage-voice-widget/components/SearchSelectField/SearchSelectField.tsx @@ -1,11 +1,10 @@ -import type { FunctionComponent, ReactNode } from 'react'; -import React, { useState } from 'react'; - import type { SelectListTextFieldProps } from '@ringcentral-integration/widgets/components/CallLogFields'; import { SelectListTextField } from '@ringcentral-integration/widgets/components/CallLogFields'; import { SelectListBasicWithScrollCheck } from '@ringcentral-integration/widgets/components/SelectList'; import type { SelectListBasicProps } from '@ringcentral-integration/widgets/components/SelectListBasic'; import { RcList } from '@ringcentral/juno'; +import type { FunctionComponent, ReactNode } from 'react'; +import React, { useState } from 'react'; import i18n from './i18n'; import styles from './styles.scss'; diff --git a/packages/engage-voice-widget/components/SearchSelectField/i18n/en-US.ts b/packages/engage-voice-widget/components/SearchSelectField/i18n/en-US.ts index c4bc67377d..f4367d53dc 100644 --- a/packages/engage-voice-widget/components/SearchSelectField/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/SearchSelectField/i18n/en-US.ts @@ -1,3 +1,3 @@ export default { search: 'Search ', -}; +} as const; diff --git a/packages/engage-voice-widget/components/SearchSelectField/i18n/index.ts b/packages/engage-voice-widget/components/SearchSelectField/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/SearchSelectField/i18n/index.ts +++ b/packages/engage-voice-widget/components/SearchSelectField/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/SearchSelectField/styles.scss b/packages/engage-voice-widget/components/SearchSelectField/styles.scss index 41dbb42406..000009c581 100644 --- a/packages/engage-voice-widget/components/SearchSelectField/styles.scss +++ b/packages/engage-voice-widget/components/SearchSelectField/styles.scss @@ -6,7 +6,6 @@ top: $working-state-height; } - // TODO: remove SearchSelectField, just use SelectList component, so need not abstract this style .noResult { @include pageSpace(); diff --git a/packages/engage-voice-widget/components/SelectList/BackHeader/BackHeader.tsx b/packages/engage-voice-widget/components/SelectList/BackHeader/BackHeader.tsx index 93618a83e8..9f3fa52ac3 100644 --- a/packages/engage-voice-widget/components/SelectList/BackHeader/BackHeader.tsx +++ b/packages/engage-voice-widget/components/SelectList/BackHeader/BackHeader.tsx @@ -1,8 +1,7 @@ -import type { FunctionComponent } from 'react'; -import React from 'react'; - import type { BackHeaderProps } from '@ringcentral-integration/widgets/components/BackHeaderV2'; import BasicBackHeader from '@ringcentral-integration/widgets/components/BackHeaderV2'; +import type { FunctionComponent } from 'react'; +import React from 'react'; import styles from '../styles.scss'; diff --git a/packages/engage-voice-widget/components/SelectList/ListItem/ListItem.tsx b/packages/engage-voice-widget/components/SelectList/ListItem/ListItem.tsx index 7bbbae0227..3c12935b72 100644 --- a/packages/engage-voice-widget/components/SelectList/ListItem/ListItem.tsx +++ b/packages/engage-voice-widget/components/SelectList/ListItem/ListItem.tsx @@ -1,10 +1,8 @@ -import type { FunctionComponent } from 'react'; -import React from 'react'; - -import classNames from 'classnames'; - import type { ListItemProps } from '@ringcentral-integration/widgets/components/SelectListV2'; import { ListItem as BaseListItem } from '@ringcentral-integration/widgets/components/SelectListV2'; +import clsx from 'clsx'; +import type { FunctionComponent } from 'react'; +import React from 'react'; import styles from './styles.scss'; @@ -14,7 +12,7 @@ export const ListItem: FunctionComponent = ({ ...rest }) => { return ( - + {children} ); diff --git a/packages/engage-voice-widget/components/SelectList/SelectList.tsx b/packages/engage-voice-widget/components/SelectList/SelectList.tsx index c303f8a85a..2874cf29f9 100644 --- a/packages/engage-voice-widget/components/SelectList/SelectList.tsx +++ b/packages/engage-voice-widget/components/SelectList/SelectList.tsx @@ -1,8 +1,7 @@ -import type { FunctionComponent } from 'react'; -import React from 'react'; - import type { SelectListV2Props } from '@ringcentral-integration/widgets/components/SelectListV2'; import { SelectListV2 } from '@ringcentral-integration/widgets/components/SelectListV2'; +import type { FunctionComponent } from 'react'; +import React from 'react'; import styles from './styles.scss'; diff --git a/packages/engage-voice-widget/components/SessionConfigPanel/SessionConfigPanel.spec.tsx b/packages/engage-voice-widget/components/SessionConfigPanel/SessionConfigPanel.spec.tsx index a392dc288f..4c6ad6b745 100644 --- a/packages/engage-voice-widget/components/SessionConfigPanel/SessionConfigPanel.spec.tsx +++ b/packages/engage-voice-widget/components/SessionConfigPanel/SessionConfigPanel.spec.tsx @@ -1,10 +1,9 @@ -import React from 'react'; - -import { mount } from 'enzyme'; - import { RcThemeProvider } from '@ringcentral/juno'; +import { mount } from 'enzyme'; +import React from 'react'; import type { EvAgent } from '../../lib/EvClient'; + import type { SessionConfigPanelProps } from './SessionConfigPanel'; import { SessionConfigPanel } from './SessionConfigPanel'; @@ -155,7 +154,7 @@ describe('', () => { }); const configureButton = getConfigureButton(); configureButton.simulate('click'); - expect(setConfigure).toBeCalled(); + expect(setConfigure).toHaveBeenCalled(); }); it('When loading, setConfigure Button is in loading state, and setConfigure cannot be fired', () => { @@ -169,6 +168,6 @@ describe('', () => { expect(configureButton.find('RcCircularProgress')).toHaveLength(1); expect(configureButton.prop('disabled')).toBe(isLoading); configureButton.simulate('click'); - expect(setConfigure).not.toBeCalled(); + expect(setConfigure).not.toHaveBeenCalled(); }); }); diff --git a/packages/engage-voice-widget/components/SessionConfigPanel/SessionConfigPanel.tsx b/packages/engage-voice-widget/components/SessionConfigPanel/SessionConfigPanel.tsx index 1c51c337e9..99228111ec 100644 --- a/packages/engage-voice-widget/components/SessionConfigPanel/SessionConfigPanel.tsx +++ b/packages/engage-voice-widget/components/SessionConfigPanel/SessionConfigPanel.tsx @@ -1,6 +1,3 @@ -import type { FunctionComponent } from 'react'; -import React from 'react'; - import { palette2, RcButton, @@ -9,6 +6,8 @@ import { styled, } from '@ringcentral/juno'; import { ArrowLeft2 as arrowLeftSvg } from '@ringcentral/juno-icon'; +import type { FunctionComponent } from 'react'; +import React from 'react'; import type { EvAgentSessionUIFunctions, @@ -17,6 +16,7 @@ import type { import type { BasicSessionPanelProps } from '../BasicSessionPanel'; import { BasicSessionPanel } from '../BasicSessionPanel'; import { EvLoginHeader } from '../EvLoginHeader'; + import i18n from './i18n'; import styles from './styles.scss'; diff --git a/packages/engage-voice-widget/components/SessionConfigPanel/i18n/en-US.ts b/packages/engage-voice-widget/components/SessionConfigPanel/i18n/en-US.ts index e7e4d71fd6..efd4c301f6 100644 --- a/packages/engage-voice-widget/components/SessionConfigPanel/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/SessionConfigPanel/i18n/en-US.ts @@ -10,4 +10,4 @@ export default { switchAccount: 'Switch account', agent: 'Agent', supervisor: 'Supervisor', -}; +} as const; diff --git a/packages/engage-voice-widget/components/SessionConfigPanel/i18n/index.ts b/packages/engage-voice-widget/components/SessionConfigPanel/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/SessionConfigPanel/i18n/index.ts +++ b/packages/engage-voice-widget/components/SessionConfigPanel/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/SessionUpdatePanel/SessionUpdatePanel.tsx b/packages/engage-voice-widget/components/SessionUpdatePanel/SessionUpdatePanel.tsx index 44faa6378c..28c06281bb 100644 --- a/packages/engage-voice-widget/components/SessionUpdatePanel/SessionUpdatePanel.tsx +++ b/packages/engage-voice-widget/components/SessionUpdatePanel/SessionUpdatePanel.tsx @@ -1,8 +1,7 @@ +import { RcButton } from '@ringcentral/juno'; import type { FunctionComponent } from 'react'; import React from 'react'; -import { RcButton } from '@ringcentral/juno'; - import type { EvAgentSessionUIFunctions, EvAgentSessionUIProps, @@ -10,6 +9,7 @@ import type { import type { BasicSessionPanelProps } from '../BasicSessionPanel'; import { BasicSessionPanel } from '../BasicSessionPanel'; import { BackHeader } from '../SelectList'; + import i18n from './i18n'; import styles from './styles.scss'; diff --git a/packages/engage-voice-widget/components/SessionUpdatePanel/SessionUpdatePanel.ut.tsx b/packages/engage-voice-widget/components/SessionUpdatePanel/SessionUpdatePanel.ut.tsx index fb172dd75f..08d10b9f86 100644 --- a/packages/engage-voice-widget/components/SessionUpdatePanel/SessionUpdatePanel.ut.tsx +++ b/packages/engage-voice-widget/components/SessionUpdatePanel/SessionUpdatePanel.ut.tsx @@ -1,9 +1,7 @@ -import React from 'react'; - -import { mount } from 'enzyme'; - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { RcThemeProvider } from '@ringcentral/juno'; +import { mount } from 'enzyme'; +import React from 'react'; import type { SessionUpdatePanelProps } from './SessionUpdatePanel'; import { SessionUpdatePanel } from './SessionUpdatePanel'; diff --git a/packages/engage-voice-widget/components/SessionUpdatePanel/i18n/en-US.ts b/packages/engage-voice-widget/components/SessionUpdatePanel/i18n/en-US.ts index 9f58fe4ff8..3ce409edf6 100644 --- a/packages/engage-voice-widget/components/SessionUpdatePanel/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/SessionUpdatePanel/i18n/en-US.ts @@ -2,4 +2,4 @@ export default { saveUpdate: 'Save update', cancel: 'Cancel', editSession: 'Edit Session', -}; +} as const; diff --git a/packages/engage-voice-widget/components/SessionUpdatePanel/i18n/index.ts b/packages/engage-voice-widget/components/SessionUpdatePanel/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/SessionUpdatePanel/i18n/index.ts +++ b/packages/engage-voice-widget/components/SessionUpdatePanel/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/SettingsPanel/SettingsPanel.tsx b/packages/engage-voice-widget/components/SettingsPanel/SettingsPanel.tsx index b10a956678..bf74b6e773 100644 --- a/packages/engage-voice-widget/components/SettingsPanel/SettingsPanel.tsx +++ b/packages/engage-voice-widget/components/SettingsPanel/SettingsPanel.tsx @@ -1,17 +1,16 @@ -import type { FunctionComponent } from 'react'; -import React from 'react'; - -import classNames from 'classnames'; - import { Tooltip } from '@ringcentral-integration/widgets/components/Rcui/Tooltip'; import { TOOLTIP_LONG_DELAY_TIME } from '@ringcentral-integration/widgets/lib/toolTipDelayTime'; import { RcButton, RcIconButton, RcTypography } from '@ringcentral/juno'; import { Edit } from '@ringcentral/juno-icon'; +import clsx from 'clsx'; +import type { FunctionComponent } from 'react'; +import React from 'react'; import type { EvSettingsUIFunctions, EvSettingsUIProps, } from '../../interfaces/EvSettingsUI.interface'; + import i18n from './i18n'; import styles from './styles.scss'; @@ -30,7 +29,7 @@ export const SettingsPanel: FunctionComponent = ({ }) => { return (
-
+
{agentName && (
{agentName}
@@ -40,12 +39,12 @@ export const SettingsPanel: FunctionComponent = ({
{userName}
-
+
{i18n.getString('sessionInfo', currentLocale)} {showEditSessionIcon && ( = ({ )}
-
+
{sessionInfo.map(({ value, label }) => (
@@ -72,14 +71,14 @@ export const SettingsPanel: FunctionComponent = ({
))} -
+
{i18n.getString('version', currentLocale)} {version}
-
+
(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/SettingsPanel/styles.scss b/packages/engage-voice-widget/components/SettingsPanel/styles.scss index e5d102b516..7b50e507fc 100644 --- a/packages/engage-voice-widget/components/SettingsPanel/styles.scss +++ b/packages/engage-voice-widget/components/SettingsPanel/styles.scss @@ -28,7 +28,7 @@ $font-wight: 700; } .name, - .version{ + .version { @include pageSpace('padding'); } @@ -71,7 +71,7 @@ $font-wight: 700; font-size: 14px; color: rgb(108, 108, 108); @include pageSpace('padding'); - span{ + span { vertical-align: -webkit-baseline-middle; display: inline-block; } diff --git a/packages/engage-voice-widget/components/SmallCallControl/SmallCallControl.tsx b/packages/engage-voice-widget/components/SmallCallControl/SmallCallControl.tsx index f86ff9551f..9565a576b9 100644 --- a/packages/engage-voice-widget/components/SmallCallControl/SmallCallControl.tsx +++ b/packages/engage-voice-widget/components/SmallCallControl/SmallCallControl.tsx @@ -1,8 +1,7 @@ +import clsx from 'clsx'; import type { FunctionComponent } from 'react'; import React from 'react'; -import classNames from 'classnames'; - import type { CallButtonsProps, HangUpButtonProps, @@ -35,7 +34,7 @@ export const SmallCallControl: FunctionComponent = ({ }) => { return (
{children || ( diff --git a/packages/engage-voice-widget/components/SmallCallControl/components/HangUpButton.tsx b/packages/engage-voice-widget/components/SmallCallControl/components/HangUpButton.tsx index d1840fc363..0a62727a86 100644 --- a/packages/engage-voice-widget/components/SmallCallControl/components/HangUpButton.tsx +++ b/packages/engage-voice-widget/components/SmallCallControl/components/HangUpButton.tsx @@ -1,14 +1,13 @@ -import type { FunctionComponent } from 'react'; -import React from 'react'; - -import classnames from 'classnames'; - import { RcIconButton } from '@ringcentral/juno'; import { HandUp } from '@ringcentral/juno-icon'; +import clsx from 'clsx'; +import type { FunctionComponent } from 'react'; +import React from 'react'; import { getCircleIconButtonTitle } from '../help'; import i18n from '../i18n'; import styles from '../styles.scss'; + import type { CallButtonsProps } from './CallButtons.interface'; export type HangUpButtonProps = CallButtonsProps & { @@ -42,7 +41,7 @@ export const HangUpButton: FunctionComponent = ({ onClick={isInComingCall ? onReject : onHangup} disabled={disableHangup} size={size} - className={classnames(styles.hangup, className)} + className={clsx(styles.hangup, className)} disableRipple data-sign={dataSign} /> diff --git a/packages/engage-voice-widget/components/SmallCallControl/components/HoldCallButton.tsx b/packages/engage-voice-widget/components/SmallCallControl/components/HoldCallButton.tsx index d7937ab852..125531ce76 100644 --- a/packages/engage-voice-widget/components/SmallCallControl/components/HoldCallButton.tsx +++ b/packages/engage-voice-widget/components/SmallCallControl/components/HoldCallButton.tsx @@ -1,11 +1,11 @@ -import type { FunctionComponent } from 'react'; -import React from 'react'; - import { RcIconButton } from '@ringcentral/juno'; import { Hold } from '@ringcentral/juno-icon'; +import type { FunctionComponent } from 'react'; +import React from 'react'; import { getCircleIconButtonTitle } from '../help'; import i18n from '../i18n'; + import type { CallButtonsProps } from './CallButtons.interface'; import { getIconColor } from './getIconColor'; diff --git a/packages/engage-voice-widget/components/SmallCallControl/components/MuteCallButton.tsx b/packages/engage-voice-widget/components/SmallCallControl/components/MuteCallButton.tsx index 09dc84b9e4..43227781c6 100644 --- a/packages/engage-voice-widget/components/SmallCallControl/components/MuteCallButton.tsx +++ b/packages/engage-voice-widget/components/SmallCallControl/components/MuteCallButton.tsx @@ -1,11 +1,11 @@ -import type { FunctionComponent } from 'react'; -import React from 'react'; - import { RcIconButton } from '@ringcentral/juno'; import { Mic, MicOff } from '@ringcentral/juno-icon'; +import type { FunctionComponent } from 'react'; +import React from 'react'; import { getCircleIconButtonTitle } from '../help'; import i18n from '../i18n'; + import type { CallButtonsProps } from './CallButtons.interface'; import { getIconColor } from './getIconColor'; diff --git a/packages/engage-voice-widget/components/SmallCallControl/components/TransferCallButton.tsx b/packages/engage-voice-widget/components/SmallCallControl/components/TransferCallButton.tsx index f4a066736d..c35666bb3f 100644 --- a/packages/engage-voice-widget/components/SmallCallControl/components/TransferCallButton.tsx +++ b/packages/engage-voice-widget/components/SmallCallControl/components/TransferCallButton.tsx @@ -1,10 +1,10 @@ -import type { FunctionComponent } from 'react'; -import React from 'react'; - import { RcIconButton } from '@ringcentral/juno'; import { TransferCall } from '@ringcentral/juno-icon'; +import type { FunctionComponent } from 'react'; +import React from 'react'; import i18n from '../i18n'; + import type { CallButtonsProps } from './CallButtons.interface'; import { getIconColor } from './getIconColor'; diff --git a/packages/engage-voice-widget/components/SmallCallControl/i18n/en-US.ts b/packages/engage-voice-widget/components/SmallCallControl/i18n/en-US.ts index 7e4b5903ca..b0e71eb96f 100644 --- a/packages/engage-voice-widget/components/SmallCallControl/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/SmallCallControl/i18n/en-US.ts @@ -6,4 +6,4 @@ export default { hold: 'Hold', onHold: 'Unhold', transfer: 'Transfer', -}; +} as const; diff --git a/packages/engage-voice-widget/components/SmallCallControl/i18n/index.ts b/packages/engage-voice-widget/components/SmallCallControl/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/SmallCallControl/i18n/index.ts +++ b/packages/engage-voice-widget/components/SmallCallControl/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/TransferCallPanel/TransferCallPanel.tsx b/packages/engage-voice-widget/components/TransferCallPanel/TransferCallPanel.tsx index 1afe76686f..5979edfcd6 100644 --- a/packages/engage-voice-widget/components/TransferCallPanel/TransferCallPanel.tsx +++ b/packages/engage-voice-widget/components/TransferCallPanel/TransferCallPanel.tsx @@ -1,6 +1,3 @@ -import type { FunctionComponent } from 'react'; -import React, { useCallback, useEffect } from 'react'; - import { CustomArrowButton } from '@ringcentral-integration/widgets/components/Rcui/CustomArrowButton'; import { RcButton, @@ -10,6 +7,8 @@ import { RcTextField, } from '@ringcentral/juno'; import { Dialer as dialerSvg } from '@ringcentral/juno-icon'; +import type { FunctionComponent } from 'react'; +import React, { useCallback, useEffect } from 'react'; import type { EvTransferCallUIFunctions, @@ -17,6 +16,7 @@ import type { } from '../../interfaces'; import { PickList } from '../PickList'; import { BackHeader } from '../SelectList'; + import i18n from './i18n'; import styles from './styles.scss'; diff --git a/packages/engage-voice-widget/components/TransferCallPanel/TransferCallPanel.ut.tsx b/packages/engage-voice-widget/components/TransferCallPanel/TransferCallPanel.ut.tsx index ded58fe6cb..2a6eed6435 100644 --- a/packages/engage-voice-widget/components/TransferCallPanel/TransferCallPanel.ut.tsx +++ b/packages/engage-voice-widget/components/TransferCallPanel/TransferCallPanel.ut.tsx @@ -1,15 +1,14 @@ -import React from 'react'; - -import { mount } from 'enzyme'; - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { RcThemeProvider } from '@ringcentral/juno'; +import { mount } from 'enzyme'; +import React from 'react'; import { transferTypes } from '../../enums/transferTypes'; import type { EvTransferOption } from '../../interfaces'; -import i18n from './i18n'; + import type { TransferCallPanelProps } from './TransferCallPanel'; import { TransferCallPanel } from './TransferCallPanel'; +import i18n from './i18n'; const currentLocale = 'en-US'; const defaultTransferOptions: EvTransferOption[] = [ @@ -172,7 +171,7 @@ export const UTCheckBackButton: StepFunction = () => { .at(0) .find('button') .simulate('click'); - expect(goBack).toBeCalled(); + expect(goBack).toHaveBeenCalled(); }; export const UTUserClickCallRecipientCases = [ @@ -203,7 +202,7 @@ export const UTUserClickCallRecipient: StepFunction = () => { callRecipient.find('input').at(0).simulate('click'); - expect(clickCallRecipient).toBeCalledWith( + expect(clickCallRecipient).toHaveBeenCalledWith( defaultTextFields[selectIndex].router, ); }; @@ -234,7 +233,7 @@ export const UTSetStayOnCall: StepFunction = ({ isStayOnCall }) => { .at(0); expect(checkbox.prop('checked')).toBe(isStayOnCall); checkbox.simulate('click'); - expect(setStayOnCall).toBeCalledWith(isStayOnCall); + expect(setStayOnCall).toHaveBeenCalledWith(isStayOnCall); wrapper.unmount(); }; @@ -247,7 +246,7 @@ export const UTTransferCallButtonDisabled: StepFunction = () => { const transferCallButton = getTransferCallButton(wrapper); expect(transferCallButton.prop('disabled')).toBe(true); transferCallButton.simulate('click'); - expect(transferCall).not.toBeCalled(); + expect(transferCall).not.toHaveBeenCalled(); }; export const UTWhenCallTransfering: StepFunction = () => { diff --git a/packages/engage-voice-widget/components/TransferCallPanel/i18n/en-US.ts b/packages/engage-voice-widget/components/TransferCallPanel/i18n/en-US.ts index b5b4f15825..d6911bc0dd 100644 --- a/packages/engage-voice-widget/components/TransferCallPanel/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/TransferCallPanel/i18n/en-US.ts @@ -4,4 +4,4 @@ export default { transfer: 'Transfer', cancel: 'Cancel', transferModalTitle: 'Call transfer failed', -}; +} as const; diff --git a/packages/engage-voice-widget/components/TransferCallPanel/i18n/index.ts b/packages/engage-voice-widget/components/TransferCallPanel/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/TransferCallPanel/i18n/index.ts +++ b/packages/engage-voice-widget/components/TransferCallPanel/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/TransferCallRecipient/InternalPanel/InternalPanel.tsx b/packages/engage-voice-widget/components/TransferCallRecipient/InternalPanel/InternalPanel.tsx index e002600c45..f4b86d1231 100644 --- a/packages/engage-voice-widget/components/TransferCallRecipient/InternalPanel/InternalPanel.tsx +++ b/packages/engage-voice-widget/components/TransferCallRecipient/InternalPanel/InternalPanel.tsx @@ -1,8 +1,7 @@ +import clsx from 'clsx'; import type { FunctionComponent } from 'react'; import React, { useEffect } from 'react'; -import classNames from 'classnames'; - import type { EvTransferCallUIFunctions, EvTransferCallUIProps, @@ -11,6 +10,7 @@ import { getInternalTransferName } from '../../../modules/EvTransferCallUI'; import { ListItem, SelectList } from '../../SelectList'; import transferCallI18n from '../i18n'; import transferCallStyles from '../styles.scss'; + import i18n from './i18n'; import styles from './styles.scss'; @@ -73,10 +73,10 @@ const InternalPanel: FunctionComponent = ({ data-sign="agentItem" >
-
+

{ .at(0) .find('button') .simulate('click'); - expect(goBack).toBeCalled(); + expect(goBack).toHaveBeenCalled(); }; export const UTAgentListAutoSync: StepFunction = () => { @@ -190,17 +189,18 @@ interface UTCheckInternalPanelRenderProps { internalOptions: string; } -export const UTCheckInternalPanelRender: StepFunction = - async ({ internalOptions }) => { - const wrapper = setup({}); - const dataSign = { - 'Search bar': 'searchBar', - 'internal recipient list': 'searchResult', - }; - expect( - wrapper.find(`[data-sign="${dataSign[internalOptions]}"]`), - ).not.toBeUndefined(); +export const UTCheckInternalPanelRender: StepFunction< + UTCheckInternalPanelRenderProps +> = async ({ internalOptions }) => { + const wrapper = setup({}); + const dataSign = { + 'Search bar': 'searchBar', + 'internal recipient list': 'searchResult', }; + expect( + wrapper.find(`[data-sign="${dataSign[internalOptions]}"]`), + ).not.toBeUndefined(); +}; export const UTCheckTransferAgentSelectCases = [ { @@ -222,21 +222,22 @@ interface UTCheckTransferAgentSelectProps { recipientDisplay: string; } -export const UTCheckTransferAgentSelect: StepFunction = - async ({ internalItem, available, recipientDisplay }) => { - const changeTransferAgentId = jest.fn(() => {}); - const agentId = '10003'; - wrapper = setup({ - changeTransferAgentId, - transferAgentList: [ - { agentId, firstName: internalItem, lastName: '', available }, - ], - }); - const selectIndex = 0; - const agentItems = getAgentItems(); - agentItems.at(selectIndex).find('[role="button"]').at(0).simulate('click'); - expect(changeTransferAgentId).toBeCalledWith(agentId); - }; +export const UTCheckTransferAgentSelect: StepFunction< + UTCheckTransferAgentSelectProps +> = async ({ internalItem, available, recipientDisplay }) => { + const changeTransferAgentId = jest.fn(() => {}); + const agentId = '10003'; + wrapper = setup({ + changeTransferAgentId, + transferAgentList: [ + { agentId, firstName: internalItem, lastName: '', available }, + ], + }); + const selectIndex = 0; + const agentItems = getAgentItems(); + agentItems.at(selectIndex).find('[role="button"]').at(0).simulate('click'); + expect(changeTransferAgentId).toHaveBeenCalledWith(agentId); +}; export const UTCheckAgentListRenderCases = [ { @@ -319,25 +320,26 @@ interface UTCheckAgentListRenderProps { availableStatus: 'Available' | 'Unavailable'; statusColor: string; } -export const UTCheckAgentListRender: StepFunction = - async ({ - agentState, - available, - recipient, - availableStatus, - statusColor, - }) => { - const wrapper = setup({ - transferAgentList: [ - { agentState, firstName: recipient, lastName: '', available }, - ], - }); - const agentItem = wrapper.find('[data-sign="agentItem"]'); - expect(agentItem.find('.agentName').text().trim()).toBe(recipient); - expect(agentItem.find('.statusText').text().trim()).toBe(availableStatus); - if (statusColor === 'green') { - expect(agentItem.find('.available')).toHaveLength(1); - } else if (statusColor === 'gray') { - expect(agentItem.find('.unavailable')).toHaveLength(1); - } - }; +export const UTCheckAgentListRender: StepFunction< + UTCheckAgentListRenderProps +> = async ({ + agentState, + available, + recipient, + availableStatus, + statusColor, +}) => { + const wrapper = setup({ + transferAgentList: [ + { agentState, firstName: recipient, lastName: '', available }, + ], + }); + const agentItem = wrapper.find('[data-sign="agentItem"]'); + expect(agentItem.find('.agentName').text().trim()).toBe(recipient); + expect(agentItem.find('.statusText').text().trim()).toBe(availableStatus); + if (statusColor === 'green') { + expect(agentItem.find('.available')).toHaveLength(1); + } else if (statusColor === 'gray') { + expect(agentItem.find('.unavailable')).toHaveLength(1); + } +}; diff --git a/packages/engage-voice-widget/components/TransferCallRecipient/InternalPanel/i18n/en-US.ts b/packages/engage-voice-widget/components/TransferCallRecipient/InternalPanel/i18n/en-US.ts index e155edf689..fffbbfb114 100644 --- a/packages/engage-voice-widget/components/TransferCallRecipient/InternalPanel/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/TransferCallRecipient/InternalPanel/i18n/en-US.ts @@ -2,4 +2,4 @@ export default { internalCallRecipient: 'Internal call recipient', available: 'Available', unavailable: 'Unavailable', -}; +} as const; diff --git a/packages/engage-voice-widget/components/TransferCallRecipient/InternalPanel/i18n/index.ts b/packages/engage-voice-widget/components/TransferCallRecipient/InternalPanel/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/TransferCallRecipient/InternalPanel/i18n/index.ts +++ b/packages/engage-voice-widget/components/TransferCallRecipient/InternalPanel/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/TransferCallRecipient/ManualEntryPanel/ManualEntryPanel.spec.tsx b/packages/engage-voice-widget/components/TransferCallRecipient/ManualEntryPanel/ManualEntryPanel.spec.tsx index 3638799f37..bf39b5f489 100644 --- a/packages/engage-voice-widget/components/TransferCallRecipient/ManualEntryPanel/ManualEntryPanel.spec.tsx +++ b/packages/engage-voice-widget/components/TransferCallRecipient/ManualEntryPanel/ManualEntryPanel.spec.tsx @@ -1,10 +1,8 @@ -import React from 'react'; - +import { RcThemeProvider } from '@ringcentral/juno'; import { mount } from 'enzyme'; +import React from 'react'; import { act } from 'react-dom/test-utils'; -import { RcThemeProvider } from '@ringcentral/juno'; - import type { ManualEntryPanelProps } from './ManualEntryPanel'; import { ManualEntryPanel } from './ManualEntryPanel'; @@ -57,7 +55,7 @@ describe('', () => { .at(0) .find('button') .simulate('click'); - expect(goBack).toBeCalled(); + expect(goBack).toHaveBeenCalled(); }); it('Display Next Button and when user click it, function changeRecipientNumber will be called', () => { @@ -75,6 +73,6 @@ describe('', () => { .at(0) .find('button') .simulate('click'); - expect(changeRecipientNumber).toBeCalledWith(userInput); + expect(changeRecipientNumber).toHaveBeenCalledWith(userInput); }); }); diff --git a/packages/engage-voice-widget/components/TransferCallRecipient/ManualEntryPanel/ManualEntryPanel.tsx b/packages/engage-voice-widget/components/TransferCallRecipient/ManualEntryPanel/ManualEntryPanel.tsx index 8502f327f8..2850f795ed 100644 --- a/packages/engage-voice-widget/components/TransferCallRecipient/ManualEntryPanel/ManualEntryPanel.tsx +++ b/packages/engage-voice-widget/components/TransferCallRecipient/ManualEntryPanel/ManualEntryPanel.tsx @@ -1,14 +1,14 @@ +import { RcButton } from '@ringcentral/juno'; import type { FunctionComponent } from 'react'; import React, { useState } from 'react'; -import { RcButton } from '@ringcentral/juno'; - import type { EvTransferCallUIFunctions, EvTransferCallUIProps, } from '../../../interfaces'; import { Dialer } from '../../DialerPanel'; import { BackHeader } from '../../SelectList'; + import i18n from './i18n'; import styles from './styles.scss'; diff --git a/packages/engage-voice-widget/components/TransferCallRecipient/ManualEntryPanel/ManualEntryPanel.ut.tsx b/packages/engage-voice-widget/components/TransferCallRecipient/ManualEntryPanel/ManualEntryPanel.ut.tsx index 98bb5b58a3..57b47e2e3c 100644 --- a/packages/engage-voice-widget/components/TransferCallRecipient/ManualEntryPanel/ManualEntryPanel.ut.tsx +++ b/packages/engage-voice-widget/components/TransferCallRecipient/ManualEntryPanel/ManualEntryPanel.ut.tsx @@ -1,9 +1,7 @@ -import React from 'react'; - -import { mount } from 'enzyme'; - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { RcThemeProvider } from '@ringcentral/juno'; +import { mount } from 'enzyme'; +import React from 'react'; import type { ManualEntryPanelProps } from './ManualEntryPanel'; import { ManualEntryPanel } from './ManualEntryPanel'; @@ -48,75 +46,75 @@ interface UTCheckManualEntryRenderProps { internalOptions: string; } -export const UTCheckManualEntryRender: StepFunction = - async ({ internalOptions }) => { - const wrapper = setup({}); - const dataSign = { - 'Enter number field': 'transferRecipientNumber', - Dialpad: 'dialPad', - }; - expect( - wrapper.find(`[data-sign="${dataSign[internalOptions]}"]`), - ).not.toBeUndefined(); +export const UTCheckManualEntryRender: StepFunction< + UTCheckManualEntryRenderProps +> = async ({ internalOptions }) => { + const wrapper = setup({}); + const dataSign = { + 'Enter number field': 'transferRecipientNumber', + Dialpad: 'dialPad', }; + expect( + wrapper.find(`[data-sign="${dataSign[internalOptions]}"]`), + ).not.toBeUndefined(); +}; -export const UTManualEntryInternationalTransferForbid: StepFunction = - async () => { - const transferRecipientCountryId = 'FRA'; - const allowManualInternationalTransfer = false; - wrapper = setup({ - allowManualInternationalTransfer, - transferRecipientCountryId, - }); - expect(wrapper.find('PickList[data-sign="transferCountry"]')).toHaveLength( - 0, - ); - }; +export const UTManualEntryInternationalTransferForbid: StepFunction< + any +> = async () => { + const transferRecipientCountryId = 'FRA'; + const allowManualInternationalTransfer = false; + wrapper = setup({ + allowManualInternationalTransfer, + transferRecipientCountryId, + }); + expect(wrapper.find('PickList[data-sign="transferCountry"]')).toHaveLength(0); +}; -export const UTManualEntryInternationalTransferAllowed: StepFunction = - async () => { - const changeRecipientCountryId = jest.fn(() => {}); - const allowManualInternationalTransfer = true; - const countryId = 'GER'; - wrapper = setup({ - allowManualInternationalTransfer, - changeRecipientCountryId, - }); +export const UTManualEntryInternationalTransferAllowed: StepFunction< + any +> = async () => { + const changeRecipientCountryId = jest.fn(() => {}); + const allowManualInternationalTransfer = true; + const countryId = 'GER'; + wrapper = setup({ + allowManualInternationalTransfer, + changeRecipientCountryId, + }); - const transferCountry = wrapper - .find('PickList[data-sign="transferCountry"]') - .at(0); - transferCountry.find('[role="button"]').simulate('click'); - document.body - .querySelector(`li[data-value="${countryId}"]`) - .click(); - expect(changeRecipientCountryId).toBeCalledWith(countryId); - }; + const transferCountry = wrapper + .find('PickList[data-sign="transferCountry"]') + .at(0); + transferCountry.find('[role="button"]').simulate('click'); + document.body + .querySelector(`li[data-value="${countryId}"]`) + .click(); + expect(changeRecipientCountryId).toHaveBeenCalledWith(countryId); +}; -export const UTManualEntryInternationalTransferRender: StepFunction = - async () => { - const transferRecipientCountryId = 'FRA'; - const transferRecipientNumber = '6508653454'; - const allowManualInternationalTransfer = true; - wrapper = setup({ - allowManualInternationalTransfer, - transferRecipientCountryId, - transferRecipientNumber, - }); - const transferCountry = wrapper.find( - 'PickList[data-sign="transferCountry"]', - ); - expect(transferCountry.prop('value')).toBe(transferRecipientCountryId); +export const UTManualEntryInternationalTransferRender: StepFunction< + any +> = async () => { + const transferRecipientCountryId = 'FRA'; + const transferRecipientNumber = '6508653454'; + const allowManualInternationalTransfer = true; + wrapper = setup({ + allowManualInternationalTransfer, + transferRecipientCountryId, + transferRecipientNumber, + }); + const transferCountry = wrapper.find('PickList[data-sign="transferCountry"]'); + expect(transferCountry.prop('value')).toBe(transferRecipientCountryId); - expect(transferCountry.find('[role="button"]').text()).toBe( - defaultTransferCountryOptions.filter( - (x) => x.countryId === transferRecipientCountryId, - )[0].countryName, - ); + expect(transferCountry.find('[role="button"]').text()).toBe( + defaultTransferCountryOptions.filter( + (x) => x.countryId === transferRecipientCountryId, + )[0].countryName, + ); - expect( - wrapper - .find('RecipientsInput[data-sign="transferRecipientNumber"]') - .prop('value'), - ).toBe(transferRecipientNumber); - }; + expect( + wrapper + .find('RecipientsInput[data-sign="transferRecipientNumber"]') + .prop('value'), + ).toBe(transferRecipientNumber); +}; diff --git a/packages/engage-voice-widget/components/TransferCallRecipient/ManualEntryPanel/i18n/en-US.ts b/packages/engage-voice-widget/components/TransferCallRecipient/ManualEntryPanel/i18n/en-US.ts index 60e9ca0960..91572c5d65 100644 --- a/packages/engage-voice-widget/components/TransferCallRecipient/ManualEntryPanel/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/TransferCallRecipient/ManualEntryPanel/i18n/en-US.ts @@ -3,4 +3,4 @@ export default { dialPlaceholder: 'Enter number', next: 'Next', transferCountry: 'Country', -}; +} as const; diff --git a/packages/engage-voice-widget/components/TransferCallRecipient/ManualEntryPanel/i18n/index.ts b/packages/engage-voice-widget/components/TransferCallRecipient/ManualEntryPanel/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/TransferCallRecipient/ManualEntryPanel/i18n/index.ts +++ b/packages/engage-voice-widget/components/TransferCallRecipient/ManualEntryPanel/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/TransferCallRecipient/PhoneBookPanel/PhoneBookPanel.tsx b/packages/engage-voice-widget/components/TransferCallRecipient/PhoneBookPanel/PhoneBookPanel.tsx index 9736168929..bcf9bb6fe3 100644 --- a/packages/engage-voice-widget/components/TransferCallRecipient/PhoneBookPanel/PhoneBookPanel.tsx +++ b/packages/engage-voice-widget/components/TransferCallRecipient/PhoneBookPanel/PhoneBookPanel.tsx @@ -1,10 +1,8 @@ -import type { FunctionComponent } from 'react'; -import React from 'react'; - -import classNames from 'classnames'; - import { Tooltip } from '@ringcentral-integration/widgets/components/Rcui/Tooltip'; import { TOOLTIP_LONG_DELAY_TIME } from '@ringcentral-integration/widgets/lib/toolTipDelayTime'; +import clsx from 'clsx'; +import type { FunctionComponent } from 'react'; +import React from 'react'; import type { EvTransferCallUIFunctions, @@ -14,6 +12,7 @@ import { formatPhoneNumber } from '../../../lib/FormatPhoneNumber'; import { ListItem, SelectList } from '../../SelectList'; import transferCallI18n from '../i18n'; import transferCallStyles from '../styles.scss'; + import i18n from './i18n'; import styles from './styles.scss'; @@ -71,7 +70,7 @@ const PhoneBookPanel: FunctionComponent = ({

-

+

{formatPhoneNumber({ phoneNumber: destination, currentLocale, diff --git a/packages/engage-voice-widget/components/TransferCallRecipient/PhoneBookPanel/PhoneBookPanel.ut.tsx b/packages/engage-voice-widget/components/TransferCallRecipient/PhoneBookPanel/PhoneBookPanel.ut.tsx index 3ec1f60401..00eefa8c08 100644 --- a/packages/engage-voice-widget/components/TransferCallRecipient/PhoneBookPanel/PhoneBookPanel.ut.tsx +++ b/packages/engage-voice-widget/components/TransferCallRecipient/PhoneBookPanel/PhoneBookPanel.ut.tsx @@ -1,12 +1,11 @@ -import React from 'react'; - -import { mount } from 'enzyme'; - import { format } from '@ringcentral-integration/phone-number'; import type { StepFunction } from '@ringcentral-integration/test-utils'; import { RcThemeProvider } from '@ringcentral/juno'; +import { mount } from 'enzyme'; +import React from 'react'; import type { EvTransferPhoneBookItem } from '../../../lib/EvClient'; + import type { PhoneBookPanelProps } from './PhoneBookPanel'; import { PhoneBookPanel } from './PhoneBookPanel'; @@ -75,7 +74,7 @@ export const UTPhoneBookCheckBackButton: StepFunction = () => { .at(0) .find('button') .simulate('click'); - expect(goBack).toBeCalled(); + expect(goBack).toHaveBeenCalled(); }; export const UTPhoneBookContactListDisplayAndHighlight: StepFunction = () => { @@ -99,7 +98,7 @@ export const UTPhoneBookContactCanBeClicked: StepFunction = () => { .find('[role="button"]') .at(0) .simulate('click'); - expect(changeTransferPhoneBookSelected).toBeCalled(); + expect(changeTransferPhoneBookSelected).toHaveBeenCalled(); }; export const UTPhoneBookListSearchCases = [ @@ -185,52 +184,54 @@ interface UTPhoneBookListSearchProps { matchedResult: string[] | string; } -export const UTPhoneBookListSearch: StepFunction = - ({ phoneBookList, searchText, matchedResult }) => { - const searchPhoneBook = jest.fn(defaultSearchPhoneBook); - wrapper = setup({ - searchPhoneBook, - transferPhoneBook: phoneBookList.map(({ name, number }) => ({ - name, - destination: number, - countryId: 'USA', - })), - }); - const eventObj = { target: { value: searchText } }; - getSearchInput().simulate('change', eventObj); - const phoneContacts = getPhoneContacts(); - if (Array.isArray(matchedResult)) { - expect(phoneContacts).toHaveLength(matchedResult.length); - const resultItems = phoneContacts.map((el) => - el.find('.phoneBookDest').text(), - ); - expect(resultItems).toStrictEqual( - matchedResult.map((i) => - format({ - phoneNumber: phoneBookList[i].number, - countryCode: 'US', - }), - ), - ); - } else { - expect(phoneContacts).toHaveLength(0); - expect(wrapper.find('[data-sign="searchResult"]').text()).toBe( - `No result found for "${searchText}"`, - ); - } - }; +export const UTPhoneBookListSearch: StepFunction< + UTPhoneBookListSearchProps +> = ({ phoneBookList, searchText, matchedResult }) => { + const searchPhoneBook = jest.fn(defaultSearchPhoneBook); + wrapper = setup({ + searchPhoneBook, + transferPhoneBook: phoneBookList.map(({ name, number }) => ({ + name, + destination: number, + countryId: 'USA', + })), + }); + const eventObj = { target: { value: searchText } }; + getSearchInput().simulate('change', eventObj); + const phoneContacts = getPhoneContacts(); + if (Array.isArray(matchedResult)) { + expect(phoneContacts).toHaveLength(matchedResult.length); + const resultItems = phoneContacts.map((el) => + el.find('.phoneBookDest').text(), + ); + expect(resultItems).toStrictEqual( + matchedResult.map((i) => + format({ + phoneNumber: phoneBookList[i].number, + countryCode: 'US', + }), + ), + ); + } else { + expect(phoneContacts).toHaveLength(0); + expect(wrapper.find('[data-sign="searchResult"]').text()).toBe( + `No result found for "${searchText}"`, + ); + } +}; interface UTCheckInternalPanelRenderProps { internalOptions: string; } -export const UTCheckPhoneBookPanelRender: StepFunction = - async ({ internalOptions }) => { - const wrapper = setup({}); - const dataSign = { - 'Search bar': 'searchBar', - 'Phone Book recipient list with numbers': 'searchResult', - }; - expect( - wrapper.find(`[data-sign="${dataSign[internalOptions]}"]`), - ).not.toBeUndefined(); +export const UTCheckPhoneBookPanelRender: StepFunction< + UTCheckInternalPanelRenderProps +> = async ({ internalOptions }) => { + const wrapper = setup({}); + const dataSign = { + 'Search bar': 'searchBar', + 'Phone Book recipient list with numbers': 'searchResult', }; + expect( + wrapper.find(`[data-sign="${dataSign[internalOptions]}"]`), + ).not.toBeUndefined(); +}; diff --git a/packages/engage-voice-widget/components/TransferCallRecipient/PhoneBookPanel/i18n/en-US.ts b/packages/engage-voice-widget/components/TransferCallRecipient/PhoneBookPanel/i18n/en-US.ts index c66c560c9b..fad55a35b7 100644 --- a/packages/engage-voice-widget/components/TransferCallRecipient/PhoneBookPanel/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/TransferCallRecipient/PhoneBookPanel/i18n/en-US.ts @@ -1,3 +1,3 @@ export default { phoneBookTransfer: 'Phone book transfer', -}; +} as const; diff --git a/packages/engage-voice-widget/components/TransferCallRecipient/PhoneBookPanel/i18n/index.ts b/packages/engage-voice-widget/components/TransferCallRecipient/PhoneBookPanel/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/TransferCallRecipient/PhoneBookPanel/i18n/index.ts +++ b/packages/engage-voice-widget/components/TransferCallRecipient/PhoneBookPanel/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/components/TransferCallRecipient/PhoneBookPanel/styles.scss b/packages/engage-voice-widget/components/TransferCallRecipient/PhoneBookPanel/styles.scss index e7f2c883f2..c0c46d1d6a 100644 --- a/packages/engage-voice-widget/components/TransferCallRecipient/PhoneBookPanel/styles.scss +++ b/packages/engage-voice-widget/components/TransferCallRecipient/PhoneBookPanel/styles.scss @@ -15,4 +15,3 @@ line-height: 20px; display: flex; } - diff --git a/packages/engage-voice-widget/components/TransferCallRecipient/i18n/en-US.ts b/packages/engage-voice-widget/components/TransferCallRecipient/i18n/en-US.ts index c4bc67377d..f4367d53dc 100644 --- a/packages/engage-voice-widget/components/TransferCallRecipient/i18n/en-US.ts +++ b/packages/engage-voice-widget/components/TransferCallRecipient/i18n/en-US.ts @@ -1,3 +1,3 @@ export default { search: 'Search ', -}; +} as const; diff --git a/packages/engage-voice-widget/components/TransferCallRecipient/i18n/index.ts b/packages/engage-voice-widget/components/TransferCallRecipient/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/components/TransferCallRecipient/i18n/index.ts +++ b/packages/engage-voice-widget/components/TransferCallRecipient/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/containers/AppView/AppView.tsx b/packages/engage-voice-widget/containers/AppView/AppView.tsx index 99cfaf24aa..39a6dde279 100644 --- a/packages/engage-voice-widget/containers/AppView/AppView.tsx +++ b/packages/engage-voice-widget/containers/AppView/AppView.tsx @@ -1,12 +1,11 @@ +import { EnvironmentPanel } from '@ringcentral-integration/widgets/components/EnvironmentPanel'; +import withPhone from '@ringcentral-integration/widgets/lib/withPhone'; import type { FunctionComponent } from 'react'; import React from 'react'; - import { connect } from 'react-redux'; -import { EnvironmentPanel } from '@ringcentral-integration/widgets/components/EnvironmentPanel'; -import withPhone from '@ringcentral-integration/widgets/lib/withPhone'; - import type { EvPhone } from '../../interfaces'; + import styles from './styles.scss'; interface AppViewProps { diff --git a/packages/engage-voice-widget/enums/agentTypes.ts b/packages/engage-voice-widget/enums/agentTypes.ts index a96439069d..cf312b0a9d 100644 --- a/packages/engage-voice-widget/enums/agentTypes.ts +++ b/packages/engage-voice-widget/enums/agentTypes.ts @@ -4,4 +4,4 @@ export const AGENT_TYPES = { } as const; export type OriginAgentTypesType = keyof typeof AGENT_TYPES; -export type AgentTypesType = typeof AGENT_TYPES[OriginAgentTypesType]; +export type AgentTypesType = (typeof AGENT_TYPES)[OriginAgentTypesType]; diff --git a/packages/engage-voice-widget/gulpfile.js b/packages/engage-voice-widget/gulpfile.js index 4b70eec3e8..c14b228db2 100644 --- a/packages/engage-voice-widget/gulpfile.js +++ b/packages/engage-voice-widget/gulpfile.js @@ -1,15 +1,14 @@ -import os from 'os'; -import path from 'path'; +import exportLocale from '@ringcentral-integration/locale-loader/lib/exportLocale'; +import importLocale from '@ringcentral-integration/locale-loader/lib/importLocale'; +import transformLoader from '@ringcentral-integration/locale-loader/lib/transformLoader'; +import localeSettings from '@ringcentral-integration/locale-settings'; import execa from 'execa'; import fs from 'fs-extra'; import gulp from 'gulp'; import babel from 'gulp-babel'; import sourcemaps from 'gulp-sourcemaps'; - -import exportLocale from '@ringcentral-integration/locale-loader/lib/exportLocale'; -import importLocale from '@ringcentral-integration/locale-loader/lib/importLocale'; -import transformLoader from '@ringcentral-integration/locale-loader/lib/transformLoader'; -import localeSettings from '@ringcentral-integration/locale-settings'; +import os from 'os'; +import path from 'path'; const BUILD_PATH = path.resolve(__dirname, '../../build/engage-voice-widgets'); const RELEASE_PATH = path.resolve( diff --git a/packages/engage-voice-widget/interfaces/EvActivityCallUI.interface.ts b/packages/engage-voice-widget/interfaces/EvActivityCallUI.interface.ts index da9bf2d0ff..6ebcba3638 100644 --- a/packages/engage-voice-widget/interfaces/EvActivityCallUI.interface.ts +++ b/packages/engage-voice-widget/interfaces/EvActivityCallUI.interface.ts @@ -8,6 +8,7 @@ import type { import type { EvSmallCallControlProps } from '../components/EvSmallCallControl'; import type { EvTransferType } from '../enums'; + import type { EvAgentScriptData, EvCallData, diff --git a/packages/engage-voice-widget/interfaces/EvAgentSessionUI.interface.ts b/packages/engage-voice-widget/interfaces/EvAgentSessionUI.interface.ts index 2eaf262da6..08d6462693 100644 --- a/packages/engage-voice-widget/interfaces/EvAgentSessionUI.interface.ts +++ b/packages/engage-voice-widget/interfaces/EvAgentSessionUI.interface.ts @@ -1,5 +1,6 @@ import type { LoginTypes } from '../enums'; import type { EvAgent, EvAvailableSkillProfile } from '../lib/EvClient'; + import type { AvailableQueue } from './SelectableQueue.interface'; export interface SkillProfile { diff --git a/packages/engage-voice-widget/interfaces/EvMainViewUI.interface.ts b/packages/engage-voice-widget/interfaces/EvMainViewUI.interface.ts index bd58755a0f..3d51170136 100644 --- a/packages/engage-voice-widget/interfaces/EvMainViewUI.interface.ts +++ b/packages/engage-voice-widget/interfaces/EvMainViewUI.interface.ts @@ -1,4 +1,5 @@ import type { EvAgentState, EvAvailableAgentState } from '../lib/EvClient'; + import type { EvOffhookState } from './EvSettingsUI.interface'; export type EvCustomAvailableAgentState = { diff --git a/packages/engage-voice-widget/lib/EvClient/EvClient.ts b/packages/engage-voice-widget/lib/EvClient/EvClient.ts index cb3026c59b..695aff8392 100644 --- a/packages/engage-voice-widget/lib/EvClient/EvClient.ts +++ b/packages/engage-voice-widget/lib/EvClient/EvClient.ts @@ -1,14 +1,14 @@ -import { EventEmitter } from 'events'; -import { waitUntilTo } from '@ringcentral-integration/commons/utils'; - -import { Module } from '@ringcentral-integration/commons/lib/di'; -import { action, RcModuleV2, state } from '@ringcentral-integration/core'; // eslint-disable-next-line import/no-unresolved import AgentLibrary from '@SDK'; +import { Module } from '@ringcentral-integration/commons/lib/di'; +import { waitUntilTo } from '@ringcentral-integration/commons/utils'; +import { action, RcModuleV2, state } from '@ringcentral-integration/core'; +import { EventEmitter } from 'events'; import { AGENT_TYPES, messageTypes } from '../../enums'; -import { _encodeSymbol } from '../constant'; import { EvTypeError } from '../EvTypeError'; +import { _encodeSymbol } from '../constant'; + import { evStatus } from './enums'; import { EvCallbackTypes } from './enums/callbackTypes'; import type { @@ -44,7 +44,7 @@ import type { RecordResponse, } from './interfaces'; -type ListenerType = typeof EvCallbackTypes['OPEN_SOCKET' | 'CLOSE_SOCKET']; +type ListenerType = (typeof EvCallbackTypes)['OPEN_SOCKET' | 'CLOSE_SOCKET']; type Listener< T extends keyof EvClientCallMapping, diff --git a/packages/engage-voice-widget/lib/EvClient/enums/evMessageTypes.ts b/packages/engage-voice-widget/lib/EvClient/enums/evMessageTypes.ts index 0b8045e4e0..0691d2a809 100644 --- a/packages/engage-voice-widget/lib/EvClient/enums/evMessageTypes.ts +++ b/packages/engage-voice-widget/lib/EvClient/enums/evMessageTypes.ts @@ -85,4 +85,5 @@ export const EvMessageTypes = { UPDATE_DIAL_DESTINATION: 'UPDATE_DIAL_DESTINATION', } as const; -export type EvMessageType = typeof EvMessageTypes[keyof typeof EvMessageTypes]; +export type EvMessageType = + (typeof EvMessageTypes)[keyof typeof EvMessageTypes]; diff --git a/packages/engage-voice-widget/lib/FormatPhoneNumber/i18n/en-US.ts b/packages/engage-voice-widget/lib/FormatPhoneNumber/i18n/en-US.ts index 2406b6387b..060ef929e2 100644 --- a/packages/engage-voice-widget/lib/FormatPhoneNumber/i18n/en-US.ts +++ b/packages/engage-voice-widget/lib/FormatPhoneNumber/i18n/en-US.ts @@ -1,3 +1,3 @@ export default { unknown: 'Unknown', -}; +} as const; diff --git a/packages/engage-voice-widget/lib/FormatPhoneNumber/i18n/index.ts b/packages/engage-voice-widget/lib/FormatPhoneNumber/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/lib/FormatPhoneNumber/i18n/index.ts +++ b/packages/engage-voice-widget/lib/FormatPhoneNumber/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/lib/callUniqueIdentifies.ts b/packages/engage-voice-widget/lib/callUniqueIdentifies.ts index 93262987c3..3239c6df78 100644 --- a/packages/engage-voice-widget/lib/callUniqueIdentifies.ts +++ b/packages/engage-voice-widget/lib/callUniqueIdentifies.ts @@ -1,4 +1,5 @@ import type { EvCallData } from '../interfaces/EvData.interface'; + import { contactMatchIdentifyEncode } from './contactMatchIdentify'; export const makeCallsUniqueIdentifies = (calls: EvCallData[]) => { diff --git a/packages/engage-voice-widget/lib/checkCountryCode.ts b/packages/engage-voice-widget/lib/checkCountryCode.ts index 3958a942c7..4e58ee77df 100644 --- a/packages/engage-voice-widget/lib/checkCountryCode.ts +++ b/packages/engage-voice-widget/lib/checkCountryCode.ts @@ -1,12 +1,12 @@ -import countries from 'i18n-iso-countries'; - import { isE164, parse, parseIncompletePhoneNumber, } from '@ringcentral-integration/phone-number'; +import countries from 'i18n-iso-countries'; import { messageTypes } from '../enums'; + import { EvTypeError } from './EvTypeError'; export const checkCountryCode = (input: string) => { diff --git a/packages/engage-voice-widget/lib/parseNumber.ts b/packages/engage-voice-widget/lib/parseNumber.ts index 3565032a71..0e544abb35 100644 --- a/packages/engage-voice-widget/lib/parseNumber.ts +++ b/packages/engage-voice-widget/lib/parseNumber.ts @@ -2,6 +2,7 @@ import { callErrors } from '@ringcentral-integration/commons/modules/Call'; import { parse } from '@ringcentral-integration/phone-number'; import { messageTypes } from '../enums'; + import { EvTypeError } from './EvTypeError'; export const parseNumber = (input: string) => { diff --git a/packages/engage-voice-widget/lib/tabLife.ts b/packages/engage-voice-widget/lib/tabLife.ts index 35fb53dd86..436f7466fa 100644 --- a/packages/engage-voice-widget/lib/tabLife.ts +++ b/packages/engage-voice-widget/lib/tabLife.ts @@ -1,6 +1,5 @@ -import { EventEmitter } from 'events'; - import { sleep, waitUntilTo } from '@ringcentral-integration/commons/utils'; +import { EventEmitter } from 'events'; const TAB_CHANNEL_KEY = 'channel$$'; diff --git a/packages/engage-voice-widget/modules/EvActiveCallControl/EvActiveCallControl.ts b/packages/engage-voice-widget/modules/EvActiveCallControl/EvActiveCallControl.ts index 5c168a1d6b..da4bee0b14 100644 --- a/packages/engage-voice-widget/modules/EvActiveCallControl/EvActiveCallControl.ts +++ b/packages/engage-voice-widget/modules/EvActiveCallControl/EvActiveCallControl.ts @@ -11,6 +11,7 @@ import type { EvClientHandUpParams, EvClientHoldSessionParams, } from '../../lib/EvClient'; + import type { ActiveCallControl, Deps } from './EvActiveCallControl.interface'; @Module({ diff --git a/packages/engage-voice-widget/modules/EvActiveCallListUI/EvActiveCallListUI.ts b/packages/engage-voice-widget/modules/EvActiveCallListUI/EvActiveCallListUI.ts index 7044a8775a..959b1ef808 100644 --- a/packages/engage-voice-widget/modules/EvActiveCallListUI/EvActiveCallListUI.ts +++ b/packages/engage-voice-widget/modules/EvActiveCallListUI/EvActiveCallListUI.ts @@ -6,6 +6,7 @@ import type { EvActiveCallListUIProps, } from '../../interfaces/EvActiveCallListUI.interface'; import type { EvCallData } from '../../interfaces/EvData.interface'; + import type { ActiveCallListUI, Deps } from './EvActiveCallListUI.interface'; @Module({ diff --git a/packages/engage-voice-widget/modules/EvActivityCallUI/EvActivityCallUI.ts b/packages/engage-voice-widget/modules/EvActivityCallUI/EvActivityCallUI.ts index 84790b8885..8354a3a7d4 100644 --- a/packages/engage-voice-widget/modules/EvActivityCallUI/EvActivityCallUI.ts +++ b/packages/engage-voice-widget/modules/EvActivityCallUI/EvActivityCallUI.ts @@ -1,6 +1,3 @@ -import { keys } from 'ramda'; -import type { Unsubscribe } from 'redux'; - import { Module } from '@ringcentral-integration/commons/lib/di'; import { action, @@ -11,6 +8,8 @@ import { watch, } from '@ringcentral-integration/core'; import type { CallLogPanelProps } from '@ringcentral-integration/widgets/components/CallLogPanel'; +import { keys } from 'ramda'; +import type { Unsubscribe } from 'redux'; import type { EvTransferType, MessageTypes } from '../../enums'; import { @@ -37,6 +36,7 @@ import type { EvIvrData, } from '../../interfaces/EvData.interface'; import type { EvBaggage } from '../../lib/EvClient'; + import type { ActivityCallUI, Deps } from './EvActivityCallUI.interface'; import i18n from './i18n'; diff --git a/packages/engage-voice-widget/modules/EvActivityCallUI/i18n/en-US.ts b/packages/engage-voice-widget/modules/EvActivityCallUI/i18n/en-US.ts index 08c4aabb66..efb73d2993 100644 --- a/packages/engage-voice-widget/modules/EvActivityCallUI/i18n/en-US.ts +++ b/packages/engage-voice-widget/modules/EvActivityCallUI/i18n/en-US.ts @@ -4,4 +4,4 @@ export default { dispositionError: 'Please choose a disposition before submitting.', [dropDownOptions.None]: 'None', pleaseSelect: 'Please select', -}; +} as const; diff --git a/packages/engage-voice-widget/modules/EvActivityCallUI/i18n/index.ts b/packages/engage-voice-widget/modules/EvActivityCallUI/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/modules/EvActivityCallUI/i18n/index.ts +++ b/packages/engage-voice-widget/modules/EvActivityCallUI/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/modules/EvAgentScript/EvAgentScript.ts b/packages/engage-voice-widget/modules/EvAgentScript/EvAgentScript.ts index 26864ad290..1518e5d0fc 100644 --- a/packages/engage-voice-widget/modules/EvAgentScript/EvAgentScript.ts +++ b/packages/engage-voice-widget/modules/EvAgentScript/EvAgentScript.ts @@ -1,9 +1,6 @@ -import { EventEmitter } from 'events'; -import { clone, reduce } from 'ramda'; - +import { SingleTabBroadcastChannel } from '@ringcentral-integration/commons/lib/SingleTabBroadcastChannel'; import { debounce } from '@ringcentral-integration/commons/lib/debounce-throttle'; import { Module } from '@ringcentral-integration/commons/lib/di'; -import { SingleTabBroadcastChannel } from '@ringcentral-integration/commons/lib/SingleTabBroadcastChannel'; import { action, RcModuleV2, @@ -11,6 +8,8 @@ import { storage, watch, } from '@ringcentral-integration/core'; +import { EventEmitter } from 'events'; +import { clone, reduce } from 'ramda'; import { agentScriptEvents, @@ -27,6 +26,7 @@ import type { EvCallDispositionItem, EvScriptResponseJSON, } from '../../lib/EvClient'; + import type { AgentScript, Deps, diff --git a/packages/engage-voice-widget/modules/EvAgentSession/EvAgentSession.ts b/packages/engage-voice-widget/modules/EvAgentSession/EvAgentSession.ts index 561bfd0ef5..484a1eb44f 100644 --- a/packages/engage-voice-widget/modules/EvAgentSession/EvAgentSession.ts +++ b/packages/engage-voice-widget/modules/EvAgentSession/EvAgentSession.ts @@ -1,6 +1,3 @@ -import { EventEmitter } from 'events'; -import { equals } from 'ramda'; - import { Module } from '@ringcentral-integration/commons/lib/di'; import { sleep } from '@ringcentral-integration/commons/utils'; import { @@ -13,6 +10,8 @@ import { watch, } from '@ringcentral-integration/core'; import { format, parse } from '@ringcentral-integration/phone-number'; +import { EventEmitter } from 'events'; +import { equals } from 'ramda'; import type { LoginTypes } from '../../enums'; import { @@ -32,6 +31,7 @@ import type { import { evStatus } from '../../lib/EvClient/enums'; import { TabLife } from '../../lib/tabLife'; import { trackEvents } from '../../lib/trackEvents'; + import type { AgentSession, Deps, FormGroup } from './EvAgentSession.interface'; import i18n from './i18n'; import { tabManagerEnabled } from './tabManagerEnabled.decorator'; diff --git a/packages/engage-voice-widget/modules/EvAgentSession/i18n/en-US.ts b/packages/engage-voice-widget/modules/EvAgentSession/i18n/en-US.ts index ee1bfb824f..163d1569ef 100644 --- a/packages/engage-voice-widget/modules/EvAgentSession/i18n/en-US.ts +++ b/packages/engage-voice-widget/modules/EvAgentSession/i18n/en-US.ts @@ -10,4 +10,4 @@ export default { multipleLoginsTitle: 'Multiple logins', multipleLoginsContent: 'This username is still logged in. Press continue to end the existing session and start a new one.', -}; +} as const; diff --git a/packages/engage-voice-widget/modules/EvAgentSession/i18n/index.ts b/packages/engage-voice-widget/modules/EvAgentSession/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/modules/EvAgentSession/i18n/index.ts +++ b/packages/engage-voice-widget/modules/EvAgentSession/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/modules/EvAgentSessionUI/EvAgentSessionUI.ts b/packages/engage-voice-widget/modules/EvAgentSessionUI/EvAgentSessionUI.ts index fd0326a69d..80b1987fbd 100644 --- a/packages/engage-voice-widget/modules/EvAgentSessionUI/EvAgentSessionUI.ts +++ b/packages/engage-voice-widget/modules/EvAgentSessionUI/EvAgentSessionUI.ts @@ -16,6 +16,7 @@ import type { } from '../../interfaces/EvAgentSessionUI.interface'; import type { AvailableQueue } from '../../interfaces/SelectableQueue.interface'; import { sortByName } from '../../lib/sortByName'; + import type { Deps, SessionConfigUI } from './EvAgentSessionUI.interface'; import i18n from './i18n'; diff --git a/packages/engage-voice-widget/modules/EvAgentSessionUI/i18n/en-US.ts b/packages/engage-voice-widget/modules/EvAgentSessionUI/i18n/en-US.ts index 1c39c110cf..64b5a2ca83 100644 --- a/packages/engage-voice-widget/modules/EvAgentSessionUI/i18n/en-US.ts +++ b/packages/engage-voice-widget/modules/EvAgentSessionUI/i18n/en-US.ts @@ -12,4 +12,4 @@ export default { saveEditionModalContent: 'Your changes have not been saved.', save: 'Save', cancel: 'Cancel', -}; +} as const; diff --git a/packages/engage-voice-widget/modules/EvAgentSessionUI/i18n/index.ts b/packages/engage-voice-widget/modules/EvAgentSessionUI/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/modules/EvAgentSessionUI/i18n/index.ts +++ b/packages/engage-voice-widget/modules/EvAgentSessionUI/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/modules/EvAuth/EvAuth.ts b/packages/engage-voice-widget/modules/EvAuth/EvAuth.ts index d78ba71807..d1550de739 100644 --- a/packages/engage-voice-widget/modules/EvAuth/EvAuth.ts +++ b/packages/engage-voice-widget/modules/EvAuth/EvAuth.ts @@ -1,6 +1,3 @@ -import { EventEmitter } from 'events'; -import type { Unsubscribe } from 'redux'; - import { Module } from '@ringcentral-integration/commons/lib/di'; import { sleep } from '@ringcentral-integration/commons/utils'; import { @@ -12,6 +9,8 @@ import { track, } from '@ringcentral-integration/core'; import format from '@ringcentral-integration/phone-number/lib/format'; +import { EventEmitter } from 'events'; +import type { Unsubscribe } from 'redux'; import { loginStatus, messageTypes, tabManagerEvents } from '../../enums'; import type { EvAgentConfig, EvAgentData } from '../../lib/EvClient'; @@ -19,6 +18,7 @@ import { EvCallbackTypes } from '../../lib/EvClient/enums'; import { EvTypeError } from '../../lib/EvTypeError'; import { sortByName } from '../../lib/sortByName'; import { trackEvents } from '../../lib/trackEvents'; + import type { Auth, AuthenticateWithTokenType, diff --git a/packages/engage-voice-widget/modules/EvAuth/i18n/en-US.ts b/packages/engage-voice-widget/modules/EvAuth/i18n/en-US.ts index f3b94c312f..a7004f72de 100644 --- a/packages/engage-voice-widget/modules/EvAuth/i18n/en-US.ts +++ b/packages/engage-voice-widget/modules/EvAuth/i18n/en-US.ts @@ -1,4 +1,4 @@ export default { default: 'Default', us: 'North America Domestic', -}; +} as const; diff --git a/packages/engage-voice-widget/modules/EvAuth/i18n/index.ts b/packages/engage-voice-widget/modules/EvAuth/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/modules/EvAuth/i18n/index.ts +++ b/packages/engage-voice-widget/modules/EvAuth/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/modules/EvCall/EvCall.ts b/packages/engage-voice-widget/modules/EvCall/EvCall.ts index 5267afd18c..53dbc5cfe9 100644 --- a/packages/engage-voice-widget/modules/EvCall/EvCall.ts +++ b/packages/engage-voice-widget/modules/EvCall/EvCall.ts @@ -13,14 +13,15 @@ import { import { messageTypes } from '../../enums'; import type { DialoutStatusesType } from '../../enums/dialoutStatus'; import { dialoutStatuses } from '../../enums/dialoutStatus'; -import { checkCountryCode } from '../../lib/checkCountryCode'; import type { EvClientManualOutdialParams, EvOffhookInitResponse, } from '../../lib/EvClient'; import { EvCallbackTypes } from '../../lib/EvClient/enums/callbackTypes'; +import { checkCountryCode } from '../../lib/checkCountryCode'; import { parseNumber } from '../../lib/parseNumber'; import { trackEvents } from '../../lib/trackEvents'; + import type { Call, Deps, State } from './EvCall.interface'; const DEFAULT_OUTBOUND_SETTING = { diff --git a/packages/engage-voice-widget/modules/EvCallDataSource/EvCallDataSource.ts b/packages/engage-voice-widget/modules/EvCallDataSource/EvCallDataSource.ts index ba02d29416..d055b44713 100644 --- a/packages/engage-voice-widget/modules/EvCallDataSource/EvCallDataSource.ts +++ b/packages/engage-voice-widget/modules/EvCallDataSource/EvCallDataSource.ts @@ -1,6 +1,3 @@ -import { EventEmitter } from 'events'; -import dayjs from 'dayjs'; - import { Module } from '@ringcentral-integration/commons/lib/di'; import { action, @@ -9,6 +6,8 @@ import { storage, } from '@ringcentral-integration/core'; import type { Mapping } from '@ringcentral-integration/widgets/typings'; +import dayjs from 'dayjs'; +import { EventEmitter } from 'events'; import { callStatus } from '../../enums'; import type { @@ -22,6 +21,7 @@ import type { EvEndedCall, EvHoldResponse, } from '../../lib/EvClient'; + import type { CallDataSource, Deps } from './EvCallDataSource.interface'; import { getTimeStamp } from './helper'; diff --git a/packages/engage-voice-widget/modules/EvCallDataSource/helper.ts b/packages/engage-voice-widget/modules/EvCallDataSource/helper.ts index 3725104a47..de78435284 100644 --- a/packages/engage-voice-widget/modules/EvCallDataSource/helper.ts +++ b/packages/engage-voice-widget/modules/EvCallDataSource/helper.ts @@ -1,6 +1,6 @@ import dayjs from 'dayjs'; -import utc from 'dayjs/plugin/utc'; import timezone from 'dayjs/plugin/timezone'; +import utc from 'dayjs/plugin/utc'; dayjs.extend(utc); dayjs.extend(timezone); diff --git a/packages/engage-voice-widget/modules/EvCallHistory/EvCallHistory.ts b/packages/engage-voice-widget/modules/EvCallHistory/EvCallHistory.ts index 4a48a0d7b0..d23b585eb6 100644 --- a/packages/engage-voice-widget/modules/EvCallHistory/EvCallHistory.ts +++ b/packages/engage-voice-widget/modules/EvCallHistory/EvCallHistory.ts @@ -3,10 +3,11 @@ import { Module } from '@ringcentral-integration/commons/lib/di'; import { computed, RcModuleV2, watch } from '@ringcentral-integration/core'; import { directTransferNotificationTypes } from '../../enums/directTransferNotificationTypes'; -import { makeCallsUniqueIdentifies } from '../../lib/callUniqueIdentifies'; -import { contactMatchIdentifyEncode } from '../../lib/contactMatchIdentify'; import { EvCallbackTypes } from '../../lib/EvClient/enums/callbackTypes'; import { formatPhoneNumber } from '../../lib/FormatPhoneNumber'; +import { makeCallsUniqueIdentifies } from '../../lib/callUniqueIdentifies'; +import { contactMatchIdentifyEncode } from '../../lib/contactMatchIdentify'; + import type { CallHistory, Deps } from './EvCallHistory.interface'; @Module({ diff --git a/packages/engage-voice-widget/modules/EvCallMonitor/EvCallMonitor.ts b/packages/engage-voice-widget/modules/EvCallMonitor/EvCallMonitor.ts index 556f75a462..d337e5aa01 100644 --- a/packages/engage-voice-widget/modules/EvCallMonitor/EvCallMonitor.ts +++ b/packages/engage-voice-widget/modules/EvCallMonitor/EvCallMonitor.ts @@ -4,9 +4,10 @@ import type { Mapping } from '@ringcentral-integration/widgets/typings'; import { callStatus } from '../../enums'; import type { EvCallData } from '../../interfaces/EvData.interface'; +import type { EvAddSessionNotification } from '../../lib/EvClient/interfaces'; import { makeCallsUniqueIdentifies } from '../../lib/callUniqueIdentifies'; import { contactMatchIdentifyEncode } from '../../lib/contactMatchIdentify'; -import type { EvAddSessionNotification } from '../../lib/EvClient/interfaces'; + import type { CallMonitor, Deps } from './EvCallMonitor.interface'; @Module({ diff --git a/packages/engage-voice-widget/modules/EvChooseAccountUI/EvChooseAccountUI.ts b/packages/engage-voice-widget/modules/EvChooseAccountUI/EvChooseAccountUI.ts index e15c4435ba..459c8a1c92 100644 --- a/packages/engage-voice-widget/modules/EvChooseAccountUI/EvChooseAccountUI.ts +++ b/packages/engage-voice-widget/modules/EvChooseAccountUI/EvChooseAccountUI.ts @@ -5,6 +5,7 @@ import type { EvChooseAccountUIFunctions, EvChooseAccountUIProps, } from '../../interfaces/EvChooseAccountUI.interface'; + import type { ChooseAccountUI, Deps } from './EvChooseAccountUI.interface'; @Module({ diff --git a/packages/engage-voice-widget/modules/EvDialerUI/EvDialerUI.ts b/packages/engage-voice-widget/modules/EvDialerUI/EvDialerUI.ts index 23bd452e8c..45c6504b7a 100644 --- a/packages/engage-voice-widget/modules/EvDialerUI/EvDialerUI.ts +++ b/packages/engage-voice-widget/modules/EvDialerUI/EvDialerUI.ts @@ -13,6 +13,7 @@ import type { EvDialerUIFunctions, EvDialerUIProps, } from '../../interfaces/EvDialerUI.interface'; + import type { Deps, DialerUI } from './EvDialerUI.interface'; @Module({ diff --git a/packages/engage-voice-widget/modules/EvIntegratedSoftphone/EvIntegratedSoftphone.ts b/packages/engage-voice-widget/modules/EvIntegratedSoftphone/EvIntegratedSoftphone.ts index d2cbaf1c98..3156798545 100644 --- a/packages/engage-voice-widget/modules/EvIntegratedSoftphone/EvIntegratedSoftphone.ts +++ b/packages/engage-voice-widget/modules/EvIntegratedSoftphone/EvIntegratedSoftphone.ts @@ -1,5 +1,3 @@ -import { EventEmitter } from 'events'; - import { Module } from '@ringcentral-integration/commons/lib/di'; import { sleep, waitUntilTo } from '@ringcentral-integration/commons/utils'; import { @@ -10,6 +8,7 @@ import { } from '@ringcentral-integration/core'; import { format } from '@ringcentral-integration/utils'; import type { CustomRenderer } from '@ringcentral-integration/widgets/modules/ModalUI/ModalUI.interface'; +import { EventEmitter } from 'events'; import { dialoutStatuses, @@ -18,14 +17,15 @@ import { } from '../../enums'; import type { EvSipRingingData } from '../../lib/EvClient'; import { EvCallbackTypes } from '../../lib/EvClient/enums'; -import { audios } from './audios'; + import type { Deps, IntegratedSoftphone, ShowRingingModalProps, } from './EvIntegratedSoftphone.interface'; -import i18n from './i18n'; import { getModalText } from './IncomingModalText'; +import { audios } from './audios'; +import i18n from './i18n'; import { runInActivityWebRTCTab } from './runInActivityWebRTCTab.decorator'; const SECOND = 1000; diff --git a/packages/engage-voice-widget/modules/EvIntegratedSoftphone/IncomingModalText.tsx b/packages/engage-voice-widget/modules/EvIntegratedSoftphone/IncomingModalText.tsx index 9775836f1f..d338002249 100644 --- a/packages/engage-voice-widget/modules/EvIntegratedSoftphone/IncomingModalText.tsx +++ b/packages/engage-voice-widget/modules/EvIntegratedSoftphone/IncomingModalText.tsx @@ -1,7 +1,6 @@ +import clsx from 'clsx'; import React from 'react'; -import classNames from 'classnames'; - import styles from './styles.scss'; type ModalTextProps = { @@ -22,7 +21,7 @@ export const getModalText = ({ return ( <>

{incomingText} diff --git a/packages/engage-voice-widget/modules/EvIntegratedSoftphone/i18n/en-US.ts b/packages/engage-voice-widget/modules/EvIntegratedSoftphone/i18n/en-US.ts index 75e2c977d3..cf65f3c335 100644 --- a/packages/engage-voice-widget/modules/EvIntegratedSoftphone/i18n/en-US.ts +++ b/packages/engage-voice-widget/modules/EvIntegratedSoftphone/i18n/en-US.ts @@ -6,4 +6,4 @@ export default { inviteModalAnswer: 'Answer', inviteModalReject: 'Reject', activeCallTip: 'To complete answering the call, click continue', -}; +} as const; diff --git a/packages/engage-voice-widget/modules/EvIntegratedSoftphone/i18n/index.ts b/packages/engage-voice-widget/modules/EvIntegratedSoftphone/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/modules/EvIntegratedSoftphone/i18n/index.ts +++ b/packages/engage-voice-widget/modules/EvIntegratedSoftphone/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/modules/EvIntegratedSoftphone/styles.scss b/packages/engage-voice-widget/modules/EvIntegratedSoftphone/styles.scss index 21c243e6e6..5722b9dcbb 100644 --- a/packages/engage-voice-widget/modules/EvIntegratedSoftphone/styles.scss +++ b/packages/engage-voice-widget/modules/EvIntegratedSoftphone/styles.scss @@ -11,6 +11,6 @@ } } -.incomingText{ +.incomingText { margin-bottom: 8px; } diff --git a/packages/engage-voice-widget/modules/EvManualDialSettingsUI/EvManualDialSettingsUI.interface.ts b/packages/engage-voice-widget/modules/EvManualDialSettingsUI/EvManualDialSettingsUI.interface.ts index e1ebe8cf1e..497d223c8b 100644 --- a/packages/engage-voice-widget/modules/EvManualDialSettingsUI/EvManualDialSettingsUI.interface.ts +++ b/packages/engage-voice-widget/modules/EvManualDialSettingsUI/EvManualDialSettingsUI.interface.ts @@ -1,7 +1,6 @@ -import type { FunctionComponent } from 'react'; - import type { Locale } from '@ringcentral-integration/commons/modules/Locale'; import type RouterInteraction from '@ringcentral-integration/widgets/modules/RouterInteraction'; +import type { FunctionComponent } from 'react'; import type { EvAuth } from '../EvAuth'; import type { EvCall } from '../EvCall'; diff --git a/packages/engage-voice-widget/modules/EvManualDialSettingsUI/EvManualDialSettingsUI.ts b/packages/engage-voice-widget/modules/EvManualDialSettingsUI/EvManualDialSettingsUI.ts index caa465f27d..6f0f25e5d3 100644 --- a/packages/engage-voice-widget/modules/EvManualDialSettingsUI/EvManualDialSettingsUI.ts +++ b/packages/engage-voice-widget/modules/EvManualDialSettingsUI/EvManualDialSettingsUI.ts @@ -11,6 +11,7 @@ import type { EvAvailableQueue, EvCallerId, } from '../../lib/EvClient'; + import type { Deps, EvManualDialSettingsRenderProps, diff --git a/packages/engage-voice-widget/modules/EvManualDialSettingsUI/i18n/en-US.ts b/packages/engage-voice-widget/modules/EvManualDialSettingsUI/i18n/en-US.ts index 1aa6e3f961..c4f77c242d 100644 --- a/packages/engage-voice-widget/modules/EvManualDialSettingsUI/i18n/en-US.ts +++ b/packages/engage-voice-widget/modules/EvManualDialSettingsUI/i18n/en-US.ts @@ -6,4 +6,4 @@ export default { country: 'Country', ringTime: 'Ring time (seconds)', us: 'United States', -}; +} as const; diff --git a/packages/engage-voice-widget/modules/EvManualDialSettingsUI/i18n/index.ts b/packages/engage-voice-widget/modules/EvManualDialSettingsUI/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/modules/EvManualDialSettingsUI/i18n/index.ts +++ b/packages/engage-voice-widget/modules/EvManualDialSettingsUI/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/modules/EvPresence/EvPresence.ts b/packages/engage-voice-widget/modules/EvPresence/EvPresence.ts index d324cc97f4..eb934822d7 100644 --- a/packages/engage-voice-widget/modules/EvPresence/EvPresence.ts +++ b/packages/engage-voice-widget/modules/EvPresence/EvPresence.ts @@ -1,5 +1,3 @@ -import { EventEmitter } from 'events'; - import { Module } from '@ringcentral-integration/commons/lib/di'; import { action, @@ -9,6 +7,7 @@ import { storage, track, } from '@ringcentral-integration/core'; +import { EventEmitter } from 'events'; import type { DialoutStatusesType } from '../../enums'; import { dialoutStatuses, messageTypes } from '../../enums'; @@ -25,6 +24,7 @@ import type { EvOffhookTermResponse, } from '../../lib/EvClient/interfaces'; import { trackEvents } from '../../lib/trackEvents'; + import type { Deps, Presence } from './EvPresence.interface'; @Module({ diff --git a/packages/engage-voice-widget/modules/EvRequeueCall/EvRequeueCall.ts b/packages/engage-voice-widget/modules/EvRequeueCall/EvRequeueCall.ts index 8e5749b667..e43925f6ce 100644 --- a/packages/engage-voice-widget/modules/EvRequeueCall/EvRequeueCall.ts +++ b/packages/engage-voice-widget/modules/EvRequeueCall/EvRequeueCall.ts @@ -10,6 +10,7 @@ import { import { requeueEvents } from '../../enums'; import type { EvCallData } from '../../interfaces/EvData.interface'; import { EvTypeError } from '../../lib/EvTypeError'; + import type { Deps, RequeueCall } from './EvRequeueCall.interface'; type EvRequeueCallStatus = Partial< diff --git a/packages/engage-voice-widget/modules/EvSettingsUI/EvSettingsUI.ts b/packages/engage-voice-widget/modules/EvSettingsUI/EvSettingsUI.ts index d662040c78..f7e00e08fe 100644 --- a/packages/engage-voice-widget/modules/EvSettingsUI/EvSettingsUI.ts +++ b/packages/engage-voice-widget/modules/EvSettingsUI/EvSettingsUI.ts @@ -1,7 +1,6 @@ -import dayjs from 'dayjs'; - import { Module } from '@ringcentral-integration/commons/lib/di'; import { computed, RcUIModuleV2 } from '@ringcentral-integration/core'; +import dayjs from 'dayjs'; import type { EvSettingsUIFunctions, @@ -9,6 +8,7 @@ import type { SessionInfo, } from '../../interfaces/EvSettingsUI.interface'; import { formatPhoneNumber } from '../../lib/FormatPhoneNumber'; + import type { Deps, SettingsUI } from './EvSettingsUI.interface'; import i18n from './i18n'; diff --git a/packages/engage-voice-widget/modules/EvSettingsUI/i18n/en-US.ts b/packages/engage-voice-widget/modules/EvSettingsUI/i18n/en-US.ts index d19e032ea0..095a616dd9 100644 --- a/packages/engage-voice-widget/modules/EvSettingsUI/i18n/en-US.ts +++ b/packages/engage-voice-widget/modules/EvSettingsUI/i18n/en-US.ts @@ -8,4 +8,4 @@ export default { ringCentralPhone: 'RingCentral Phone', externalPhone: 'External Phone', unknown: 'Unknown', -}; +} as const; diff --git a/packages/engage-voice-widget/modules/EvSettingsUI/i18n/index.ts b/packages/engage-voice-widget/modules/EvSettingsUI/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/modules/EvSettingsUI/i18n/index.ts +++ b/packages/engage-voice-widget/modules/EvSettingsUI/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/modules/EvStorage/EvStorage.ts b/packages/engage-voice-widget/modules/EvStorage/EvStorage.ts index c3294c9313..82aa6e9722 100644 --- a/packages/engage-voice-widget/modules/EvStorage/EvStorage.ts +++ b/packages/engage-voice-widget/modules/EvStorage/EvStorage.ts @@ -3,6 +3,7 @@ import { loginStatus } from '@ringcentral-integration/commons/modules/Auth'; import { Storage } from '@ringcentral-integration/commons/modules/Storage'; import { loginStatus as evLoginStatus } from '../../enums/loginStatus'; + import type { Deps } from './EvStorage.interface'; @Module({ diff --git a/packages/engage-voice-widget/modules/EvSubscription/EvSubscription.ts b/packages/engage-voice-widget/modules/EvSubscription/EvSubscription.ts index f59a547fd2..683df9e314 100644 --- a/packages/engage-voice-widget/modules/EvSubscription/EvSubscription.ts +++ b/packages/engage-voice-widget/modules/EvSubscription/EvSubscription.ts @@ -1,12 +1,12 @@ -import { EventEmitter } from 'events'; - import { Module } from '@ringcentral-integration/commons/lib/di'; import { RcModuleV2 } from '@ringcentral-integration/core'; +import { EventEmitter } from 'events'; import type { EvClientCallBackValueType, EvClientCallMapping, } from '../../lib/EvClient/interfaces'; + import type { Deps, Subscription } from './EvSubscription.interface'; @Module({ diff --git a/packages/engage-voice-widget/modules/EvTabManager/EvTabManager.ts b/packages/engage-voice-widget/modules/EvTabManager/EvTabManager.ts index 4ab213387b..7902a7617e 100644 --- a/packages/engage-voice-widget/modules/EvTabManager/EvTabManager.ts +++ b/packages/engage-voice-widget/modules/EvTabManager/EvTabManager.ts @@ -1,8 +1,7 @@ -import { EventEmitter } from 'events'; - import { Module } from '@ringcentral-integration/commons/lib/di'; import { TabManager } from '@ringcentral-integration/commons/modules/TabManager'; import { action, globalStorage, state } from '@ringcentral-integration/core'; +import { EventEmitter } from 'events'; import { tabManagerEvents } from '../../enums'; diff --git a/packages/engage-voice-widget/modules/EvTransferCall/EvTransferCall.ts b/packages/engage-voice-widget/modules/EvTransferCall/EvTransferCall.ts index cf59a77cc2..69d5af970e 100644 --- a/packages/engage-voice-widget/modules/EvTransferCall/EvTransferCall.ts +++ b/packages/engage-voice-widget/modules/EvTransferCall/EvTransferCall.ts @@ -1,5 +1,3 @@ -import { alpha3ToAlpha2 } from 'i18n-iso-countries'; - import { Module } from '@ringcentral-integration/commons/lib/di'; import { callErrors } from '@ringcentral-integration/commons/modules/Call'; import { @@ -10,6 +8,7 @@ import { storage, } from '@ringcentral-integration/core'; import { format, formatTypes } from '@ringcentral-integration/phone-number'; +import { alpha3ToAlpha2 } from 'i18n-iso-countries'; import type { DirectTransferNotificationTypes, @@ -30,8 +29,6 @@ import { } from '../../enums'; import type { Handler } from '../../interfaces/Common.interface'; import type { EvTransferViewPhoneBookItem } from '../../interfaces/EvTransferCallUI.interface'; -import { AsyncEventEmitter } from '../../lib/asyncEventEmitter'; -import { checkCountryCode } from '../../lib/checkCountryCode'; import type { EvClientTransferParams, EvDirectAgentListItem, @@ -40,7 +37,10 @@ import type { } from '../../lib/EvClient'; import { EvCallbackTypes } from '../../lib/EvClient/enums/callbackTypes'; import { EvTypeError } from '../../lib/EvTypeError'; +import { AsyncEventEmitter } from '../../lib/asyncEventEmitter'; +import { checkCountryCode } from '../../lib/checkCountryCode'; import { parseNumber } from '../../lib/parseNumber'; + import type { Deps, EvTransferFailHandler, diff --git a/packages/engage-voice-widget/modules/EvTransferCall/i18n/en-US.ts b/packages/engage-voice-widget/modules/EvTransferCall/i18n/en-US.ts index 11cc60d718..4f41aa1948 100644 --- a/packages/engage-voice-widget/modules/EvTransferCall/i18n/en-US.ts +++ b/packages/engage-voice-widget/modules/EvTransferCall/i18n/en-US.ts @@ -13,4 +13,4 @@ export default { acceptIncomingTransfer: 'Accept Call', ignoreIncomingTransfer: 'Ignore', transferModalTitle: 'Call transfer failed', -}; +} as const; diff --git a/packages/engage-voice-widget/modules/EvTransferCall/i18n/index.ts b/packages/engage-voice-widget/modules/EvTransferCall/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/modules/EvTransferCall/i18n/index.ts +++ b/packages/engage-voice-widget/modules/EvTransferCall/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/modules/EvTransferCallUI/EvTransferCallUI.ts b/packages/engage-voice-widget/modules/EvTransferCallUI/EvTransferCallUI.ts index cac583888f..d0b5431498 100644 --- a/packages/engage-voice-widget/modules/EvTransferCallUI/EvTransferCallUI.ts +++ b/packages/engage-voice-widget/modules/EvTransferCallUI/EvTransferCallUI.ts @@ -11,6 +11,7 @@ import type { GoToRequeueGroupDetailPageParams, } from '../../interfaces/EvTransferCallUI.interface'; import type { EvDirectAgentListItem } from '../../lib/EvClient'; + import type { Deps, TransferCallUI } from './EvTransferCallUI.interface'; import i18n from './i18n'; import { getInternalTransferName } from './util'; diff --git a/packages/engage-voice-widget/modules/EvTransferCallUI/i18n/en-US.ts b/packages/engage-voice-widget/modules/EvTransferCallUI/i18n/en-US.ts index 220742be18..ff297b2bce 100644 --- a/packages/engage-voice-widget/modules/EvTransferCallUI/i18n/en-US.ts +++ b/packages/engage-voice-widget/modules/EvTransferCallUI/i18n/en-US.ts @@ -14,4 +14,4 @@ export default { [transferTypes.phoneBook]: 'Phone book transfer', [transferTypes.manualEntry]: 'Enter a number', [transferTypes.queue]: 'Queue transfer', -}; +} as const; diff --git a/packages/engage-voice-widget/modules/EvTransferCallUI/i18n/index.ts b/packages/engage-voice-widget/modules/EvTransferCallUI/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/engage-voice-widget/modules/EvTransferCallUI/i18n/index.ts +++ b/packages/engage-voice-widget/modules/EvTransferCallUI/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/engage-voice-widget/modules/EvWorkingState/EvWorkingState.ts b/packages/engage-voice-widget/modules/EvWorkingState/EvWorkingState.ts index 1506e381ee..58531560f8 100644 --- a/packages/engage-voice-widget/modules/EvWorkingState/EvWorkingState.ts +++ b/packages/engage-voice-widget/modules/EvWorkingState/EvWorkingState.ts @@ -16,6 +16,7 @@ import { } from '../../enums'; import type { EvAgentState, EvAvailableAgentState } from '../../lib/EvClient'; import { EvCallbackTypes } from '../../lib/EvClient/enums/callbackTypes'; + import type { Deps, State, WorkingState } from './EvWorkingState.interface'; const PendingDisposition: EvAvailableAgentState = { diff --git a/packages/engage-voice-widget/modules/MainViewUI/MainViewUI.ts b/packages/engage-voice-widget/modules/MainViewUI/MainViewUI.ts index 2c1dae9fe8..91f28d84b4 100644 --- a/packages/engage-voice-widget/modules/MainViewUI/MainViewUI.ts +++ b/packages/engage-voice-widget/modules/MainViewUI/MainViewUI.ts @@ -7,6 +7,7 @@ import type { EvMainViewUIProps, } from '../../interfaces/EvMainViewUI.interface'; import { getClockByTimestamp } from '../../lib/getClockByTimestamp'; + import type { Deps, MainView } from './MainViewUI.interface'; @Module({ diff --git a/packages/engage-voice-widget/package.json b/packages/engage-voice-widget/package.json index 1955611e25..2f9205e23e 100644 --- a/packages/engage-voice-widget/package.json +++ b/packages/engage-voice-widget/package.json @@ -1,6 +1,6 @@ { "name": "@ringcentral-integration/engage-voice-widgets", - "version": "0.14.0", + "version": "0.15.0", "description": "RingCentral Engage Voice Integration Widget Library", "homepage": "https://github.com/ringcentral/ringcentral-js-widgets/tree/master/packages/engage-voice-widgets#readme", "bugs": { @@ -32,7 +32,6 @@ "test": "yarn run-test" }, "dependencies": { - "classnames": "^2.2.6", "i18n-iso-countries": "^4.3.1", "prop-types": "^15.7.2", "ramda": "^0.28.0", @@ -53,9 +52,9 @@ "@ringcentral-integration/test-utils": "*", "@ringcentral-integration/utils": "*", "@ringcentral-integration/widgets": "*", - "@ringcentral/juno": "^2.35.2", - "@ringcentral/juno-icon": "^1.43.0", - "@testing-library/react": "^12.1.4", + "@ringcentral/juno": "^2.42.0", + "@ringcentral/juno-icon": "^1.76.0", + "@testing-library/react": "^12.1.5", "@wojtekmaj/enzyme-adapter-react-17": "^0.6.3", "dotenv": "^6.2.0", "enzyme": "^3.7.0", @@ -65,18 +64,18 @@ "gulp-babel": "^8.0.0", "gulp-istanbul": "^1.1.1", "gulp-sourcemaps": "^2.6.5", - "jest-html-reporters": "^3.0.8" + "jest-html-reporters": "^3.1.7" }, "peerDependencies": { - "@ringcentral-integration/commons": "^0.14.0", - "@ringcentral-integration/core": "^0.14.0", + "@ringcentral-integration/commons": "^0.15.0", + "@ringcentral-integration/core": "^0.15.0", "@ringcentral-integration/phone-number": "^1.0.4", - "@ringcentral-integration/utils": "^0.14.0", - "@ringcentral-integration/widgets": "^0.14.0", - "@ringcentral/juno": "^2.35.2", - "@ringcentral/juno-icon": "^1.42.0" + "@ringcentral-integration/utils": "^0.15.0", + "@ringcentral-integration/widgets": "^0.15.0", + "@ringcentral/juno": "^2.42.0", + "@ringcentral/juno-icon": "^1.76.0" }, - "ci": { - "@ringcentral-integration/engage-voice-widgets": "**" + "nx": { + "tags": ["scope:coverage-ignore"] } } diff --git a/packages/engage-voice-widget/test/steps/index.ts b/packages/engage-voice-widget/test/steps/index.ts index 49bbba8f17..96ef0f7f47 100644 --- a/packages/engage-voice-widget/test/steps/index.ts +++ b/packages/engage-voice-widget/test/steps/index.ts @@ -1,5 +1,6 @@ import type { StepFunction as BaseStepFunction } from '@ringcentral-integration/test-utils'; import { BaseStep } from '@ringcentral-integration/test-utils'; + import type { Context } from '../interfaces'; export { diff --git a/packages/engage-voice-widget/test/utils/moduleUIPageMount.tsx b/packages/engage-voice-widget/test/utils/moduleUIPageMount.tsx index 21f1809942..3b22670507 100644 --- a/packages/engage-voice-widget/test/utils/moduleUIPageMount.tsx +++ b/packages/engage-voice-widget/test/utils/moduleUIPageMount.tsx @@ -1,8 +1,6 @@ -import React from 'react'; - -import { mount } from 'enzyme'; - import { RcThemeProvider } from '@ringcentral/juno'; +import { mount } from 'enzyme'; +import React from 'react'; /** * mount ui module with props automatically diff --git a/packages/eslint-settings/.eslintrc.js b/packages/eslint-settings/.eslintrc.js index c32f056e20..0d470b1d69 100644 --- a/packages/eslint-settings/.eslintrc.js +++ b/packages/eslint-settings/.eslintrc.js @@ -1,15 +1,23 @@ /** @type {import('eslint').Linter.Config} */ module.exports = { - plugins: ['@nrwl/nx', 'lodash'], + plugins: ['@typescript-eslint', 'lodash', 'jsx-a11y'], ignorePatterns: ['node_modules', 'dist', 'build', 'html-report', '*.json'], + env: { + es6: true, + node: true, + browser: true, + }, overrides: [ // js ts files { files: ['*.ts', '*.tsx', '*.js', '*.jsx'], extends: [ - 'plugin:@nrwl/nx/javascript', + 'eslint:recommended', + 'plugin:react/recommended', + 'plugin:react-hooks/recommended', 'plugin:import/recommended', 'plugin:prettier/recommended', + 'plugin:jsx-a11y/recommended', ], settings: { 'import/resolver': { @@ -17,50 +25,53 @@ module.exports = { extensions: ['.js', '.jsx', '.ts', '.tsx'], }, }, + react: { + version: 'detect', // React version. "detect" automatically picks the version you have installed. + }, }, rules: { 'lodash/import-scope': 'error', - 'react/no-array-index-key': 'warn', + 'react/display-name': 'off', + 'react/prop-types': 'off', 'import/no-cycle': 'error', - 'import/order': 'error', 'import/no-duplicates': 'error', 'import/named': 'off', + 'no-undef': 'error', + // just use ts to verify that + 'import/default': 'off', 'no-console': 'warn', '@typescript-eslint/no-empty-function': 'off', - // for more detail view here https://nx.dev/structure/monorepo-tags - '@nrwl/nx/enforce-module-boundaries': [ - 'error', - { - enforceBuildableLibDependency: false, - allow: [], - depConstraints: [ - { - sourceTag: '*', - onlyDependOnLibsWithTags: ['*'], - }, - ], - }, - ], + }, + parserOptions: { + ecmaVersion: 2021, + sourceType: 'module', }, }, // js files { files: ['*.js', '*.jsx'], - extends: ['plugin:@nrwl/nx/javascript'], - rules: { - '@typescript-eslint/no-empty-function': 'off', - }, + extends: [], + rules: {}, }, // ts files { files: ['*.ts', '*.tsx'], - extends: ['plugin:@nrwl/nx/typescript', 'plugin:import/typescript'], + extends: [ + 'plugin:@typescript-eslint/recommended', + 'plugin:import/typescript', + ], rules: { + '@typescript-eslint/no-unused-vars': [ + 'warn', + { + argsIgnorePattern: '^_', + }, + ], '@typescript-eslint/no-non-null-asserted-optional-chain': 'off', '@typescript-eslint/no-empty-interface': 'off', '@typescript-eslint/no-var-requires': 'warn', - '@typescript-eslint/no-unused-vars': 'off', '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/no-explicit-any': 'off', // * close that for current eslint still not support metadata https://github.com/typescript-eslint/typescript-eslint/issues/5468 // '@typescript-eslint/consistent-type-imports': 'error', '@typescript-eslint/ban-types': [ @@ -79,28 +90,12 @@ module.exports = { // react files { files: ['*.jsx', '*.tsx'], - extends: ['plugin:@nrwl/nx/react'], + extends: [], rules: { // a11y still not need in our app 'jsx-a11y/anchor-is-valid': 'off', }, }, - { - files: ['**/__stories__/**/*'], - extends: ['plugin:storybook/recommended'], - rules: { - 'no-console': 'off', - '@nrwl/nx/enforce-module-boundaries': 'off', - 'jest/no-disabled-tests': 'off', - 'jest/valid-expect': 'error', - 'jest/valid-expect-in-promise': 'error', - 'jest/no-identical-title': 'warn', - 'jsx-a11y/accessible-emoji': 'warn', - 'react/button-has-type': 'off', - 'jsx-a11y/label-has-associated-control': 'warn', - 'no-alert': 'warn', - }, - }, // scripts related files { files: ['**/scripts/**/*', '**/tools/**/*', 'gulpfile.js'], @@ -112,12 +107,12 @@ module.exports = { 'import/default': 'off', 'import/no-named-as-default-member': 'off', 'import/no-cycle': 'off', - '@nrwl/nx/enforce-module-boundaries': 'off', }, }, // test files { files: [ + 'packages/script-weaver-e2e-test/**/*', '**/*.test.ts', '**/*.spec.ts', '**/*.spec.tsx', @@ -128,10 +123,15 @@ module.exports = { '**/*.spec.jsx', ], plugins: ['jest'], + extends: ['plugin:jest/recommended'], rules: { - '@nrwl/nx/enforce-module-boundaries': 'off', + 'no-console': 'off', 'react-hooks/rules-of-hooks': 'off', + 'react/jsx-key': 'off', + 'jest/no-conditional-expect': 'off', 'no-undef': 'off', + 'jest/no-export': 'off', + 'jest/no-standalone-expect': 'off', }, }, { @@ -142,7 +142,7 @@ module.exports = { }, // for next generation projects, we should install all dep in root folder, so there is no longer need that settings { - files: ['__next__/**'], + files: ['apps/**'], rules: { 'import/no-extraneous-dependencies': 'off', // * close that for current eslint still not support metadata https://github.com/typescript-eslint/typescript-eslint/issues/5468 diff --git a/packages/eslint-settings/package.json b/packages/eslint-settings/package.json index 07a6505d3e..53ffa126f4 100644 --- a/packages/eslint-settings/package.json +++ b/packages/eslint-settings/package.json @@ -11,9 +11,8 @@ "update-eslint": "ncu '/eslint/' -u" }, "dependencies": { - "@nrwl/eslint-plugin-nx": "15.3.3", - "@typescript-eslint/eslint-plugin": "5.36.1", - "@typescript-eslint/parser": "5.36.1", + "@typescript-eslint/eslint-plugin": "^6.18.1", + "@typescript-eslint/parser": "^6.18.1", "eslint": "8.23.0", "eslint-config-prettier": "8.5.0", "eslint-plugin-import": "2.26.0", @@ -22,8 +21,7 @@ "eslint-plugin-lodash": "^7.4.0", "eslint-plugin-prettier": "4.2.1", "eslint-plugin-react": "7.31.1", - "eslint-plugin-react-hooks": "4.6.0", - "eslint-plugin-storybook": "0.6.4" + "eslint-plugin-react-hooks": "4.6.0" }, "engines": { "node": ">=14" diff --git a/packages/glip-widgets/components/EmojiSelect/index.js b/packages/glip-widgets/components/EmojiSelect/index.js index 0c937a967c..723d983906 100644 --- a/packages/glip-widgets/components/EmojiSelect/index.js +++ b/packages/glip-widgets/components/EmojiSelect/index.js @@ -1,7 +1,6 @@ -import React from 'react'; +import clsx from 'clsx'; import PropTypes from 'prop-types'; -import classnames from 'classnames'; - +import React from 'react'; import Emojify from 'react-emojione'; import emojiones from '../../assets/images/emojione.png'; @@ -11,7 +10,7 @@ import styles from './styles.scss'; export default function EmojiSelect({ onSelect, className }) { return ( -

+
{emojis.map((emoji) => { const emojsStr = `:${emoji}:`; return ( diff --git a/packages/glip-widgets/components/GlipChatForm/index.js b/packages/glip-widgets/components/GlipChatForm/index.js index cf43a05db7..733390fb8e 100644 --- a/packages/glip-widgets/components/GlipChatForm/index.js +++ b/packages/glip-widgets/components/GlipChatForm/index.js @@ -1,20 +1,20 @@ -import React, { Component } from 'react'; +import clsx from 'clsx'; import PropTypes from 'prop-types'; -import classnames from 'classnames'; -import Tooltip from 'rc-tooltip'; -import 'rc-tooltip/assets/bootstrap_white.css'; -import 'rc-editor-mention/assets/index.css'; import Mention, { Nav, toString, toEditorState, getMentions, } from 'rc-editor-mention'; - -import EmojiSelect from '../EmojiSelect'; +import 'rc-editor-mention/assets/index.css'; +import Tooltip from 'rc-tooltip'; +import 'rc-tooltip/assets/bootstrap_white.css'; +import React, { Component } from 'react'; import emojiIcon from '../../assets/images/emoji.png'; import uploadIcon from '../../assets/images/upload.png'; +import EmojiSelect from '../EmojiSelect'; + import styles from './styles.scss'; function isOnMobileDevice() { @@ -183,7 +183,7 @@ export default class GlipChatForm extends Component { const { className, placeholder, height } = this.props; const noFoundString = 'No found.'; // TODO: i18n after string confirmed return ( -
+
) : null; return ( -
+
+
{image} {unreadEl}
diff --git a/packages/glip-widgets/components/GlipGroupItem/index.js b/packages/glip-widgets/components/GlipGroupItem/index.js index fff388c357..371c0076c7 100644 --- a/packages/glip-widgets/components/GlipGroupItem/index.js +++ b/packages/glip-widgets/components/GlipGroupItem/index.js @@ -1,11 +1,11 @@ -import React from 'react'; +import clsx from 'clsx'; import PropTypes from 'prop-types'; -import classnames from 'classnames'; +import React from 'react'; import { getPostAbstract } from '../../lib/formatPost'; - import GlipGroupAvatar from '../GlipGroupAvatar'; import GlipGroupName from '../GlipGroupName'; + import styles from './styles.scss'; function LatestPost({ latestPost, members }) { @@ -43,11 +43,7 @@ LatestPost.defaultProps = { export default function GlipGroup({ group, className, onSelectGroup, active }) { return (
: null; // TODO: update searching with i18n return ( -
+
+
*:first-child { +.markdown-body > *:first-child { margin-top: 0 !important; } -.markdown-body>*:last-child { +.markdown-body > *:last-child { margin-bottom: 0 !important; } @@ -340,7 +341,7 @@ margin-bottom: 0; } -.markdown-body li>p { +.markdown-body li > p { margin-top: 16px; } @@ -367,11 +368,11 @@ border-left: 4px solid #ddd; } -.markdown-body blockquote>:first-child { +.markdown-body blockquote > :first-child { margin-top: 0; } -.markdown-body blockquote>:last-child { +.markdown-body blockquote > :last-child { margin-bottom: 0; } @@ -414,17 +415,17 @@ padding-bottom: 0.2em; margin: 0; font-size: 85%; - background-color: rgba(0,0,0,0.04); + background-color: rgba(0, 0, 0, 0.04); border-radius: 3px; } .markdown-body code:before, .markdown-body code:after { letter-spacing: -0.2em; - content: "\00a0"; + content: '\00a0'; } -.markdown-body pre>code { +.markdown-body pre > code { padding: 0; margin: 0; font-size: 100%; @@ -590,7 +591,7 @@ .markdown-body kbd { display: inline-block; padding: 3px 5px; - font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace; + font: 11px Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: 10px; color: #555; vertical-align: middle; @@ -603,11 +604,11 @@ .markdown-body:before { display: table; - content: ""; + content: ''; } .markdown-body:after { display: table; clear: both; - content: ""; + content: ''; } diff --git a/packages/glip-widgets/components/GlipPostContent/index.js b/packages/glip-widgets/components/GlipPostContent/index.js index 20166cf1dd..7bf3835deb 100644 --- a/packages/glip-widgets/components/GlipPostContent/index.js +++ b/packages/glip-widgets/components/GlipPostContent/index.js @@ -1,11 +1,9 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; - import DownloadIcon from '@ringcentral-integration/widgets/assets/images/Download.svg'; +import clsx from 'clsx'; +import PropTypes from 'prop-types'; +import React from 'react'; import isPicture from '../../lib/isPicture'; - import Markdown from '../GlipMarkdown'; import styles from './styles.scss'; @@ -50,9 +48,7 @@ Attachments.propTypes = { function PostContent({ post, className, atRender }) { if (!post.text && (!post.attachments || post.attachments.length === 0)) { return ( -
- Unsupported message -
+
Unsupported message
); } let text = post.text; @@ -66,7 +62,7 @@ function PostContent({ post, className, atRender }) { ) : null; return ( -
+
{textContent} {attachments} diff --git a/packages/glip-widgets/components/GlipPostItem/index.js b/packages/glip-widgets/components/GlipPostItem/index.js index e7963d617c..ef79dda6cb 100644 --- a/packages/glip-widgets/components/GlipPostItem/index.js +++ b/packages/glip-widgets/components/GlipPostItem/index.js @@ -1,10 +1,11 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; import status from '@ringcentral-integration/commons/modules/GlipPosts/status'; +import clsx from 'clsx'; +import PropTypes from 'prop-types'; +import React from 'react'; import defaultAvatar from '../../assets/images/default_avatar.png'; import GlipPostContent from '../GlipPostContent'; + import styles from './styles.scss'; function PostAvatar({ creator, viewProfile }) { @@ -103,7 +104,7 @@ export default function GlipPost({ } // TODO: update joining status with i18n return ( -
+
diff --git a/packages/glip-widgets/components/GlipPostList/index.js b/packages/glip-widgets/components/GlipPostList/index.js index 40ff8f707e..466094f746 100644 --- a/packages/glip-widgets/components/GlipPostList/index.js +++ b/packages/glip-widgets/components/GlipPostList/index.js @@ -1,7 +1,9 @@ -import React, { PureComponent } from 'react'; +import clsx from 'clsx'; import PropTypes from 'prop-types'; -import classnames from 'classnames'; +import React, { PureComponent } from 'react'; + import GlipPostItem from '../GlipPostItem'; + import styles from './styles.scss'; export default class GlipPostList extends PureComponent { @@ -102,7 +104,7 @@ export default class GlipPostList extends PureComponent { let lastDate; return (
{ this._listRef = list; }} diff --git a/packages/glip-widgets/components/GlipTeamCreation/styles.scss b/packages/glip-widgets/components/GlipTeamCreation/styles.scss index f707f3b3b6..673532ef8e 100644 --- a/packages/glip-widgets/components/GlipTeamCreation/styles.scss +++ b/packages/glip-widgets/components/GlipTeamCreation/styles.scss @@ -13,7 +13,7 @@ .selectedContactItem { box-sizing: content-box; - background-color: #066FAC; + background-color: #066fac; position: relative; color: #fff; border-radius: 20px; diff --git a/packages/glip-widgets/package.json b/packages/glip-widgets/package.json index 0727f2538a..7d738a0094 100644 --- a/packages/glip-widgets/package.json +++ b/packages/glip-widgets/package.json @@ -20,7 +20,6 @@ "release": "yarn gulp release" }, "dependencies": { - "classnames": "^2.2.5", "immutable": "^3.7.4", "prop-types": "^15.7.2", "ramda": "^0.28.0", @@ -34,23 +33,20 @@ "@ringcentral-integration/core": "*", "@ringcentral-integration/locale-loader": "*", "@ringcentral-integration/locale-settings": "*", - "autoprefixer": "^9.8.4", "execa": "^5.0.0", "fs-extra": "^10.1.0", "gulp": "^4.0.2", "gulp-babel": "^8.0.0", "gulp-istanbul": "^1.1.1", "gulp-sourcemaps": "^2.6.5", - "identity-obj-proxy": "^3.0.0", - "react-svg-loader": "^3.0.3", "redux-logger": "^3.0.6", "redux-thunk": "^2.2.0", "yargs": "^17.1.1" }, "peerDependencies": { - "@ringcentral-integration/commons": "^0.14.0", - "@ringcentral-integration/core": "^0.14.0", - "@ringcentral-integration/widgets": "^0.14.0", + "@ringcentral-integration/commons": "^0.15.0", + "@ringcentral-integration/core": "^0.15.0", + "@ringcentral-integration/widgets": "^0.15.0", "react": "^17.0.2", "react-dom": "^17.0.2", "react-redux": "^5.1.1", @@ -59,5 +55,10 @@ }, "ci": { "ringcentral-widgets-test": "**" + }, + "nx": { + "tags": [ + "scope:ci-group-1" + ] } } diff --git a/packages/i18n-dayjs/.gitignore b/packages/i18n-dayjs/.gitignore new file mode 100644 index 0000000000..3c3629e647 --- /dev/null +++ b/packages/i18n-dayjs/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/packages/i18n-dayjs/LICENSE b/packages/i18n-dayjs/LICENSE new file mode 100644 index 0000000000..c2f1915821 --- /dev/null +++ b/packages/i18n-dayjs/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2024 RingCentral, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/i18n-dayjs/README.md b/packages/i18n-dayjs/README.md new file mode 100644 index 0000000000..f046a34fa0 --- /dev/null +++ b/packages/i18n-dayjs/README.md @@ -0,0 +1 @@ +# I18n dayjs diff --git a/packages/i18n-dayjs/gulpfile.js b/packages/i18n-dayjs/gulpfile.js new file mode 100644 index 0000000000..2276059770 --- /dev/null +++ b/packages/i18n-dayjs/gulpfile.js @@ -0,0 +1,93 @@ +import cp from 'child_process'; +import fs from 'fs-extra'; +import gulp from 'gulp'; +import path from 'path'; +import yargs from 'yargs'; + +const DEFAULT_BUILD_PATH = path.resolve(__dirname, '../../build/i18n-dayjs'); + +const { argv } = yargs + .alias({ + buildPath: 'build-path', + }) + .default('buildPath', DEFAULT_BUILD_PATH); + +const { buildPath } = argv; + +export function clean() { + return fs.remove(buildPath); +} + +export function compile() { + return exec('yarn build'); +} + +export const build = gulp.series(clean, compile); + +async function exec(command) { + return new Promise((resolve, reject) => { + cp.exec(command, (error, stdout) => { + if (error) { + reject(error); + return; + } + resolve(stdout); + }); + }); +} + +async function getVersionFromTag() { + try { + let tag = await exec( + 'git describe --exact-match --tags $(git rev-parse HEAD)', + ); + tag = tag.replace(/\r?\n|\r/g, ''); + if (/^\d+.\d+.\d+/.test(tag)) { + return tag; + } + return null; + } catch (e) { + return null; + } +} + +const RELEASE_PATH = path.resolve(__dirname, '../../release/i18n-dayjs'); + +export async function releaseClean() { + if (!(await fs.exists(RELEASE_PATH))) { + await fs.mkdirp(RELEASE_PATH); + } + const files = (await fs.readdir(RELEASE_PATH)).filter( + (file) => !/^\./.test(file), + ); + for (const file of files) { + await fs.remove(path.resolve(RELEASE_PATH, file)); + } +} + +export function releaseCopy() { + return gulp + .src([`${buildPath}/**`, `${__dirname}/README.md`, `${__dirname}/LICENSE`]) + .pipe(gulp.dest(RELEASE_PATH)); +} + +export async function generatePackage() { + const packageInfo = JSON.parse( + await fs.readFile(path.resolve(__dirname, 'package.json')), + ); + delete packageInfo.scripts; + delete packageInfo.devDependencies; + const version = await getVersionFromTag(); + if (version) { + packageInfo.version = version; + } + await fs.writeFile( + path.resolve(RELEASE_PATH, 'package.json'), + JSON.stringify(packageInfo, null, 2), + ); +} + +export const release = gulp.series( + gulp.parallel(build, releaseClean), + gulp.parallel(releaseCopy, generatePackage), +); diff --git a/packages/i18n-dayjs/index.ts b/packages/i18n-dayjs/index.ts new file mode 100644 index 0000000000..8420b1093f --- /dev/null +++ b/packages/i18n-dayjs/index.ts @@ -0,0 +1 @@ +export * from './src'; diff --git a/packages/i18n-dayjs/package.json b/packages/i18n-dayjs/package.json new file mode 100644 index 0000000000..4829642f17 --- /dev/null +++ b/packages/i18n-dayjs/package.json @@ -0,0 +1,38 @@ +{ + "name": "@ringcentral-integration/i18n-dayjs", + "version": "0.15.0", + "private": false, + "description": "RingCentral integration i18n with dayjs", + "homepage": "https://github.com/ringcentral/ringcentral-js-widgets#readme", + "bugs": { + "url": "https://github.com/ringcentral/ringcentral-js-widgets/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/ringcentral/ringcentral-js-widgets.git" + }, + "license": "MIT", + "author": "RingCentral Integrations", + "scripts": { + "build": "tsc", + "gulp": "gulp --require @ringcentral-integration/babel-settings/lib/register.js", + "release": "yarn gulp release" + }, + "dependencies": { + "dayjs": "^1.11.7" + }, + "devDependencies": { + "@ringcentral-integration/babel-settings": "*", + "@ringcentral-integration/i18n": "*", + "@ringcentral-integration/utils": "*", + "fs-extra": "^10.1.0", + "gulp": "^4.0.2", + "gulp-babel": "^8.0.0", + "gulp-sourcemaps": "^2.6.5", + "yargs": "^17.1.1" + }, + "peerDependencies": { + "@ringcentral-integration/i18n": "^2.2.1", + "@ringcentral-integration/utils": "^0.15.0" + } +} diff --git a/packages/i18n-dayjs/src/dayjsLanguagesMap.ts b/packages/i18n-dayjs/src/dayjsLanguagesMap.ts new file mode 100644 index 0000000000..b2f1c9cd83 --- /dev/null +++ b/packages/i18n-dayjs/src/dayjsLanguagesMap.ts @@ -0,0 +1,28 @@ +// ringcentral-js-widgets/locale-settings/index.js +import dayjs from 'dayjs'; + +export const dayjsLanguagesMap = { + 'en-US': 'en', + 'de-DE': 'de', + 'en-AU': 'en-au', + 'en-GB': 'en-gb', + 'es-419': 'es-do', + 'es-ES': 'es', + 'fr-CA': 'fr-ca', + 'fr-FR': 'fr', + 'it-IT': 'it', + 'ja-JP': 'ja', + 'pt-BR': 'pt-br', + 'pt-PT': 'pt', + 'zh-CN': 'zh-cn', + 'zh-HK': 'zh-hk', + 'zh-TW': 'zh-tw', + 'nl-NL': 'nl', + 'ko-KR': 'ko', + 'fi-FI': 'fi', +}; + +export const setDayjsLocale = (locale: string) => { + const dayjsLocale = dayjsLanguagesMap[locale as never]; + dayjs.locale(dayjsLocale || locale!); +}; diff --git a/packages/i18n-dayjs/src/i18n/de-DE.ts b/packages/i18n-dayjs/src/i18n/de-DE.ts new file mode 100644 index 0000000000..978d78b34b --- /dev/null +++ b/packages/i18n-dayjs/src/i18n/de-DE.ts @@ -0,0 +1,3 @@ +import 'dayjs/locale/de'; + +export default {}; diff --git a/packages/i18n-dayjs/src/i18n/en-AU.ts b/packages/i18n-dayjs/src/i18n/en-AU.ts new file mode 100644 index 0000000000..2618aaab74 --- /dev/null +++ b/packages/i18n-dayjs/src/i18n/en-AU.ts @@ -0,0 +1,3 @@ +import 'dayjs/locale/en-au'; + +export default {}; diff --git a/packages/i18n-dayjs/src/i18n/en-GB.ts b/packages/i18n-dayjs/src/i18n/en-GB.ts new file mode 100644 index 0000000000..356f12d629 --- /dev/null +++ b/packages/i18n-dayjs/src/i18n/en-GB.ts @@ -0,0 +1,3 @@ +import 'dayjs/locale/en-gb'; + +export default {}; diff --git a/packages/i18n-dayjs/src/i18n/en-US.ts b/packages/i18n-dayjs/src/i18n/en-US.ts new file mode 100644 index 0000000000..927e1a9f2f --- /dev/null +++ b/packages/i18n-dayjs/src/i18n/en-US.ts @@ -0,0 +1 @@ +export default {} as const; diff --git a/packages/i18n-dayjs/src/i18n/es-419.ts b/packages/i18n-dayjs/src/i18n/es-419.ts new file mode 100644 index 0000000000..9bf0cfd9c9 --- /dev/null +++ b/packages/i18n-dayjs/src/i18n/es-419.ts @@ -0,0 +1,3 @@ +import 'dayjs/locale/es-do'; + +export default {}; diff --git a/packages/i18n-dayjs/src/i18n/es-ES.ts b/packages/i18n-dayjs/src/i18n/es-ES.ts new file mode 100644 index 0000000000..3968dab599 --- /dev/null +++ b/packages/i18n-dayjs/src/i18n/es-ES.ts @@ -0,0 +1,3 @@ +import 'dayjs/locale/es'; + +export default {}; diff --git a/packages/i18n-dayjs/src/i18n/fi-FI.ts b/packages/i18n-dayjs/src/i18n/fi-FI.ts new file mode 100644 index 0000000000..319aa39b69 --- /dev/null +++ b/packages/i18n-dayjs/src/i18n/fi-FI.ts @@ -0,0 +1,3 @@ +import 'dayjs/locale/fi'; + +export default {}; diff --git a/packages/i18n-dayjs/src/i18n/fr-CA.ts b/packages/i18n-dayjs/src/i18n/fr-CA.ts new file mode 100644 index 0000000000..792a7cade0 --- /dev/null +++ b/packages/i18n-dayjs/src/i18n/fr-CA.ts @@ -0,0 +1,3 @@ +import 'dayjs/locale/fr-ca'; + +export default {}; diff --git a/packages/i18n-dayjs/src/i18n/fr-FR.ts b/packages/i18n-dayjs/src/i18n/fr-FR.ts new file mode 100644 index 0000000000..1bde301cd1 --- /dev/null +++ b/packages/i18n-dayjs/src/i18n/fr-FR.ts @@ -0,0 +1,3 @@ +import 'dayjs/locale/fr'; + +export default {}; diff --git a/packages/ringcentral-widgets/components/MeetingConfigs/i18n/index.ts b/packages/i18n-dayjs/src/i18n/index.ts similarity index 71% rename from packages/ringcentral-widgets/components/MeetingConfigs/i18n/index.ts rename to packages/i18n-dayjs/src/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/ringcentral-widgets/components/MeetingConfigs/i18n/index.ts +++ b/packages/i18n-dayjs/src/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/i18n-dayjs/src/i18n/it-IT.ts b/packages/i18n-dayjs/src/i18n/it-IT.ts new file mode 100644 index 0000000000..47de3d73e7 --- /dev/null +++ b/packages/i18n-dayjs/src/i18n/it-IT.ts @@ -0,0 +1,3 @@ +import 'dayjs/locale/it'; + +export default {}; diff --git a/packages/i18n-dayjs/src/i18n/ja-JP.ts b/packages/i18n-dayjs/src/i18n/ja-JP.ts new file mode 100644 index 0000000000..d13dd3c9f6 --- /dev/null +++ b/packages/i18n-dayjs/src/i18n/ja-JP.ts @@ -0,0 +1,3 @@ +import 'dayjs/locale/ja'; + +export default {}; diff --git a/packages/i18n-dayjs/src/i18n/ko-KR.ts b/packages/i18n-dayjs/src/i18n/ko-KR.ts new file mode 100644 index 0000000000..56efe37fdc --- /dev/null +++ b/packages/i18n-dayjs/src/i18n/ko-KR.ts @@ -0,0 +1,3 @@ +import 'dayjs/locale/ko'; + +export default {}; diff --git a/packages/ringcentral-widgets/components/MeetingConfigs/i18n/loadLocale.ts b/packages/i18n-dayjs/src/i18n/loadLocale.ts similarity index 100% rename from packages/ringcentral-widgets/components/MeetingConfigs/i18n/loadLocale.ts rename to packages/i18n-dayjs/src/i18n/loadLocale.ts diff --git a/packages/i18n-dayjs/src/i18n/nl-NL.ts b/packages/i18n-dayjs/src/i18n/nl-NL.ts new file mode 100644 index 0000000000..dc8459bf97 --- /dev/null +++ b/packages/i18n-dayjs/src/i18n/nl-NL.ts @@ -0,0 +1,3 @@ +import 'dayjs/locale/nl'; + +export default {}; diff --git a/packages/i18n-dayjs/src/i18n/pt-BR.ts b/packages/i18n-dayjs/src/i18n/pt-BR.ts new file mode 100644 index 0000000000..7f46ac0851 --- /dev/null +++ b/packages/i18n-dayjs/src/i18n/pt-BR.ts @@ -0,0 +1,3 @@ +import 'dayjs/locale/pt-br'; + +export default {}; diff --git a/packages/i18n-dayjs/src/i18n/pt-PT.ts b/packages/i18n-dayjs/src/i18n/pt-PT.ts new file mode 100644 index 0000000000..65368fa2f5 --- /dev/null +++ b/packages/i18n-dayjs/src/i18n/pt-PT.ts @@ -0,0 +1,3 @@ +import 'dayjs/locale/pt'; + +export default {}; diff --git a/packages/i18n-dayjs/src/i18n/zh-CN.ts b/packages/i18n-dayjs/src/i18n/zh-CN.ts new file mode 100644 index 0000000000..9dd43dd48f --- /dev/null +++ b/packages/i18n-dayjs/src/i18n/zh-CN.ts @@ -0,0 +1,3 @@ +import 'dayjs/locale/zh-cn'; + +export default {}; diff --git a/packages/i18n-dayjs/src/i18n/zh-HK.ts b/packages/i18n-dayjs/src/i18n/zh-HK.ts new file mode 100644 index 0000000000..0171721583 --- /dev/null +++ b/packages/i18n-dayjs/src/i18n/zh-HK.ts @@ -0,0 +1,3 @@ +import 'dayjs/locale/zh-hk'; + +export default {}; diff --git a/packages/i18n-dayjs/src/i18n/zh-TW.ts b/packages/i18n-dayjs/src/i18n/zh-TW.ts new file mode 100644 index 0000000000..b718e5fb68 --- /dev/null +++ b/packages/i18n-dayjs/src/i18n/zh-TW.ts @@ -0,0 +1,3 @@ +import 'dayjs/locale/zh-tw'; + +export default {}; diff --git a/packages/i18n-dayjs/src/index.ts b/packages/i18n-dayjs/src/index.ts new file mode 100644 index 0000000000..fbcf65da24 --- /dev/null +++ b/packages/i18n-dayjs/src/index.ts @@ -0,0 +1,3 @@ +import './i18n'; + +export * from './dayjsLanguagesMap'; diff --git a/packages/i18n-dayjs/tsconfig.json b/packages/i18n-dayjs/tsconfig.json new file mode 100644 index 0000000000..679a07017a --- /dev/null +++ b/packages/i18n-dayjs/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../tsconfig.json", + "include": ["./**/*.ts", "./**/*.tsx", "./typings/**/*.d.ts"], + "compilerOptions": { + "outDir": "../../build/i18n-dayjs", + "noEmit": false, + "sourceMap": true + } +} diff --git a/packages/i18n-dayjs/typings/typings.d.ts b/packages/i18n-dayjs/typings/typings.d.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/i18n/.babelrc b/packages/i18n/.babelrc deleted file mode 100644 index 0967ef424b..0000000000 --- a/packages/i18n/.babelrc +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/packages/i18n/LICENSE b/packages/i18n/LICENSE index 2e02baa6d5..c2f1915821 100644 --- a/packages/i18n/LICENSE +++ b/packages/i18n/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016 RingCentral, Inc. +Copyright (c) 2024 RingCentral, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/packages/i18n/babel.config.js b/packages/i18n/babel.config.js deleted file mode 100644 index bf8cb4c149..0000000000 --- a/packages/i18n/babel.config.js +++ /dev/null @@ -1,3 +0,0 @@ -const baseConfig = require('@ringcentral-integration/babel-settings'); - -module.exports = baseConfig; diff --git a/packages/i18n/constants.ts b/packages/i18n/constants.ts new file mode 100644 index 0000000000..68056bed56 --- /dev/null +++ b/packages/i18n/constants.ts @@ -0,0 +1,2 @@ +export const DEFAULT_LOCALE = 'en-US'; +export const PSEUDO_LOCALE = 'en-ZZ'; diff --git a/packages/i18n/gulpfile.js b/packages/i18n/gulpfile.js index a399606aa7..75ace65e3d 100644 --- a/packages/i18n/gulpfile.js +++ b/packages/i18n/gulpfile.js @@ -1,53 +1,25 @@ -import path from 'path'; import cp from 'child_process'; -import gulp from 'gulp'; import fs from 'fs-extra'; -import babel from 'gulp-babel'; -import sourcemaps from 'gulp-sourcemaps'; +import gulp from 'gulp'; +import path from 'path'; import yargs from 'yargs'; const DEFAULT_BUILD_PATH = path.resolve(__dirname, '../../build/i18n'); -const DEFAULT_BABEL_CONFIG = 'babel.config.js'; -const SUPPORTED_BABEL_CONFIGS = [ - DEFAULT_BABEL_CONFIG, - 'electron-babel.config.js', -]; const { argv } = yargs .alias({ buildPath: 'build-path', - babelConfig: 'babel-config', }) - .default('buildPath', DEFAULT_BUILD_PATH) - .default('babelConfig', DEFAULT_BABEL_CONFIG) - .choices('babelConfig', SUPPORTED_BABEL_CONFIGS); + .default('buildPath', DEFAULT_BUILD_PATH); -const { buildPath, babelConfig } = argv; +const { buildPath } = argv; export function clean() { return fs.remove(buildPath); } export function compile() { - const configFile = path.resolve(__dirname, babelConfig); - if (!fs.existsSync(configFile)) { - throw new Error(`Not found babel config ${configFile}`); - } - return gulp - .src( - [ - './lib/**/*.js', - '!./lib/**/*.test.js', - './index.js', - ], - { - base: './', - }, - ) - .pipe(sourcemaps.init()) - .pipe(babel({ configFile })) - .pipe(sourcemaps.write('.')) - .pipe(gulp.dest(buildPath)); + return exec('yarn build'); } export const build = gulp.series(clean, compile); diff --git a/packages/i18n/index.js b/packages/i18n/i18n.ts similarity index 50% rename from packages/i18n/index.js rename to packages/i18n/i18n.ts index 0220274825..ee94667437 100644 --- a/packages/i18n/index.js +++ b/packages/i18n/i18n.ts @@ -1,15 +1,22 @@ +import { DEFAULT_LOCALE, PSEUDO_LOCALE } from './constants'; import { getLanguageFromLocale } from './lib/getLanguageFromLocale'; import toPseudoString from './lib/toPseudoString'; -export const DEFAULT_LOCALE = 'en-US'; -export const PSEUDO_LOCALE = 'en-ZZ'; +type LanguageInstance = Record< + /** + * key of i18n + */ + string, + string +> | null; + export const RUNTIME = { - locale: DEFAULT_LOCALE, - defaultLocale: DEFAULT_LOCALE, - instances: new Set(), + locale: DEFAULT_LOCALE as string, + defaultLocale: DEFAULT_LOCALE as string, + instances: new Set<{ load: () => Promise }>(), padRatio: 0.3, - fallbackLocale: DEFAULT_LOCALE, - languageDefaults: null, + fallbackLocale: DEFAULT_LOCALE as string, + languageDefaults: null as LanguageInstance, }; /** @@ -18,7 +25,7 @@ export const RUNTIME = { * @param {String} locale - The desired locale. * @return Promise */ -async function setLocale(locale) { +async function setLocale(locale: string) { RUNTIME.locale = locale; await reloadLocales(); } @@ -29,17 +36,17 @@ async function reloadLocales() { } } -async function setDefaultLocale(locale) { +async function setDefaultLocale(locale: string) { RUNTIME.defaultLocale = locale; await reloadLocales(); } -async function setLanguageDefaults(defaults) { +async function setLanguageDefaults(defaults: LanguageInstance) { RUNTIME.languageDefaults = defaults; await reloadLocales(); } -function checkDefaults(locale) { +function checkDefaults(locale: string) { return ( (RUNTIME.languageDefaults && RUNTIME.languageDefaults[locale]) || locale ); @@ -49,31 +56,46 @@ function checkDefaults(locale) { * @class * @description I18n is a simple localizations helper class that represents a set of locale files. */ -export default class I18n { +export default class I18n> { + _cache: Record = {}; /** * @constructor * @description Accepts a loadLocale function that should be async and resolve to the locale * object when invoked. * @param {String => Promise} loadLocale - Asynchronous locale loader function. */ - constructor(loadLocale) { - if (typeof loadLocale !== 'function') { + constructor( + private _loadLocale: (local?: string) => Promise< + | LanguageInstance + | { + __esModule: boolean; + default: LanguageInstance; + } + >, + ) { + if (typeof _loadLocale !== 'function') { throw new Error('loadLocale must be a function'); } - this._loadLocale = loadLocale; - this._cache = {}; + RUNTIME.instances.add(this); this.load(); } - async _load(locale) { + + private async _loadLocaleInstance(locale: string) { + const result = await this._loadLocale(locale); + return (result?.__esModule ? result.default : result) as LanguageInstance; + } + + async _load(locale: string) { if (locale !== PSEUDO_LOCALE && !this._cache[locale]) { let data; try { - data = await this._loadLocale(locale); + data = await this._loadLocaleInstance(locale); + if (!data) { const lang = getLanguageFromLocale(locale); if (lang) { - data = await this._loadLocale(lang); + data = await this._loadLocaleInstance(lang); } } } catch (error) { @@ -90,56 +112,52 @@ export default class I18n { await this._load(checkDefaults(RUNTIME.defaultLocale)); await this._load(checkDefaults(RUNTIME.locale)); } - _getString(key, locale) { - if ( - this._cache[locale] && - Object.prototype.hasOwnProperty.call(this._cache[locale], key) - ) { - return this._cache[locale][key]; + _getString(key: string, locale: string) { + const currI18n = this._cache[locale]; + if (currI18n && Object.prototype.hasOwnProperty.call(currI18n, key)) { + return currI18n[key]; } + const lang = getLanguageFromLocale(locale); + const currParsedLocal = lang && this._cache[lang]; if ( - this._cache[lang] && - Object.prototype.hasOwnProperty.call(this._cache[lang], key) + currParsedLocal && + Object.prototype.hasOwnProperty.call(currParsedLocal, key) ) { - return this._cache[lang][key]; + return currParsedLocal[key]; } - if ( - this._cache[RUNTIME.defaultLocale] && - Object.prototype.hasOwnProperty.call( - this._cache[RUNTIME.defaultLocale], - key, - ) - ) { - return this._cache[RUNTIME.defaultLocale][key]; + + const defaultI18n = this._cache[RUNTIME.defaultLocale]; + if (defaultI18n && Object.prototype.hasOwnProperty.call(defaultI18n, key)) { + return defaultI18n[key]; } + + const fallbackI18n = this._cache[RUNTIME.fallbackLocale]; if ( - this._cache[RUNTIME.fallbackLocale] && - Object.prototype.hasOwnProperty.call( - this._cache[RUNTIME.fallbackLocale], - key, - ) + fallbackI18n && + Object.prototype.hasOwnProperty.call(fallbackI18n, key) ) { - return this._cache[RUNTIME.fallbackLocale][key]; + return fallbackI18n[key]; } return key; } - getString(key, locale = RUNTIME.locale) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + getString(key: K, locale = RUNTIME.locale) { if (locale === PSEUDO_LOCALE) { return toPseudoString({ - str: this._getString(key, RUNTIME.fallbackLocale), + str: this._getString(key as never, RUNTIME.fallbackLocale), padRatio: RUNTIME.padRatio, }); } - return this._getString(key, checkDefaults(locale)); + return this._getString(key as never, checkDefaults(locale)); } - static checkDefaults(locale) { + static checkDefaults(locale: string) { return checkDefaults(locale); } - checkDefaults(locale) { + checkDefaults(locale: string) { return checkDefaults(locale); } @@ -169,22 +187,23 @@ export default class I18n { console.log('ratio must be a number'); return; } - RUNTIME.padRatio = Number.parseFloat(ratio); + RUNTIME.padRatio = + typeof ratio === 'number' ? ratio : Number.parseFloat(ratio); } - static setDefaultLocale(locale) { + static setDefaultLocale(locale: string) { return setDefaultLocale(locale); } - async setDefaultLocale(locale) { + async setDefaultLocale(locale: string) { return setDefaultLocale(locale); } - static async setLanguageDefaults(defaults) { + static async setLanguageDefaults(defaults: LanguageInstance) { return setLanguageDefaults(defaults); } - async setLanguageDefaults(defaults) { + async setLanguageDefaults(defaults: LanguageInstance) { return setLanguageDefaults(defaults); } } diff --git a/packages/i18n/index.d.ts b/packages/i18n/index.d.ts deleted file mode 100644 index 24049cc990..0000000000 --- a/packages/i18n/index.d.ts +++ /dev/null @@ -1,30 +0,0 @@ -export declare const DEFAULT_LOCALE = 'en-US'; -export declare const PSEUDO_LOCALE = 'en-ZZ'; -export declare const RUNTIME: { - locale: string; - defaultLocale: string; - fallbackLocale: string; - instances: Set; - padRatio: number; -}; -declare function setLocale(locale: any): Promise; - -export type GetI18nKey = Parameters[0]; - -export default class I18n> { - _loadLocale: string; - _cache: Record>; - constructor(loadLocale: string); - _load(locale: string): Promise; - load(): Promise; - _getString(key: string, locale: string): string; - getString(key: K, locale?: string): string; - readonly currentLocale: string; - /** - * set current locale, that will fetch the locale data - */ - static readonly setLocale: typeof setLocale; - static setDefaultLocale: (locale: string) => void; - static padRatio: number; -} -export {}; diff --git a/packages/i18n/index.ts b/packages/i18n/index.ts new file mode 100644 index 0000000000..12177941a2 --- /dev/null +++ b/packages/i18n/index.ts @@ -0,0 +1,4 @@ +export * from './i18n'; +export * from './constants'; + +export { default } from './i18n'; diff --git a/packages/i18n/lib/formatLocale/index.d.ts b/packages/i18n/lib/formatLocale/index.d.ts deleted file mode 100644 index c8d2c74e15..0000000000 --- a/packages/i18n/lib/formatLocale/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export default function formatLocale(locale: any, delimeter?: string): any; diff --git a/packages/i18n/lib/formatLocale/index.test.js b/packages/i18n/lib/formatLocale/index.test.ts similarity index 100% rename from packages/i18n/lib/formatLocale/index.test.js rename to packages/i18n/lib/formatLocale/index.test.ts diff --git a/packages/i18n/lib/formatLocale/index.js b/packages/i18n/lib/formatLocale/index.ts similarity index 85% rename from packages/i18n/lib/formatLocale/index.js rename to packages/i18n/lib/formatLocale/index.ts index 912d42ed18..59c4bbced5 100644 --- a/packages/i18n/lib/formatLocale/index.js +++ b/packages/i18n/lib/formatLocale/index.ts @@ -4,7 +4,7 @@ * @param {String} locale * @returns {String} */ -export default function formatLocale(locale, delimeter = '-') { +export default function formatLocale(locale: string, delimeter = '-'): string { const tokens = locale.split(/[-_]/); tokens[0] = tokens[0].toLowerCase(); if (tokens.length > 1) { diff --git a/packages/i18n/lib/getLanguageFromLocale/index.d.ts b/packages/i18n/lib/getLanguageFromLocale/index.d.ts deleted file mode 100644 index 24e1faf001..0000000000 --- a/packages/i18n/lib/getLanguageFromLocale/index.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const languageRexExp: RegExp; - -export const getLanguageFromLocale: (locale: string) => string; diff --git a/packages/i18n/lib/getLanguageFromLocale/index.js b/packages/i18n/lib/getLanguageFromLocale/index.js deleted file mode 100644 index 5caf9e7fa8..0000000000 --- a/packages/i18n/lib/getLanguageFromLocale/index.js +++ /dev/null @@ -1,5 +0,0 @@ -export const languageRexExp = /^([a-z]{2})(-.*)?/; - -export const getLanguageFromLocale = (locale) => { - return locale.match(languageRexExp)?.[1] ?? null; -}; diff --git a/packages/i18n/lib/getLanguageFromLocale/index.ts b/packages/i18n/lib/getLanguageFromLocale/index.ts new file mode 100644 index 0000000000..2cabaef5bd --- /dev/null +++ b/packages/i18n/lib/getLanguageFromLocale/index.ts @@ -0,0 +1,7 @@ +import { DEFAULT_LOCALE } from '../../constants'; + +export const languageRexExp = /^([a-z]{2})(-.*)?/; + +export const getLanguageFromLocale = (locale: string) => { + return locale.match(languageRexExp)?.[1] ?? DEFAULT_LOCALE; +}; diff --git a/packages/i18n/lib/processI18n/index.test.ts b/packages/i18n/lib/processI18n/index.test.ts new file mode 100644 index 0000000000..4a55b83b57 --- /dev/null +++ b/packages/i18n/lib/processI18n/index.test.ts @@ -0,0 +1,26 @@ +import { processI18n } from './index'; +import type { I18nStrings } from './type'; + +describe('process i18n test', () => { + const givenData: { + appName: I18nStrings; + } = { + appName: { + __i18n__: true, + translations: { + 'en-US': 'Test app name', + 'zh-CN': '测试 app', + }, + }, + }; + + it.each` + locale | expected + ${'en-US'} | ${'Test app name'} + ${'zh-CN'} | ${'测试 app'} + ${'en-ZZ'} | ${'[[~!]Ţéšţ åþþ ñåɱé[~!]]'} + `('get localized appName via locale: $locale', ({ locale, expected }) => { + const actual = processI18n(givenData, locale); + expect(actual.appName).toEqual(expected); + }); +}); diff --git a/packages/i18n/lib/processI18n/index.ts b/packages/i18n/lib/processI18n/index.ts new file mode 100644 index 0000000000..c478c5e390 --- /dev/null +++ b/packages/i18n/lib/processI18n/index.ts @@ -0,0 +1,64 @@ +import { DEFAULT_LOCALE, PSEUDO_LOCALE } from '../../constants'; +import I18n from '../../i18n'; +import { getLanguageFromLocale } from '../getLanguageFromLocale'; +import toPseudoString from '../toPseudoString'; + +import { I18nFlag, I18nStrings, LocaleCode } from './type'; + +export type ConvertI18nToString = T extends Array + ? Array> + : T extends object + ? { + [K in keyof T]: T[K] extends string | I18nStrings + ? string + : ConvertI18nToString; + } + : T; + +/** + * processI18n takes brandConfigs with I18nStrings objects and based on the locale, return brandConfig without the I18nStrings. + * @param config BrandConfig + * @param locale + * @param defaultLocale + * @param parentKey parent key of object + * @returns BrandConfig without I18nStrings structure + */ +export function processI18n( + input: T, + locale: LocaleCode = DEFAULT_LOCALE, + defaultLocale: LocaleCode = DEFAULT_LOCALE, + parentKey?: string, +): ConvertI18nToString { + if (Array.isArray(input)) { + return input.map((item) => + processI18n(item, locale, defaultLocale), + ) as ConvertI18nToString; + } + if (input && typeof input === 'object') { + if ((input as unknown as I18nStrings)[I18nFlag]) { + if (locale === PSEUDO_LOCALE) { + return toPseudoString({ + str: + (input as unknown as I18nStrings).translations[defaultLocale] ?? + parentKey, + padRatio: I18n.padRatio, + }) as unknown as ConvertI18nToString; + } + return ((input as unknown as I18nStrings).translations[locale] ?? + (input as unknown as I18nStrings).translations[ + getLanguageFromLocale(locale) + ] ?? + (input as unknown as I18nStrings).translations[ + defaultLocale + ]) as unknown as ConvertI18nToString; + } + return Object.keys(input).reduce((acc, key) => { + if (Object.hasOwnProperty.call(input, key)) { + acc[key] = processI18n((input as any)[key], locale, defaultLocale, key); + } + return acc; + }, {} as any) as ConvertI18nToString; + } + + return input as ConvertI18nToString; +} diff --git a/packages/i18n/lib/processI18n/type.ts b/packages/i18n/lib/processI18n/type.ts new file mode 100644 index 0000000000..df279cafce --- /dev/null +++ b/packages/i18n/lib/processI18n/type.ts @@ -0,0 +1,10 @@ +export const I18nFlag = '__i18n__'; + +export type I18nStrings = { + [I18nFlag]: true; + translations: { + [k: string]: T; + }; +}; + +export type LocaleCode = string; diff --git a/packages/i18n/lib/toPseudoString/index.d.ts b/packages/i18n/lib/toPseudoString/index.d.ts deleted file mode 100644 index 8f43d58860..0000000000 --- a/packages/i18n/lib/toPseudoString/index.d.ts +++ /dev/null @@ -1,74 +0,0 @@ -export declare const charMap: { - a: number; - b: number; - c: number; - d: number; - e: number; - f: number; - g: number; - h: number; - i: number; - j: number; - k: number; - l: number; - m: number; - n: number; - o: number; - p: number; - q: number; - r: number; - s: number; - t: number; - u: number; - v: number; - w: number; - x: number; - y: number; - z: number; - A: number; - B: number; - C: number; - D: number; - E: number; - F: number; - G: number; - H: number; - I: number; - J: number; - K: number; - L: number; - M: number; - N: number; - O: number; - P: number; - Q: number; - R: number; - S: number; - T: number; - U: number; - V: number; - W: number; - X: number; - Y: number; - Z: number; -}; -export declare function toAccentString(str: string): string; -export declare function processVars(str: string): string; -export declare function padString({ - str, - padRatio, - padChar, -}?: { - str?: string; - padRatio?: number; - padChar?: string; -}): string; -export default function toPseudoString({ - str, - padRatio, - padChar, -}: { - str: string; - padRatio?: number; - padChar?: string; -}): string; diff --git a/packages/i18n/lib/toPseudoString/index.test.js b/packages/i18n/lib/toPseudoString/index.test.ts similarity index 96% rename from packages/i18n/lib/toPseudoString/index.test.js rename to packages/i18n/lib/toPseudoString/index.test.ts index cad0d2b485..aaca87afa5 100644 --- a/packages/i18n/lib/toPseudoString/index.test.js +++ b/packages/i18n/lib/toPseudoString/index.test.ts @@ -1,4 +1,5 @@ import faker from '@faker-js/faker'; + import toPseudoString, { toAccentString, processVars } from './'; describe('toAccentString', () => { @@ -26,7 +27,7 @@ describe('processVars', () => { test('should accentify ICU strings without messing with variables', () => { [...new Array(10)] // this is a bound function in old version faker - .map(() => `{${faker.lorem.word.bind()}}`) + .map(() => `{${faker.lorem.word()}}`) .forEach((str) => { const line = `${faker.lorem.words()} ${str} ${faker.lorem.words()}`; const accentedLine = processVars(line); @@ -52,7 +53,7 @@ describe('toPseudoString', () => { test('should accentify ICU strings without messing with variables', () => { [...new Array(10)] // this is a bound function in old version faker - .map(() => `{${faker.lorem.word.bind()}}`) + .map(() => `{${faker.lorem.word()}}`) .forEach((str) => { const line = `${faker.lorem.words()} ${str} ${faker.lorem.words()}`; const accentedLine = toPseudoString({ str: line }); diff --git a/packages/i18n/lib/toPseudoString/index.js b/packages/i18n/lib/toPseudoString/index.ts similarity index 76% rename from packages/i18n/lib/toPseudoString/index.js rename to packages/i18n/lib/toPseudoString/index.ts index f844166441..9e6891afe4 100644 --- a/packages/i18n/lib/toPseudoString/index.js +++ b/packages/i18n/lib/toPseudoString/index.ts @@ -1,3 +1,9 @@ +export type ToPseudoStringParams = { + str: string; + padRatio?: number; + padChar?: string; +}; + export const charMap = { a: 0x00e5, b: 0x0180, @@ -51,17 +57,17 @@ export const charMap = { X: 0x1e8a, Y: 0x00dd, Z: 0x017d, -}; +} as const; const padCharacters = '~!@#$%^&*'; const replaceFunctions = Object.keys(charMap).map((char) => { const regExp = new RegExp(char, 'g'); - const accentChar = String.fromCharCode(charMap[char]); - return (str) => str.replace(regExp, accentChar); + const accentChar = String.fromCharCode(charMap[char as keyof typeof charMap]); + return (str: string) => str.replace(regExp, accentChar); }); -export function toAccentString(str) { +export function toAccentString(str: string) { let output = `${str}`; replaceFunctions.forEach((fn) => { output = fn(output); @@ -70,10 +76,10 @@ export function toAccentString(str) { } const varsRegExp = /\{.*?\}/; -export function processVars(str) { +export function processVars(str: string) { // extract {xxx} let input = `${str}`; - const tokens = []; + const tokens: string[] = []; let match = varsRegExp.exec(input); while (match) { tokens.push(toAccentString(input.substring(0, match.index))); @@ -85,10 +91,18 @@ export function processVars(str) { return tokens.join(''); } -export function padString({ str, padRatio = 0.3, padChar = ' ' } = {}) { +export function padString({ + str, + padRatio = 0.3, + padChar = ' ', +}: { + str?: string; + padRatio?: number; + padChar?: string; +} = {}) { const normalized = str || ''; const padLen = Math.ceil((normalized.length * padRatio) / 2); - const padding = []; + const padding: string[] = []; for (let i = 0; i < padLen; i += 1) { padding.push(padCharacters[i % padCharacters.length]); } @@ -97,9 +111,13 @@ export function padString({ str, padRatio = 0.3, padChar = ' ' } = {}) { } const escapeRegExp = /'.*?'/; -export default function toPseudoString({ str, padRatio, padChar }) { +export default function toPseudoString({ + str, + padRatio, + padChar, +}: ToPseudoStringParams) { let input = `${str}`; - const tokens = []; + const tokens: string[] = []; let match = escapeRegExp.exec(input); while (match) { tokens.push(processVars(input.substring(0, match.index))); diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 66931b0d17..7ebf6ff8b0 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@ringcentral-integration/i18n", - "version": "2.2.0", + "version": "2.2.1", "private": false, "description": "A simple I18n implementation used in RingCentral Integration projects.", "homepage": "https://github.com/ringcentral/ringcentral-js-widgets#readme", @@ -13,9 +13,8 @@ }, "license": "MIT", "author": "RingCentral Integrations", - "main": "index.js", "scripts": { - "build": "yarn gulp build", + "build": "tsc", "gulp": "gulp --require @ringcentral-integration/babel-settings/lib/register.js", "release": "yarn gulp release", "test": "yarn run-test" @@ -23,12 +22,12 @@ "devDependencies": { "@faker-js/faker": "^5.5.3", "@ringcentral-integration/babel-settings": "*", - "babel-jest": "^27.4.6", + "babel-jest": "^29.7.0", "fs-extra": "^10.1.0", "gulp": "^4.0.2", "gulp-babel": "^8.0.0", "gulp-sourcemaps": "^2.6.5", - "jest": "^27.4.7", + "jest": "^29.7.0", "yargs": "^17.1.1" }, "peerDependencies": { @@ -37,5 +36,10 @@ "common": true, "ci": { "@ringcentral-integration/i18n": "**" + }, + "nx": { + "tags": [ + "scope:ci-group-1" + ] } } diff --git a/packages/i18n/tsconfig.json b/packages/i18n/tsconfig.json new file mode 100644 index 0000000000..67070240fe --- /dev/null +++ b/packages/i18n/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "include": ["./**/*.ts", "./**/*.tsx", "./typings/**/*.d.ts"], + "exclude": ["./**/*.test.ts"], + "compilerOptions": { + "outDir": "../../build/i18n", + "noEmit": false, + "sourceMap": true + } +} diff --git a/packages/locale-loader/gulpfile.js b/packages/locale-loader/gulpfile.js index 317446c6ba..982113cfd8 100644 --- a/packages/locale-loader/gulpfile.js +++ b/packages/locale-loader/gulpfile.js @@ -1,9 +1,9 @@ -import path from 'path'; import cp from 'child_process'; -import gulp from 'gulp'; import fs from 'fs-extra'; +import gulp from 'gulp'; import babel from 'gulp-babel'; import sourcemaps from 'gulp-sourcemaps'; +import path from 'path'; const BUILD_PATH = path.resolve(__dirname, '../../build/locale-loader'); diff --git a/packages/locale-loader/index.js b/packages/locale-loader/index.js index eceef381a3..3918c9af83 100644 --- a/packages/locale-loader/index.js +++ b/packages/locale-loader/index.js @@ -1,8 +1,7 @@ -import localeLoader from './lib/localeLoader'; - +import consolidateLocale from './lib/consolidateLocale'; import exportLocale from './lib/exportLocale'; import importLocale from './lib/importLocale'; -import consolidateLocale from './lib/consolidateLocale'; +import localeLoader from './lib/localeLoader'; import transformLoader from './lib/transformLoader'; export { diff --git a/packages/locale-loader/lib/compileLocaleData/index.js b/packages/locale-loader/lib/compileLocaleData/index.js index b7c1e22aac..9464cf75ac 100644 --- a/packages/locale-loader/lib/compileLocaleData/index.js +++ b/packages/locale-loader/lib/compileLocaleData/index.js @@ -1,9 +1,10 @@ -import path from 'path'; +import formatLocale from '@ringcentral-integration/i18n/lib/formatLocale'; import fs from 'fs-extra'; +import path from 'path'; import { filter, reduce } from 'ramda'; -import formatLocale from '@ringcentral-integration/i18n/lib/formatLocale'; -import isLocaleFile from '../isLocaleFile'; + import findLoaderFiles from '../findLoaderFiles'; +import isLocaleFile from '../isLocaleFile'; import parseLocaleFile from '../parseLocaleFile'; export function findLocaleFiles(folderPath) { diff --git a/packages/locale-loader/lib/consolidateLocale/index.js b/packages/locale-loader/lib/consolidateLocale/index.js index ae0d98a525..5483d0e635 100644 --- a/packages/locale-loader/lib/consolidateLocale/index.js +++ b/packages/locale-loader/lib/consolidateLocale/index.js @@ -1,5 +1,5 @@ -import importLocale from '../importLocale'; import defaultConfig from '../defaultConfig'; +import importLocale from '../importLocale'; export default async function consolidateLocale({ sourceFolder = defaultConfig.sourceFolder, diff --git a/packages/locale-loader/lib/exportLocale/index.js b/packages/locale-loader/lib/exportLocale/index.js index 2780a2167a..48d7862b86 100644 --- a/packages/locale-loader/lib/exportLocale/index.js +++ b/packages/locale-loader/lib/exportLocale/index.js @@ -1,9 +1,10 @@ -import path from 'path'; import fs from 'fs-extra'; +import path from 'path'; import { forEach } from 'ramda'; + import compileLocaleData from '../compileLocaleData'; import defaultConfig from '../defaultConfig'; -import { generateXlfData, generateJsonData } from '../generateData'; +import { generateJsonData, generateXlfData } from '../generateData'; export function writeData({ localizationFolder, data, ext }) { fs.ensureDirSync(localizationFolder); @@ -16,6 +17,26 @@ export function writeData({ localizationFolder, data, ext }) { }, Object.keys(data)); } +/** + * Exports the locale data based on the provided options. + * + * by default, it will export the data in `xlf` format. + * + * if the --json flag is provided, it will export the data in `json` format. + * + * @param {Object} options - The options for exporting the locale data. + * @param {string} [options.sourceFolder] - The source folder path. + * @param {string} [options.localizationFolder] - The localization folder path. + * @param {string} [options.sourceLocale] - The source locale. + * @param {*} options.supportedLocales - The supported locales. + * @param {*} [options.translationLocales] - The translation locales. + * @param {string} [options.exportType='diff'] - The export type. + * @param {boolean} [options.fillEmptyWithSource=true] - Whether to fill empty translations with the source text. + * @param {boolean} [options.json] - Whether to export the data in JSON format. + * @param {boolean} [options.writeFile=true] - Whether to write the data to a file. + * @returns {Promise} - The exported data or a promise that resolves with the exported data. + * @throws {Error} - If options.supportedLocales is missing. + */ export default function exportLocale({ sourceFolder = defaultConfig.sourceFolder, localizationFolder = defaultConfig.localizationFolder, @@ -24,7 +45,7 @@ export default function exportLocale({ translationLocales = supportedLocales, exportType = 'diff', fillEmptyWithSource = true, - json = false, + json = process.argv.includes('--json'), writeFile = true, } = {}) { if (!supportedLocales) { diff --git a/packages/locale-loader/lib/exportLocale/index.test.js b/packages/locale-loader/lib/exportLocale/index.test.js index 46ffac62d1..0ed1a603a0 100644 --- a/packages/locale-loader/lib/exportLocale/index.test.js +++ b/packages/locale-loader/lib/exportLocale/index.test.js @@ -1,6 +1,7 @@ -import path from 'path'; import fs from 'fs-extra'; -import exportLocale from '.'; +import path from 'path'; + +import exportLocale from './'; const sourceFolder = './testData/exportLocale'; const localizationFolder = './localization/exportLocale'; diff --git a/packages/locale-loader/lib/extractAnnotations/index.test.js b/packages/locale-loader/lib/extractAnnotations/index.test.js index c8ed856a63..ca7a25e9c2 100644 --- a/packages/locale-loader/lib/extractAnnotations/index.test.js +++ b/packages/locale-loader/lib/extractAnnotations/index.test.js @@ -1,6 +1,6 @@ import dedent from 'dedent'; -import { transformSync } from '@babel/core'; -import extractAnnotations from '.'; + +import extractAnnotations from './'; describe('extractAnnotations', () => { const sampleContent = dedent` diff --git a/packages/locale-loader/lib/findLoaderFiles/index.js b/packages/locale-loader/lib/findLoaderFiles/index.js index e1fda4cace..c608eb8303 100644 --- a/packages/locale-loader/lib/findLoaderFiles/index.js +++ b/packages/locale-loader/lib/findLoaderFiles/index.js @@ -1,6 +1,7 @@ import fs from 'fs-extra'; import glob from 'glob'; import { filter } from 'ramda'; + import isLoaderFile from '../isLoaderFile'; /** diff --git a/packages/locale-loader/lib/findLoaderFiles/index.test.js b/packages/locale-loader/lib/findLoaderFiles/index.test.js index 754cf3547c..c7aac5ae28 100644 --- a/packages/locale-loader/lib/findLoaderFiles/index.test.js +++ b/packages/locale-loader/lib/findLoaderFiles/index.test.js @@ -1,6 +1,7 @@ -import path from 'path'; import fs from 'fs-extra'; -import findLoaderFiles from '.'; +import path from 'path'; + +import findLoaderFiles from './'; const sourceFolder = './testData/getLoaderFiles'; const loaderContent = '/* loadLocale */'; diff --git a/packages/locale-loader/lib/generateData/index.js b/packages/locale-loader/lib/generateData/index.js index 34911a2d4a..5461e416c4 100644 --- a/packages/locale-loader/lib/generateData/index.js +++ b/packages/locale-loader/lib/generateData/index.js @@ -1,6 +1,6 @@ import path from 'path'; +import { forEach, reduce } from 'ramda'; import xml from 'xml-js'; -import { reduce, forEach } from 'ramda'; function generateBaseData(allLocales) { return reduce( diff --git a/packages/locale-loader/lib/generateLoaderContent/index.js b/packages/locale-loader/lib/generateLoaderContent/index.js index a4f3c87bd8..1073c01115 100644 --- a/packages/locale-loader/lib/generateLoaderContent/index.js +++ b/packages/locale-loader/lib/generateLoaderContent/index.js @@ -1,22 +1,14 @@ -import dedent from 'dedent'; import formatLocale from '@ringcentral-integration/i18n/lib/formatLocale'; +import dedent from 'dedent'; function getBaseName(f) { return f.replace(/\.(js|json|ts)$/i, ''); } function returnLoadLocaleCode(chunk, locale, basename) { - const padding = chunk ? ' ' : ' '; - let code = ` - ${padding}const data = require('./${basename}'); - ${padding}return resolve(data.__esModule === true ? data.default : data);`; + let code = `require('./${basename}')`; if (chunk) { - code = ` - if (typeof require.ensure === 'function') { - return require.ensure('./${basename}', (require) => {${code} - }, '${locale}'); - } else {${code} - }`; + return `import(/* webpackChunkName: "${locale}" */'./${basename}')`; } return code; } @@ -66,21 +58,14 @@ export default function generateLoaderContent( let langDefaultCase = ''; if (!usedLang[lang]) { usedLang[lang] = true; - langDefaultCase = ` - case '${lang}': - `; + langDefaultCase = `locale==='${lang}'||`; } return `${langDefaultCase} - case '${locale}': {${returnCode} - }`; + locale==='${locale}'? ${returnCode}:`; }); - return dedent`export default function loadLocale(locale) { - return new Promise((resolve) => { - switch (locale) {${cases.join('')} - default: - return resolve(null); - } - }); + const value = dedent`export default function loadLocale(locale) { + return ${cases.join('')}null; }\n`; + return value; } diff --git a/packages/locale-loader/lib/generateLoaderContent/index.test.js b/packages/locale-loader/lib/generateLoaderContent/index.test.js index 4f3673a601..35f4b14c80 100644 --- a/packages/locale-loader/lib/generateLoaderContent/index.test.js +++ b/packages/locale-loader/lib/generateLoaderContent/index.test.js @@ -1,8 +1,9 @@ -import path from 'path'; import { transform } from '@babel/core'; import formatLocale from '@ringcentral-integration/i18n/lib/formatLocale'; import fs from 'fs-extra'; -import generateLoaderContent from '.'; +import path from 'path'; + +import generateLoaderContent from './'; const files = ['en_us.js', 'FR-FR.JS', 'aa-AAAA-ZZ.JS']; @@ -29,7 +30,7 @@ describe('generateLoaderContent', () => { expect(content.indexOf(locale) !== -1).toBe(true); }); test(`should contain case '${lang}': `, () => { - expect(content.indexOf(`case '${lang}':`) > -1).toBe(true); + expect(content).toMatchSnapshot(); }); }); test('should be valid js file content', () => { diff --git a/packages/locale-loader/lib/importLocale/index.js b/packages/locale-loader/lib/importLocale/index.js index 8d9d4315c7..82cf73a61f 100644 --- a/packages/locale-loader/lib/importLocale/index.js +++ b/packages/locale-loader/lib/importLocale/index.js @@ -1,11 +1,13 @@ -import path from 'path'; +/* eslint-disable no-inner-declarations */ import generate from '@babel/generator'; import { parse } from '@babel/parser'; import formatLocale from '@ringcentral-integration/i18n/lib/formatLocale'; import chalk from 'chalk'; import fs from 'fs-extra'; import inquirer from 'inquirer'; -import { filter, find, forEach, reduce } from 'ramda'; +import path from 'path'; +import prettier from 'prettier'; +import { forEach, reduce } from 'ramda'; import asyncForEach from '../asyncForEach'; import asyncReduce from '../asyncReduce'; @@ -48,7 +50,17 @@ function writeFiles({ const { code } = generate(targetData.ast); const annotations = getAnnotations(targetData.annotations); - const output = `${eslint}${code}\n\n${annotations}\n`; + const output = prettier.format(`${eslint}${code}\n\n${annotations}\n`, { + parser: 'typescript', + // this copy from integration basic prettier config + // TODO: read prettier config by user's project + bracketSpacing: true, + singleQuote: true, + trailingComma: 'all', + arrowParens: 'always', + bracketSameLine: false, + endOfLine: 'auto', + }); fs.writeFileSync( path.resolve(sourceFolder, folderPath, targetData.file), output, @@ -148,15 +160,17 @@ async function mergeTranslationData({ await asyncForEach(async (fileName) => { const filePath = path.resolve(sourceFolder, fileName); const folderPath = path.dirname(filePath); + const sourceLocaleFile = + localeData[folderPath] && localeData[folderPath].files[sourceLocale]; + + if (sourceLocaleFile) { + const sourceData = sourceLocaleFile.data; + + const ext = path.extname(sourceLocaleFile.file) || '.ts'; - if ( - localeData[folderPath] && - localeData[folderPath].files[sourceLocale] - ) { - const sourceData = localeData[folderPath].files[sourceLocale].data; if (!localeData[folderPath].files[locale]) { localeData[folderPath].files[locale] = { - file: `${formatLocale(locale)}.js`, + file: `${formatLocale(locale)}${ext}`, }; } if (!localeData[folderPath].files[locale].data) { @@ -227,37 +241,53 @@ async function mergeTranslationData({ if (locale !== sourceLocale) { const targetData = localeData[folderPath].files[locale]; const sourceData = localeData[folderPath].files[sourceLocale]; - targetData.ast = parse(sourceData.content, { sourceType: 'module' }); + targetData.ast = parse(sourceData.content, { + sourceType: 'module', + plugins: ['typescript'], + }); targetData.annotations = new Map(); - const defaultExport = find( + function getData(source) { + const properties = source.properties.filter((prop) => { + const wrapInBracket = + prop.key.type === 'MemberExpression' || + prop.key.type === 'TemplateLiteral'; + const key = wrapInBracket + ? `[${generate(prop.key).code}]` + : generate(prop.key).code; + const entry = targetData.data.get(key); + if (entry && entry.value) { + prop.value = { + type: 'StringLiteral', + value: entry.value, + extra: { + // generate desired raw to by pass babel jsesc use + raw: JSON.stringify(entry.value), + rawValue: entry.value, + }, + }; + targetData.annotations.set(key, sourceData.data.get(key).value); + return true; + } + return false; + }); + source.properties = properties; + } + + const defaultExport = targetData.ast.program.body.find( (item) => item.type === 'ExportDefaultDeclaration', - targetData.ast.program.body, ); - const properties = filter((prop) => { - const wrapInBracket = - prop.key.type === 'MemberExpression' || - prop.key.type === 'TemplateLiteral'; - const key = wrapInBracket - ? `[${generate(prop.key).code}]` - : generate(prop.key).code; - const entry = targetData.data.get(key); - if (entry && entry.value) { - prop.value = { - type: 'StringLiteral', - value: entry.value, - extra: { - // generate desired raw to by pass babel jsesc use - raw: JSON.stringify(entry.value), - rawValue: entry.value, - }, - }; - targetData.annotations.set(key, sourceData.data.get(key).value); - return true; + + if (defaultExport) { + if (defaultExport.declaration.type === 'ObjectExpression') { + getData(defaultExport.declaration); + } else if (defaultExport.declaration.type === 'TSAsExpression') { + const nest = defaultExport.declaration.expression; + if (nest.type === 'ObjectExpression') { + getData(nest); + } } - return false; - }, defaultExport.declaration.properties); - defaultExport.declaration.properties = properties; + } } }, Object.keys(localeData[folderPath].files)); }, Object.keys(localeData)); @@ -274,7 +304,7 @@ export default async function importLocale({ silent = defaultConfig.silent, json = false, disableEslint = true, - rawData, + rawData = undefined, } = {}) { if (!supportedLocales) { throw new Error('options.supportedLocales is missing'); diff --git a/packages/locale-loader/lib/importLocale/index.test.js b/packages/locale-loader/lib/importLocale/js.test.js similarity index 99% rename from packages/locale-loader/lib/importLocale/index.test.js rename to packages/locale-loader/lib/importLocale/js.test.js index 8045f2ca09..cad3c2542c 100644 --- a/packages/locale-loader/lib/importLocale/index.test.js +++ b/packages/locale-loader/lib/importLocale/js.test.js @@ -1,8 +1,9 @@ -import path from 'path'; -import fs from 'fs-extra'; +import importLocale from '.'; import { transformSync } from '@babel/core'; +import fs from 'fs-extra'; +import path from 'path'; + import exportLocale from '../exportLocale'; -import importLocale from '.'; const sourceFolder = './testData/importLocale'; const localizationFolder = './localization/importLocale'; @@ -41,7 +42,7 @@ async function generateSource() { world\`, 419: 'number as key', [\`\${obj.key}-copy\`]: 'template as key', - }; + } as const; `, ); } @@ -198,7 +199,7 @@ describe('importLocale', () => { [obj.key]: 'testValue', 'single-quote': 'Single Quote', "double-'quote'": "Double Quote", - }; + } as const; `, ); await importLocale({ @@ -239,7 +240,7 @@ describe('importLocale', () => { concat: 'item1' + 'item2', template: \`hello world\`, - }; + } as const; `, ); await importLocale(config); diff --git a/packages/locale-loader/lib/importLocale/ts.test.js b/packages/locale-loader/lib/importLocale/ts.test.js new file mode 100644 index 0000000000..d345ee38ec --- /dev/null +++ b/packages/locale-loader/lib/importLocale/ts.test.js @@ -0,0 +1,287 @@ +import importLocale from '.'; +import { transformSync } from '@babel/core'; +import fs from 'fs-extra'; +import path from 'path'; + +import exportLocale from '../exportLocale'; + +const sourceFolder = './testData/importLocale2'; +const localizationFolder = './localization/importLocale2'; + +async function clean() { + await fs.emptyDir(sourceFolder); + await fs.emptyDir(localizationFolder); +} + +function encodeValue(str) { + return `@#@${JSON.stringify(str)}@#@`; +} + +async function generateSource() { + await fs.ensureDir(sourceFolder); + await fs.writeFile( + path.resolve(sourceFolder, 'loadLocale.ts'), + '/* loadLocale */', + ); + await fs.writeFile( + path.resolve(sourceFolder, 'en-US.ts'), + ` + const obj = { + key: 'testKey', + }; + + export default { + modern: 'rogue', + whisky: 'Vault', + [obj.key]: 'testValue', + newline: 'containes\\nnewline', + 'single-quote': 'Single Quote', + "double-'quote'": "Double Quote", + concat: 'item1' + 'item2', + template: \`hello + world\`, + 419: 'number as key', + [\`\${obj.key}-copy\`]: 'template as key', + } as const; + `, + ); +} + +describe('importLocale', () => { + const config = { + localizationFolder, + sourceFolder, + supportedLocales: ['en-US', 'en-GB'], + silent: true, + interactive: false, + }; + beforeEach(async () => { + await clean(); + await generateSource(); + exportLocale(config); + }); + afterEach(clean); + test('should throw when supportedLocales is not defined', async () => { + let error; + try { + await importLocale(); + } catch (e) { + error = e; + } + + expect(() => { + throw error; + }).toThrow('options.supportedLocales is missing'); + }); + test('should import generated xlf files', async () => { + const xlfPath = path.resolve(localizationFolder, 'en-GB.xlf'); + const xlfContent = await fs.readFile(xlfPath, 'utf8'); + await fs.writeFile( + xlfPath, + xlfContent + .replace('VaultChanged') + .replace( + 'testValuetestValueChanged', + ), + ); + await importLocale(config); + const filePath = path.resolve(sourceFolder, 'en-GB.ts'); + expect(await fs.exists(filePath)).toBe(true); + const content = await fs.readFile(filePath, 'utf8'); + let json; + expect(() => { + json = eval(transformSync(content, { filename: 'en-GB.ts' }).code); + }).not.toThrow(); + expect(json.modern).toBe('rogue'); + expect(json.whisky).toBe('Changed'); + expect(json.testKey).toBe('testValueChanged'); + expect(json.concat).toBe('item1item2'); + expect(json.template).toBe(`hello + world`); + expect(json[419]).toBe('number as key'); + expect(json['testKey-copy']).toBe('template as key'); + }); + test('should generate annotations', async () => { + await importLocale(config); + const filePath = path.resolve(sourceFolder, 'en-GB.ts'); + expect(await fs.exists(filePath)).toBe(true); + const content = await fs.readFile(filePath, 'utf8'); + expect( + content.indexOf( + `// @key: ${encodeValue('modern')} @source: ${encodeValue('rogue')}`, + ) > -1, + ).toBe(true); + expect( + content.indexOf( + `// @key: ${encodeValue('whisky')} @source: ${encodeValue('Vault')}`, + ) > -1, + ).toBe(true); + expect( + content.indexOf( + `// @key: ${encodeValue('[obj.key]')} @source: ${encodeValue( + 'testValue', + )}`, + ) > -1, + ).toBe(true); + expect( + content.indexOf( + `// @key: ${encodeValue('newline')} @source: ${encodeValue( + 'containes\nnewline', + )}`, + ) > -1, + ).toBe(true); + }); + + test('should only import keys where its source value is identical to current source', async () => { + const xlfPath = path.resolve(localizationFolder, 'en-GB.xlf'); + const xlfContent = await fs.readFile(xlfPath, 'utf8'); + await fs.writeFile( + xlfPath, + xlfContent + .replace('VaultChanged') + .replace( + 'testValuetestValueChanged', + ), + ); + await importLocale(config); + const filePath = path.resolve(sourceFolder, 'en-GB.ts'); + expect(await fs.exists(filePath)).toBe(true); + const content = await fs.readFile(filePath, 'utf8'); + let json; + expect(() => { + json = eval(transformSync(content, { filename: 'en-GB.ts' }).code); + }).not.toThrow(); + expect(json.modern).toBe('rogue'); + expect(json.whisky).toBe(undefined); + expect(json.testKey).toBe(undefined); + }); + + test('should skip keys that no longer exist', async () => { + const xlfPath = path.resolve(localizationFolder, 'en-GB.xlf'); + const xlfContent = await fs.readFile(xlfPath, 'utf8'); + await fs.writeFile( + xlfPath, + xlfContent + .replace('VaultChanged') + .replace( + 'testValuetestValueChanged', + ), + ); + await importLocale(config); + const filePath = path.resolve(sourceFolder, 'en-GB.ts'); + expect(await fs.exists(filePath)).toBe(true); + const content = await fs.readFile(filePath, 'utf8'); + let json; + expect(() => { + json = eval(transformSync(content, { filename: 'en-GB.ts' }).code); + }).not.toThrow(); + expect(json.modern).toBe('rogue'); + expect(json.whisky).toBe(undefined); + expect(json.testKey).toBe(undefined); + }); + + test('should remove keys that no longer exist in source', async () => { + await importLocale(config); + const xlfPath = path.resolve(localizationFolder, 'en-GB.xlf'); + await fs.remove(xlfPath); + await fs.writeFile( + path.resolve(sourceFolder, 'en-US.ts'), + ` + const obj = { + key: 'testKey', + }; + + export default { + whisky: 'Vault', + [obj.key]: 'testValue', + 'single-quote': 'Single Quote', + "double-'quote'": "Double Quote", + } as const; + `, + ); + await importLocale({ + ...config, + }); + + const filePath = path.resolve(sourceFolder, 'en-GB.ts'); + expect(await fs.exists(filePath)).toBe(true); + const content = await fs.readFile(filePath, 'utf8'); + let json; + expect(() => { + json = eval(transformSync(content, { filename: 'en-GB.ts' }).code); + }).not.toThrow(); + + expect(json.whisky).toBe('Vault'); + expect(json.modern).toBe(undefined); + expect(json.newline).toBe(undefined); + }); + + test('should remove keys that the source value has changed', async () => { + await importLocale(config); + const xlfPath = path.resolve(localizationFolder, 'en-GB.xlf'); + await fs.remove(xlfPath); + await fs.writeFile( + path.resolve(sourceFolder, 'en-US.ts'), + ` + const obj = { + key: 'testKey', + }; + + export default { + modern: 'changed', + whisky: 'Vault', + [obj.key]: 'testValue', + newline: 'alsoChanged', + 'single-quote': 'Single Quote', + "double-'quote'": "Double Quote", + concat: 'item1' + 'item2', + template: \`hello + world\`, + } as const; + `, + ); + await importLocale(config); + + const filePath = path.resolve(sourceFolder, 'en-GB.ts'); + expect(await fs.exists(filePath)).toBe(true); + const content = await fs.readFile(filePath, 'utf8'); + let json; + expect(() => { + json = eval(transformSync(content, { filename: 'en-GB.ts' }).code); + }).not.toThrow(); + expect(json.modern).toBe(undefined); + expect(json.newline).toBe(undefined); + }); + + test('it should work for files without trailing comma', async () => { + await fs.writeFile( + path.resolve(sourceFolder, 'en-US.ts'), + ` + const obj = { + key: 'testKey', + }; + + export default { + modern: 'rogue', + whisky: 'Vault', + [obj.key]: 'testValue', + newline: 'containes\\nnewline', + 'single-quote': 'Single Quote', + "double-'quote'": "Double Quote", + newKey: 'newKey' + }; + `, + ); + exportLocale(config); + await importLocale(config); + const filePath = path.resolve(sourceFolder, 'en-GB.ts'); + expect(await fs.exists(filePath)).toBe(true); + const content = await fs.readFile(filePath, 'utf8'); + expect(() => { + eval(transformSync(content, { filename: 'en-GB.ts' }).code); + }).not.toThrow(); + }); +}); diff --git a/packages/locale-loader/lib/isLoaderFile/index.test.js b/packages/locale-loader/lib/isLoaderFile/index.test.js index c55ff76a98..e5e7634fca 100644 --- a/packages/locale-loader/lib/isLoaderFile/index.test.js +++ b/packages/locale-loader/lib/isLoaderFile/index.test.js @@ -1,5 +1,6 @@ import { forEach } from 'ramda'; -import isLoaderFile from '.'; + +import isLoaderFile from './'; const validFiles = [ '/* loadLocale */', diff --git a/packages/locale-loader/lib/isLocaleFile/index.test.js b/packages/locale-loader/lib/isLocaleFile/index.test.js index 4f43ff89b6..01f045a766 100644 --- a/packages/locale-loader/lib/isLocaleFile/index.test.js +++ b/packages/locale-loader/lib/isLocaleFile/index.test.js @@ -1,5 +1,6 @@ import { forEach } from 'ramda'; -import isLocaleFile from '.'; + +import isLocaleFile from './'; describe('isLocaleFile', () => { describe('Valid file names:', () => { diff --git a/packages/locale-loader/lib/localeLoader/index.js b/packages/locale-loader/lib/localeLoader/index.js index 0fb7552c68..8e3352fe3c 100644 --- a/packages/locale-loader/lib/localeLoader/index.js +++ b/packages/locale-loader/lib/localeLoader/index.js @@ -1,8 +1,9 @@ import fs from 'fs-extra'; import loaderUtils from 'loader-utils'; + import generateLoaderContent from '../generateLoaderContent'; -import isLocaleFile, { localeFilter } from '../isLocaleFile'; import isLoaderFile, { noChunks } from '../isLoaderFile'; +import isLocaleFile, { localeFilter } from '../isLocaleFile'; /** * diff --git a/packages/locale-loader/lib/localeLoader/index.test.js b/packages/locale-loader/lib/localeLoader/index.test.js index f2198707fe..7eb43a582b 100644 --- a/packages/locale-loader/lib/localeLoader/index.test.js +++ b/packages/locale-loader/lib/localeLoader/index.test.js @@ -1,12 +1,7 @@ -import path from 'path'; -import fs from 'fs-extra'; -import formatLocale from '@ringcentral-integration/i18n/lib/formatLocale'; -import localeLoader from '.'; +import localeLoader from './'; const testFolder = './testData'; -const files = ['en_us.js', 'FR-FR.JS', 'aa-AAAA-ZZ.JS']; - class MockBuilder { constructor({ input }) { this.input = input; diff --git a/packages/locale-loader/lib/parseLocaleFile/index.js b/packages/locale-loader/lib/parseLocaleFile/index.js index 202cb26e7b..99a96fd377 100644 --- a/packages/locale-loader/lib/parseLocaleFile/index.js +++ b/packages/locale-loader/lib/parseLocaleFile/index.js @@ -1,19 +1,16 @@ -import { find, forEach } from 'ramda'; -import { parse } from '@babel/parser'; import generate from '@babel/generator'; +import { parse } from '@babel/parser'; +import { find, forEach } from 'ramda'; + import extractAnnotations from '../extractAnnotations'; /* eslint { no-eval: 0 } */ export default function parseLocaleFile(rawContent) { const data = new Map(); const { content, annotations } = extractAnnotations(rawContent); - const ast = parse(content, { sourceType: 'module' }); + const ast = parse(content, { sourceType: 'module', plugins: ['typescript'] }); - const defaultExport = find( - (item) => item.type === 'ExportDefaultDeclaration', - ast.program.body, - ); - if (defaultExport && defaultExport.declaration.type === 'ObjectExpression') { + function getData(properties) { forEach((prop) => { // get raw key from source content let key = content.substring(prop.key.start, prop.key.end); @@ -34,7 +31,23 @@ export default function parseLocaleFile(rawContent) { value, source, }); - }, defaultExport.declaration.properties); + }, properties); + } + + const defaultExport = find( + (item) => item.type === 'ExportDefaultDeclaration', + ast.program.body, + ); + + if (defaultExport) { + if (defaultExport.declaration.type === 'ObjectExpression') { + getData(defaultExport.declaration.properties); + } else if (defaultExport.declaration.type === 'TSAsExpression') { + const nest = defaultExport.declaration.expression; + if (nest.type === 'ObjectExpression') { + getData(nest.properties); + } + } } return { diff --git a/packages/locale-loader/lib/parseLocaleFile/index.test.js b/packages/locale-loader/lib/parseLocaleFile/index.test.js index 65ba5b2993..bf818fbffa 100644 --- a/packages/locale-loader/lib/parseLocaleFile/index.test.js +++ b/packages/locale-loader/lib/parseLocaleFile/index.test.js @@ -1,6 +1,8 @@ import dedent from 'dedent'; + import extractAnnotations from '../extractAnnotations'; -import parseLocaleFile from '.'; + +import parseLocaleFile from './'; describe('parseLocaleFile', () => { const source = dedent` @@ -54,5 +56,28 @@ describe('parseLocaleFile', () => { test('should contain annotations', () => { expect(parsedData.annotations).toEqual(annotations); }); + test('should also work with ts', () => { + expect( + parseLocaleFile(dedent` + export default { + whisky: 'vault', + modern: 'rogue', + } as const; + `).data, + ).toMatchInlineSnapshot(` + Map { + "whisky" => { + "key": "whisky", + "source": undefined, + "value": "vault", + }, + "modern" => { + "key": "modern", + "source": undefined, + "value": "rogue", + }, + } + `); + }); }); }); diff --git a/packages/locale-loader/lib/readJsonData.js b/packages/locale-loader/lib/readJsonData.js index 239ea4779d..de43cfca89 100644 --- a/packages/locale-loader/lib/readJsonData.js +++ b/packages/locale-loader/lib/readJsonData.js @@ -1,5 +1,5 @@ -import path from 'path'; import fs from 'fs-extra'; +import path from 'path'; import { reduce } from 'ramda'; export function readJsonData({ diff --git a/packages/locale-loader/lib/readXlfData/index.js b/packages/locale-loader/lib/readXlfData/index.js index 720b994a02..9dc2b4ff7e 100644 --- a/packages/locale-loader/lib/readXlfData/index.js +++ b/packages/locale-loader/lib/readXlfData/index.js @@ -1,7 +1,7 @@ -import path from 'path'; -import xml from 'xml-js'; import fs from 'fs-extra'; +import path from 'path'; import { reduce } from 'ramda'; +import xml from 'xml-js'; function extractKey(str) { return str.substring(1, str.length - 1); diff --git a/packages/locale-loader/lib/transformLoader/index.js b/packages/locale-loader/lib/transformLoader/index.js index 5e46cad68d..d91f9512bf 100644 --- a/packages/locale-loader/lib/transformLoader/index.js +++ b/packages/locale-loader/lib/transformLoader/index.js @@ -1,9 +1,10 @@ +import fs from 'fs-extra'; import path from 'path'; import through from 'through2'; -import fs from 'fs-extra'; + import generateLoaderContent from '../generateLoaderContent'; -import isLocaleFile from '../isLocaleFile'; import isLoaderFile, { noChunks } from '../isLoaderFile'; +import isLocaleFile from '../isLocaleFile'; /** * - `supportedLocales` to support locales diff --git a/packages/locale-loader/lib/transformLoader/index.test.js b/packages/locale-loader/lib/transformLoader/index.test.js index b41096641b..fbe6faa41c 100644 --- a/packages/locale-loader/lib/transformLoader/index.test.js +++ b/packages/locale-loader/lib/transformLoader/index.test.js @@ -1,7 +1,8 @@ -import path from 'path'; -import gulp from 'gulp'; import fs from 'fs-extra'; -import transformLocaleLoader from '.'; +import gulp from 'gulp'; +import path from 'path'; + +import transformLocaleLoader from './'; const sourceFolder = './testData-transformLocaleLoader'; diff --git a/packages/locale-loader/package.json b/packages/locale-loader/package.json index 7233014c98..3f45448568 100644 --- a/packages/locale-loader/package.json +++ b/packages/locale-loader/package.json @@ -1,6 +1,6 @@ { "name": "@ringcentral-integration/locale-loader", - "version": "2.2.1", + "version": "2.2.5", "description": "Locale Loader for webpack", "homepage": "https://github.com/ringcentral/ringcentral-js-widgets#readme", "bugs": { @@ -23,7 +23,7 @@ "dependencies": { "@babel/generator": "^7.11.4", "@babel/parser": "^7.11.4", - "chalk": "^2.4.1", + "chalk": "^4.1.2", "dedent": "^0.7.0", "fs-extra": "^10.1.0", "glob": "^7.1.2", @@ -34,20 +34,25 @@ "xml-js": "^1.6.2" }, "devDependencies": { - "@babel/core": "^7.20.12", + "@babel/core": "^7.24.0", "@ringcentral-integration/babel-settings": "*", - "babel-jest": "^27.4.6", + "babel-jest": "^29.7.0", "gulp": "^4.0.2", "gulp-babel": "^8.0.0", "gulp-sourcemaps": "^2.6.5", - "jest": "^27.4.7", - "jest-html-reporters": "^3.0.8" + "jest": "^29.7.0", + "jest-html-reporters": "^3.1.7" }, "peerDependencies": { - "@ringcentral-integration/i18n": "^2.0.0" + "@ringcentral-integration/i18n": "^2.2.1" }, "common": true, "ci": { "@ringcentral-integration/locale-loader": "**" + }, + "nx": { + "tags": [ + "scope:ci-group-1" + ] } } diff --git a/packages/locale-settings/index.d.ts b/packages/locale-settings/index.d.ts index ec00b658f5..a3567683f1 100644 --- a/packages/locale-settings/index.d.ts +++ b/packages/locale-settings/index.d.ts @@ -39,7 +39,7 @@ export const translationLocales: readonly [ 'fi-FI', ]; -export type SupportedLocales = typeof supportedLocales[number]; +export type SupportedLocales = (typeof supportedLocales)[number]; export type SupportedLanguages = typeof supportedLocales extends { [index: number]: `${infer E}-${string}`; } diff --git a/packages/locale-settings/package.json b/packages/locale-settings/package.json index 3cf0e3beda..376631f703 100644 --- a/packages/locale-settings/package.json +++ b/packages/locale-settings/package.json @@ -7,5 +7,10 @@ "main": "index.js", "ci": { "ringcentral-widgets-test": "**" + }, + "nx": { + "tags": [ + "scope:ci-group-1" + ] } } diff --git a/packages/phone-number/index.ts b/packages/phone-number/index.ts index 6304dddabe..c6577381af 100644 --- a/packages/phone-number/index.ts +++ b/packages/phone-number/index.ts @@ -1,32 +1,34 @@ import type { CountryCode } from 'libphonenumber-js'; import { - parseIncompletePhoneNumber, - isValidNumber, - getCountryCallingCode, - formatNumber, AsYouType, + formatNumber, + getCountryCallingCode, + isValidNumber, + parseIncompletePhoneNumber, } from 'libphonenumber-js'; -import format, { formatTypes, isUSOrCAOrPR } from './lib/format'; +import { customFormat } from './lib/customFormat/customFormat'; import detect from './lib/detect'; -import parse from './lib/parse'; +import format, { formatTypes, isUSOrCAOrPR } from './lib/format'; import isE164 from './lib/isE164'; import isSameLocalNumber from './lib/isSameLocalNumber'; +import parse from './lib/parse'; export { - format, - isUSOrCAOrPR, + AsYouType, + customFormat, detect, - parse, - isE164, + format, + formatNumber, formatTypes, - AsYouType, + getCountryCallingCode, + isE164, // Legacy isSameLocalNumber, - parseIncompletePhoneNumber, + isUSOrCAOrPR, isValidNumber, - getCountryCallingCode, - formatNumber, + parse, + parseIncompletePhoneNumber, }; export type { CountryCode }; diff --git a/packages/phone-number/lib/customFormat/customFormat.interface.ts b/packages/phone-number/lib/customFormat/customFormat.interface.ts new file mode 100644 index 0000000000..f000c78436 --- /dev/null +++ b/packages/phone-number/lib/customFormat/customFormat.interface.ts @@ -0,0 +1,6 @@ +export interface CustomFormatOptions { + localPhoneNumber: string; + template: string; + templateChar?: string; + strict?: boolean; +} diff --git a/packages/phone-number/lib/customFormat/customFormat.test.ts b/packages/phone-number/lib/customFormat/customFormat.test.ts new file mode 100644 index 0000000000..6b57f2a904 --- /dev/null +++ b/packages/phone-number/lib/customFormat/customFormat.test.ts @@ -0,0 +1,66 @@ +import { customFormat } from './customFormat'; + +describe('customFormat', () => { + it('should format phone numbers with custom format template', () => { + const localUSNumbers = [ + '(650) 555-1234', + '6505551234', + '650.555.1234', + '650-555-1234', + '650 555 1234', + ]; + let template = 'XXX-XXX-XXXX'; + localUSNumbers.forEach((localPhoneNumber) => { + expect(customFormat({ localPhoneNumber, template })).toBe('650-555-1234'); + }); + template = '(XXX) XXX-XXXX'; + localUSNumbers.forEach((localPhoneNumber) => { + expect(customFormat({ localPhoneNumber, template })).toBe( + '(650) 555-1234', + ); + }); + template = 'XXX.XXX.XXXX'; + localUSNumbers.forEach((localPhoneNumber) => { + expect(customFormat({ localPhoneNumber, template })).toBe('650.555.1234'); + }); + template = 'XXX XXX XXXX'; + localUSNumbers.forEach((localPhoneNumber) => { + expect(customFormat({ localPhoneNumber, template })).toBe('650 555 1234'); + }); + }); + it("should throw an error if the number of digits in the phone number doesn't match the number of template characters in strict mode", () => { + const localPhoneNumber = '6505551234'; + const template = 'XXX-XXX-XXX'; + expect(() => + customFormat({ localPhoneNumber, template, strict: true }), + ).toThrow( + `Invalid custom format: 6505551234 => XXX-XXX-XXX, number of digits don't match: 10 !== 9`, + ); + }); + it('should ignore extra digits in non strict mode', () => { + const localPhoneNumber = '6505551234'; + const template = 'XXX-XXX-XXX'; + expect(customFormat({ localPhoneNumber, template })).toBe('650-555-123'); + }); + it('should ignore extra template characters in non strict mode', () => { + const localPhoneNumber = '6505551234'; + const template = 'XXX-XXX-XXXXX'; + expect(customFormat({ localPhoneNumber, template })).toBe('650-555-1234'); + }); + it('should format phone numbers with custom format template and custom template character', () => { + const localUSNumbers = [ + '(650) 555-1234', + '6505551234', + '650.555.1234', + '650-555-1234', + '650 555 1234', + ]; + const template = 'YYY-YYY-YYYY'; + const templateChar = 'Y'; + localUSNumbers.forEach((localPhoneNumber) => { + expect(customFormat({ localPhoneNumber, template, templateChar })).toBe( + '650-555-1234', + ); + }); + }); +}); diff --git a/packages/phone-number/lib/customFormat/customFormat.ts b/packages/phone-number/lib/customFormat/customFormat.ts new file mode 100644 index 0000000000..141ec28787 --- /dev/null +++ b/packages/phone-number/lib/customFormat/customFormat.ts @@ -0,0 +1,56 @@ +import { extractDigits } from '../extractDigits'; + +import { CustomFormatOptions } from './customFormat.interface'; + +/** + * @param localPhoneNumber phone number in local number format + * @param template template to format the phone number + * @param templateChar character to replace with phone number digits + * @param strict if true, will throw an error if the number of digits in the phone number doesn't match the number of template characters + * @returns formatted phone number + * + * @example + * ```ts + * customFormat({ localPhoneNumber: '6505551234', template: 'XXX-XXX-XXXX' }); // '650-555-1234' + * + * customFormat({ localPhoneNumber: '6505551234', template: '(YYY) YYY-YYYY', templateChar: 'Y' }); // '(650) 555-1234' + * + * customFormat({ localPhoneNumber: '6505551234', template: 'XXX-XXX-XXX', strict: true }); // Error: Invalid custom format: 6505551234 => XXX-XXX-XXX, number of digits don't match: 10 !== 9 + * + * customFormat({ localPhoneNumber: '6505551234', template: 'XXX-XXX-XXX'}); // '650-555-123' // ignore extra digits in non strict mode + * + * customFormat({ localPhoneNumber: '6505551234', template: 'XXX-XXX-XXXXX'); // '650-555-1234' // extra X will be ignored in non strict mode + * ``` + */ +export function customFormat({ + localPhoneNumber, + template, + templateChar = 'X', + strict = false, +}: CustomFormatOptions) { + const localDigits = extractDigits(localPhoneNumber).split(''); + const numOfDigits = localDigits.length; + let templateCharCount = 0; + + const result = template + .split('') + .reduce((acc, char) => { + if (char === templateChar) { + templateCharCount += 1; + if (localDigits.length > 0) { + acc.push(localDigits.shift()!); + } + } else { + acc.push(char); + } + return acc; + }, []) + .join(''); + + if (strict && templateCharCount !== numOfDigits) { + throw new Error( + `Invalid custom format: ${localPhoneNumber} => ${template}, number of digits don't match: ${numOfDigits} !== ${templateCharCount}`, + ); + } + return result; +} diff --git a/packages/phone-number/lib/extractDigits.ts b/packages/phone-number/lib/extractDigits.ts new file mode 100644 index 0000000000..2009287a46 --- /dev/null +++ b/packages/phone-number/lib/extractDigits.ts @@ -0,0 +1,5 @@ +const regNonDigits = /[^\d]/g; + +export function extractDigits(str: string) { + return str.replace(regNonDigits, ''); +} diff --git a/packages/phone-number/lib/libphonenumber-js.js b/packages/phone-number/lib/libphonenumber-js.js index 75e3dbc135..2c0400dcab 100644 --- a/packages/phone-number/lib/libphonenumber-js.js +++ b/packages/phone-number/lib/libphonenumber-js.js @@ -1,5 +1,5 @@ /** * Expose the base libphonenumber-js package so projects with specific needs - * can utilize the base funcitons. + * can utilize the base functions. */ export * from 'libphonenumber-js'; diff --git a/packages/phone-number/lib/parse/index.ts b/packages/phone-number/lib/parse/index.ts index 66f8f83816..0572e2220a 100644 --- a/packages/phone-number/lib/parse/index.ts +++ b/packages/phone-number/lib/parse/index.ts @@ -1,7 +1,9 @@ import type { CountryCode, ParsedNumber } from 'libphonenumber-js'; import { parseNumber, AsYouType } from 'libphonenumber-js'; + import cleanNumber from '../cleanNumber'; import extractControls from '../extractControls'; + import type { ParseResult, ParseParam, ProcessParam } from './parse.interface'; const invalidCharsRegExp = /[^\d*+#\-(). ]/; diff --git a/packages/phone-number/package.json b/packages/phone-number/package.json index f224389572..ccdfe1c445 100644 --- a/packages/phone-number/package.json +++ b/packages/phone-number/package.json @@ -27,17 +27,20 @@ }, "devDependencies": { "@ringcentral-integration/babel-settings": "*", - "babel-jest": "^27.4.6", + "babel-jest": "^29.7.0", "fs-extra": "^10.1.0", "gulp": "^4.0.2", "gulp-babel": "^8.0.0", "gulp-sourcemaps": "^2.6.5", - "jest": "^27.4.7", - "jest-html-reporters": "^3.0.8" + "jest": "^29.7.0", + "jest-html-reporters": "^3.1.7" }, "peerDependencies": {}, "common": true, "ci": { "@ringcentral-integration/phone-number": "**" + }, + "nx": { + "tags": ["scope:ci-group-1"] } } diff --git a/packages/react-hooks/.gitignore b/packages/react-hooks/.gitignore new file mode 100644 index 0000000000..3c3629e647 --- /dev/null +++ b/packages/react-hooks/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/packages/react-hooks/LICENSE b/packages/react-hooks/LICENSE new file mode 100644 index 0000000000..c2f1915821 --- /dev/null +++ b/packages/react-hooks/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2024 RingCentral, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/react-hooks/README.md b/packages/react-hooks/README.md new file mode 100644 index 0000000000..9b1c22e63e --- /dev/null +++ b/packages/react-hooks/README.md @@ -0,0 +1,3 @@ +# @ringcentral-integration/react-hooks + +// diff --git a/packages/react-hooks/gulpfile.js b/packages/react-hooks/gulpfile.js new file mode 100644 index 0000000000..b3741a4d4a --- /dev/null +++ b/packages/react-hooks/gulpfile.js @@ -0,0 +1,93 @@ +import cp from 'child_process'; +import fs from 'fs-extra'; +import gulp from 'gulp'; +import path from 'path'; +import yargs from 'yargs'; + +const DEFAULT_BUILD_PATH = path.resolve(__dirname, '../../build/react-hooks'); + +const { argv } = yargs + .alias({ + buildPath: 'build-path', + }) + .default('buildPath', DEFAULT_BUILD_PATH); + +const { buildPath } = argv; + +export function clean() { + return fs.remove(buildPath); +} + +export function compile() { + return exec('yarn build'); +} + +export const build = gulp.series(clean, compile); + +async function exec(command) { + return new Promise((resolve, reject) => { + cp.exec(command, (error, stdout) => { + if (error) { + reject(error); + return; + } + resolve(stdout); + }); + }); +} + +async function getVersionFromTag() { + try { + let tag = await exec( + 'git describe --exact-match --tags $(git rev-parse HEAD)', + ); + tag = tag.replace(/\r?\n|\r/g, ''); + if (/^\d+.\d+.\d+/.test(tag)) { + return tag; + } + return null; + } catch (e) { + return null; + } +} + +const RELEASE_PATH = path.resolve(__dirname, '../../release/react-hooks'); + +export async function releaseClean() { + if (!(await fs.exists(RELEASE_PATH))) { + await fs.mkdirp(RELEASE_PATH); + } + const files = (await fs.readdir(RELEASE_PATH)).filter( + (file) => !/^\./.test(file), + ); + for (const file of files) { + await fs.remove(path.resolve(RELEASE_PATH, file)); + } +} + +export function releaseCopy() { + return gulp + .src([`${buildPath}/**`, `${__dirname}/README.md`, `${__dirname}/LICENSE`]) + .pipe(gulp.dest(RELEASE_PATH)); +} + +export async function generatePackage() { + const packageInfo = JSON.parse( + await fs.readFile(path.resolve(__dirname, 'package.json')), + ); + delete packageInfo.scripts; + delete packageInfo.devDependencies; + const version = await getVersionFromTag(); + if (version) { + packageInfo.version = version; + } + await fs.writeFile( + path.resolve(RELEASE_PATH, 'package.json'), + JSON.stringify(packageInfo, null, 2), + ); +} + +export const release = gulp.series( + gulp.parallel(build, releaseClean), + gulp.parallel(releaseCopy, generatePackage), +); diff --git a/packages/react-hooks/index.ts b/packages/react-hooks/index.ts new file mode 100644 index 0000000000..8420b1093f --- /dev/null +++ b/packages/react-hooks/index.ts @@ -0,0 +1 @@ +export * from './src'; diff --git a/packages/react-hooks/jest.config.js b/packages/react-hooks/jest.config.js new file mode 100644 index 0000000000..d7567e167e --- /dev/null +++ b/packages/react-hooks/jest.config.js @@ -0,0 +1,10 @@ +const merge = require('@ringcentral-integration/test-utils/lib/merge'); +const baseConfig = require('@ringcentral-integration/test-utils/config/next-jest.config'); + +module.exports = merge(baseConfig, { + roots: ['/src'], + setupFiles: [ + '@ringcentral-integration/mock/setup.ts', + '/test/jest.setup.ts', + ], +}); diff --git a/packages/react-hooks/package.json b/packages/react-hooks/package.json new file mode 100644 index 0000000000..6dbbaab268 --- /dev/null +++ b/packages/react-hooks/package.json @@ -0,0 +1,45 @@ +{ + "name": "@ringcentral-integration/react-hooks", + "version": "0.15.0", + "license": "MIT", + "private": false, + "description": "RingCentral integration react hooks library", + "homepage": "https://github.com/ringcentral/ringcentral-js-widgets#readme", + "bugs": { + "url": "https://github.com/ringcentral/ringcentral-js-widgets/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/ringcentral/ringcentral-js-widgets.git" + }, + "author": "RingCentral Integrations", + "scripts": { + "build": "tsc", + "test": "yarn run-test", + "gulp": "gulp --require @ringcentral-integration/babel-settings/lib/register.js", + "release": "yarn gulp release", + "tsc-check": "yarn tsc --project tsconfig.lint.json --noEmit" + }, + "peerDependencies": { + "@ringcentral/juno": "^2.42.0", + "react": "^17.0.2" + }, + "devDependencies": { + "@ringcentral/juno": "^2.42.0", + "@types/hammerjs": "^2.0.41", + "react": "^17.0.2" + }, + "dependencies": { + "copy-to-clipboard": "^3.3.3", + "hammerjs": "^2.0.8", + "react-use": "^17.5.0" + }, + "ci": { + "@ringcentral-integration/react-hooks": "**" + }, + "nx": { + "tags": [ + "scope:ci-group-1" + ] + } +} diff --git a/packages/react-hooks/src/index.ts b/packages/react-hooks/src/index.ts new file mode 100644 index 0000000000..b1e52a6bdc --- /dev/null +++ b/packages/react-hooks/src/index.ts @@ -0,0 +1,4 @@ +export * from './useHammer'; +export * from './usePanMove'; +export * from './useResizeAndToggle'; +export * from './useZoom'; diff --git a/packages/react-hooks/src/useHammer.ts b/packages/react-hooks/src/useHammer.ts new file mode 100644 index 0000000000..c4b0f10332 --- /dev/null +++ b/packages/react-hooks/src/useHammer.ts @@ -0,0 +1,26 @@ +import { getRefElement, RefOrElementOrCallback } from '@ringcentral/juno'; +import { useEffect, useRef } from 'react'; + +export const useHammer = ( + target: RefOrElementOrCallback | EventTarget, + callback: (manager: HammerManager) => void, +) => { + const hammerRef = useRef(); + + useEffect(() => { + const targetRefElm = getRefElement(target as HTMLElement)!; + + (async () => { + await import('hammerjs'); + + const hammer = new Hammer.Manager(targetRefElm); + + hammerRef.current = hammer; + callback(hammer); + })(); + + return () => { + hammerRef.current?.destroy(); + }; + }, []); +}; diff --git a/packages/react-hooks/src/usePanMove.ts b/packages/react-hooks/src/usePanMove.ts new file mode 100644 index 0000000000..3b7556b21c --- /dev/null +++ b/packages/react-hooks/src/usePanMove.ts @@ -0,0 +1,45 @@ +import { RefOrElementOrCallback } from '@ringcentral/juno'; + +import { useHammer } from './useHammer'; + +type UsePanResizeOptions = { + onMove: (delta: number) => void; + direction?: 'horizontal' | 'vertical'; + onMoveStart?: () => void; + onMoveEnd?: () => void; +}; + +export const usePanMove = ( + target: RefOrElementOrCallback | EventTarget, + { + onMove, + onMoveStart, + onMoveEnd, + direction = 'horizontal', + }: UsePanResizeOptions, +) => { + useHammer(target, (hammer) => { + const pan = new Hammer.Pan({ + direction: + direction === 'horizontal' + ? Hammer.DIRECTION_HORIZONTAL + : Hammer.DIRECTION_VERTICAL, + threshold: 0, + }); + + hammer.add(pan); + + hammer.on('panstart', () => { + onMoveStart?.(); + }); + + hammer.on('panleft panright', (e: HammerInput) => { + const delta = e.deltaX; + onMove(delta); + }); + + hammer.on('panend pancancel', () => { + onMoveEnd?.(); + }); + }); +}; diff --git a/packages/react-hooks/src/useResizeAndToggle.tsx b/packages/react-hooks/src/useResizeAndToggle.tsx new file mode 100644 index 0000000000..820f369012 --- /dev/null +++ b/packages/react-hooks/src/useResizeAndToggle.tsx @@ -0,0 +1,108 @@ +import { + css, + getRefElement, + px, + RefOrElementOrCallback, + styled, + useEventCallback, + useResultRef, +} from '@ringcentral/juno'; +import React, { useLayoutEffect, useMemo, useRef, useState } from 'react'; + +import { usePanMove } from './usePanMove'; + +export const DragAnchor = styled.div<{ direction?: 'left' | 'right' }>` + width: 10px; + position: absolute; + user-select: none; + height: 100%; + top: 0; + ${({ direction }) => + direction === 'right' + ? css` + right: 0; + transform: translateX(50%); + ` + : css` + left: 0; + transform: translateX(-50%); + `} + cursor: col-resize; + z-index: 1; +`; + +export type DragResizeState = { + show: boolean; + width: number; +}; +export type UseResizeAndToggleOptions = { + /** + * direction to increase size + * + * @default 'right' + */ + direction?: 'left' | 'right'; + getCacheStateAndAction: () => [ + DragResizeState, + (value: DragResizeState) => void, + ]; +}; + +/** + * group resize and toggle logic together, + * let you can control resize and toggle easily. + */ +export const useResizeAndToggle = ( + target: RefOrElementOrCallback | EventTarget, + { getCacheStateAndAction, direction = 'right' }: UseResizeAndToggleOptions, +) => { + const { current: cacheStateAndAction } = useResultRef(() => + getCacheStateAndAction(), + ); + const [cacheState, setCacheState] = cacheStateAndAction; + + const [show, setShow] = useState(cacheState.show); + const widthRef = useRef(cacheState.width); + const dragAnchorRef = useRef(null); + + usePanMove(dragAnchorRef, { + onMove: (delta) => { + const targetElm = getRefElement(target as HTMLElement)!; + + if (!targetElm) return; + + // set width directly, make performance better + targetElm.style.width = px( + widthRef.current + (direction === 'right' ? delta : -delta), + ); + }, + onMoveEnd: () => { + const targetElm = getRefElement(target as HTMLElement)!; + if (!targetElm) return; + + // only trigger cache when move end and release change + widthRef.current = targetElm.clientWidth; + setCacheState({ show, width: widthRef.current }); + }, + }); + + useLayoutEffect(() => { + const targetElm = getRefElement(target as HTMLElement)!; + if (!targetElm) return; + + targetElm.style.width = px(widthRef.current); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return { + show, + setShow: useEventCallback((show: boolean) => { + setShow(show); + setCacheState({ show, width: widthRef.current }); + }), + dragNode: useMemo( + () => , + [direction], + ), + }; +}; diff --git a/packages/react-hooks/src/useZoom/bind-hammer-zoom.ts b/packages/react-hooks/src/useZoom/bind-hammer-zoom.ts new file mode 100644 index 0000000000..a5e71eb8c9 --- /dev/null +++ b/packages/react-hooks/src/useZoom/bind-hammer-zoom.ts @@ -0,0 +1,224 @@ +type BindHammerZoomOptions = { + hammer: HammerManager; + /** + * min rate of zoom + * + * @default 1 + */ + min?: number; + /** + * max rate of zoom + * + * @default 10 + */ + max?: number; + getTarget: () => HTMLElement; + getContainer: () => HTMLElement; + onScale: (scale: number) => void; + onDragChange: (state: boolean) => void; +}; + +export const bindHammerZoom = ({ + hammer, + min = 1, + max = 10, + getTarget, + getContainer, + onScale, + onDragChange, +}: BindHammerZoomOptions) => { + let scale = 1; + let initScale = scale; + let position = { x: 0, y: 0 }; + let draggable = false; + const centerMove = { ...position }; + let initPosition = position; + const target = getTarget(); + const container = getContainer(); + const defaultWidth = target.clientWidth; + const defaultHeight = target.clientHeight; + + const getIsBiggerThanContainer = () => { + const containerWidth = container.clientWidth; + const containerHeight = container.clientHeight; + + const xBeBigger = defaultWidth * scale > containerWidth; + const yBeBigger = defaultHeight * scale > containerHeight; + return { + x: xBeBigger, + y: yBeBigger, + bigger: xBeBigger || yBeBigger, + }; + }; + + const validatePositionBoundary = (newPosition: HammerPoint) => { + const containerHeight = container.clientHeight; + const containerWidth = container.clientWidth; + const width = target!.clientWidth; + const height = target!.clientHeight; + + const currentWidth = scale * width; + const outOfBoxWidth = Math.abs(currentWidth - containerWidth) / 2; + const currentHeight = scale * height; + const outOfBoxHeight = Math.abs(currentHeight - containerHeight) / 2; + + return { + x: Math.min(Math.max(newPosition.x, -outOfBoxWidth), outOfBoxWidth), + y: Math.min(Math.max(newPosition.y, -outOfBoxHeight), outOfBoxHeight), + }; + }; + + const validateZoomBoundary = (scale: number) => { + const toScale = Math.min(Math.max(scale, min), max); + + return toScale; + }; + + const zoomStart = (center: HammerPoint) => { + const rect = target.getBoundingClientRect(); + initScale = scale; + const { x, y } = center; + centerMove.x = (x - (rect.x + rect.width / 2)) / scale; + centerMove.y = (y - (rect.y + rect.height / 2)) / scale; + + initPosition = { ...position }; + }; + + const zoom = (sourceScale: number) => { + const toScale = validateZoomBoundary(sourceScale); + + scale = toScale; + setScale(); + onScale(scale); + + const biggerThanContainer = getIsBiggerThanContainer(); + + if (biggerThanContainer.bigger) { + if (!draggable) { + onDragChange(true); + } + draggable = true; + + const result = validatePositionBoundary({ + x: initPosition.x - centerMove.x * (scale - initScale), + y: initPosition.y - centerMove.y * (scale - initScale), + }); + + position.x = result.x; + position.y = result.y; + } else { + if (draggable) { + onDragChange(false); + } + draggable = false; + + position.x = 0; + position.y = 0; + } + setPosition(); + }; + + const zooming = (rate: number) => { + zoom(initScale * rate); + }; + + hammer.on('doubletap', () => { + toggle(); + }); + + hammer.on('pinchstart', (e) => { + zoomStart(e.center); + }); + + hammer.on('pinchmove', (e) => { + zooming(e.scale); + }); + + hammer.on('panstart', () => { + initPosition = { + x: position.x, + y: position.y, + }; + }); + + hammer.on('panmove', (e) => { + const biggerThanContainer = getIsBiggerThanContainer(); + + if (!biggerThanContainer.bigger) return; + + const result = validatePositionBoundary({ + x: initPosition.x + e.deltaX, + y: initPosition.y + e.deltaY, + }); + + if (biggerThanContainer.x) { + position.x = result.x; + } + if (biggerThanContainer.y) { + position.y = result.y; + } + setPosition(); + }); + + const toggleAnimation = (state: boolean) => { + if (state) { + target.style.transition = + target.style.transition || '195ms ease-in-out all'; + return; + } + target.style.transition = ''; + }; + + const setPosition = () => { + toggleAnimation(false); + target?.style.setProperty('--x', `${position.x}px`); + target?.style.setProperty('--y', `${position.y}px`); + }; + + const setScale = () => { + toggleAnimation(false); + target?.style.setProperty('--scale', `${scale}`); + }; + + const reset = () => { + scale = 1; + initScale = scale; + position = { x: 0, y: 0 }; + target?.style.removeProperty('--scale'); + target?.style.removeProperty('--x'); + target?.style.removeProperty('--y'); + toggleAnimation(true); + onScale(scale); + }; + + const zoomCenter = () => { + if (!target) return; + + scale = 1.5; + initScale = scale; + position = { x: 0, y: 0 }; + initPosition = { ...position }; + setScale(); + setPosition(); + toggleAnimation(true); + onScale(scale); + }; + + const toggle = () => { + if (scale !== 1) { + reset(); + return; + } + + zoomCenter(); + }; + + return { + reset, + zoomCenter, + toggle, + zoomStart, + zooming, + zoom, + }; +}; diff --git a/packages/react-hooks/src/useZoom/index.ts b/packages/react-hooks/src/useZoom/index.ts new file mode 100644 index 0000000000..ef64d0f9f4 --- /dev/null +++ b/packages/react-hooks/src/useZoom/index.ts @@ -0,0 +1,2 @@ +export * from './bind-hammer-zoom'; +export * from './useZoom'; diff --git a/packages/react-hooks/src/useZoom/useZoom.ts b/packages/react-hooks/src/useZoom/useZoom.ts new file mode 100644 index 0000000000..bfb6a19178 --- /dev/null +++ b/packages/react-hooks/src/useZoom/useZoom.ts @@ -0,0 +1,76 @@ +import { + getRefElement, + RefOrElementOrCallback, + useEventListener, +} from '@ringcentral/juno'; +import { useRef } from 'react'; + +import { useHammer } from '../useHammer'; + +import { bindHammerZoom } from './bind-hammer-zoom'; + +export const useHammerZoom = ( + target: RefOrElementOrCallback | EventTarget, + { + container, + min, + max, + onScale, + onDragChange, + }: { + container: RefOrElementOrCallback | EventTarget; + min: number; + max: number; + onScale: (scale: number) => void; + onDragChange: (state: boolean) => void; + }, +) => { + const zoomActionRef = useRef>(); + + useHammer(target, (manager) => { + const targetRefElm = getRefElement(target as HTMLElement)!; + const containerRefElm = getRefElement(container as HTMLElement)!; + + targetRefElm.style.scale = 'var(--scale, 1)'; + targetRefElm.style.transformOrigin = 'var(--origin)'; + targetRefElm.style.translate = 'var(--x) var(--y) 0'; + + const events = [ + new Hammer.Pinch(), + new Hammer.Tap({ event: 'doubletap', taps: 2 }), + new Hammer.Pan({ threshold: 0 }), + ]; + + events.forEach((recognizer) => { + manager.add(recognizer); + }); + + zoomActionRef.current = bindHammerZoom({ + hammer: manager, + min, + max, + getTarget: () => targetRefElm, + getContainer: () => containerRefElm!, + onScale, + onDragChange, + }); + }); + + useEventListener(container, 'wheel', (e: WheelEvent) => { + const action = zoomActionRef.current; + + action?.zoomStart({ x: e.clientX, y: e.clientY }); + action?.zooming(e.deltaY < 0 ? 1.1 : 0.9); + + e.preventDefault(); + }); + + return { + zoom: (toScale: number) => { + zoomActionRef.current?.zoom(toScale); + }, + reset: () => { + zoomActionRef.current?.reset(); + }, + }; +}; diff --git a/packages/react-hooks/test/.eslintrc b/packages/react-hooks/test/.eslintrc new file mode 100644 index 0000000000..9d077f5a9c --- /dev/null +++ b/packages/react-hooks/test/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "../../../ringcentral-js-widgets/eslint-settings/crius/.eslintrc" +} diff --git a/packages/react-hooks/test/features/bind-hammer-zoom.test.ts b/packages/react-hooks/test/features/bind-hammer-zoom.test.ts new file mode 100644 index 0000000000..5025514eff --- /dev/null +++ b/packages/react-hooks/test/features/bind-hammer-zoom.test.ts @@ -0,0 +1,85 @@ +import { bindHammerZoom } from '../../src/useZoom/bind-hammer-zoom'; + +describe('bindHammerZoom', () => { + let mockHammer: any; + let mockGetTarget: jest.Mock; + let mockGetContainer: jest.Mock; + let mockOnScale: jest.Mock; + let mockOnDragChange: jest.Mock; + + beforeEach(() => { + mockHammer = { + on: jest.fn(), + }; + mockGetTarget = jest.fn(() => document.createElement('div')); + mockGetContainer = jest.fn(); + mockOnScale = jest.fn(); + mockOnDragChange = jest.fn(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should initialize with default values', () => { + const bindZoom = bindHammerZoom({ + hammer: mockHammer, + getTarget: mockGetTarget, + getContainer: mockGetContainer, + onScale: mockOnScale, + onDragChange: mockOnDragChange, + }); + + expect(bindZoom.reset).toBeDefined(); + expect(bindZoom.zoomCenter).toBeDefined(); + expect(bindZoom.toggle).toBeDefined(); + expect(bindZoom.zoomStart).toBeDefined(); + expect(bindZoom.zooming).toBeDefined(); + expect(bindZoom.zoom).toBeDefined(); + }); + + it('should reset the zoom to default values', () => { + const bindZoom = bindHammerZoom({ + hammer: mockHammer, + getTarget: mockGetTarget, + getContainer: mockGetContainer, + onScale: mockOnScale, + onDragChange: mockOnDragChange, + }); + + bindZoom.reset(); + + expect(mockOnDragChange).not.toHaveBeenCalled(); + expect(mockOnScale).toHaveBeenCalledWith(1); + }); + + it('should zoom to the center', () => { + const bindZoom = bindHammerZoom({ + hammer: mockHammer, + getTarget: mockGetTarget, + getContainer: mockGetContainer, + onScale: mockOnScale, + onDragChange: mockOnDragChange, + }); + + bindZoom.zoomCenter(); + + expect(mockOnScale).toHaveBeenCalledWith(1.5); + }); + + it('should toggle between reset and zoomCenter', () => { + const bindZoom = bindHammerZoom({ + hammer: mockHammer, + getTarget: mockGetTarget, + getContainer: mockGetContainer, + onScale: mockOnScale, + onDragChange: mockOnDragChange, + }); + + bindZoom.toggle(); // Zoom to center + bindZoom.toggle(); // Reset + + expect(mockOnDragChange).not.toHaveBeenCalled(); + expect(mockOnScale).toHaveBeenCalledWith(1); + }); +}); diff --git a/packages/react-hooks/test/features/indext.test.ts b/packages/react-hooks/test/features/indext.test.ts new file mode 100644 index 0000000000..6ebd531702 --- /dev/null +++ b/packages/react-hooks/test/features/indext.test.ts @@ -0,0 +1,3 @@ +test('', () => { + // +}); diff --git a/packages/react-hooks/test/features/useZoom.test.ts b/packages/react-hooks/test/features/useZoom.test.ts new file mode 100644 index 0000000000..66dcae1145 --- /dev/null +++ b/packages/react-hooks/test/features/useZoom.test.ts @@ -0,0 +1,79 @@ +import { waitUntil } from '@ringcentral-integration/commons/utils'; +import { renderHook, act } from '@testing-library/react-hooks'; + +import { useHammerZoom } from '../../src/useZoom/useZoom'; + +describe('useHammerZoom', () => { + let mockTarget: HTMLElement; + let mockContainer: HTMLElement; + let mockOnScale: jest.Mock; + let mockOnDragChange: jest.Mock; + + beforeEach(() => { + mockTarget = document.createElement('div'); + mockContainer = document.createElement('div'); + mockOnScale = jest.fn(); + mockOnDragChange = jest.fn(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should initialize with default values', async () => { + const { result } = renderHook(() => + useHammerZoom(mockTarget, { + container: mockContainer, + min: 0, + max: 1, + onScale: mockOnScale, + onDragChange: mockOnDragChange, + }), + ); + + await waitUntil(() => !!result.current); + + expect(result.current.zoom).toBeDefined(); + expect(result.current.reset).toBeDefined(); + }); + + it('should call onScale when zooming', async () => { + const { result } = renderHook(() => + useHammerZoom(mockTarget, { + container: mockContainer, + min: 0, + max: 1, + onScale: mockOnScale, + onDragChange: mockOnDragChange, + }), + ); + + await waitUntil(() => !!result.current); + + act(() => { + result.current.zoom(1.5); + }); + + expect(mockOnScale).toHaveBeenCalledWith(1); + }); + + it('should call reset when resetting zoom', async () => { + const { result } = renderHook(() => + useHammerZoom(mockTarget, { + container: mockContainer, + min: 0, + max: 1, + onScale: mockOnScale, + onDragChange: mockOnDragChange, + }), + ); + + await waitUntil(() => !!result.current); + + act(() => { + result.current.reset(); + }); + + expect(mockOnScale).toHaveBeenCalledWith(1); + }); +}); diff --git a/packages/react-hooks/test/jest.setup.ts b/packages/react-hooks/test/jest.setup.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/react-hooks/test/tsconfig.json b/packages/react-hooks/test/tsconfig.json new file mode 100644 index 0000000000..29d3f0aeb0 --- /dev/null +++ b/packages/react-hooks/test/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../../../ringcentral-js-widgets/tsconfig.test.json", + "include": ["./**/*.ts", "./**/*.tsx", "../typings/**/*"], + "files": ["./typings/typing.d.ts"] +} diff --git a/packages/react-hooks/test/typings/typing.d.ts b/packages/react-hooks/test/typings/typing.d.ts new file mode 100644 index 0000000000..7d4d4f8ae0 --- /dev/null +++ b/packages/react-hooks/test/typings/typing.d.ts @@ -0,0 +1,12 @@ +/// + +declare const global: typeof globalThis & { + instance: { + app: any; + rcMock: any; + example?: any; + payload: { + // + }; + }; +}; diff --git a/packages/react-hooks/tsconfig.json b/packages/react-hooks/tsconfig.json new file mode 100644 index 0000000000..9fc5557519 --- /dev/null +++ b/packages/react-hooks/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../tsconfig.json", + "include": ["./**/*.ts", "./**/*.tsx"], + "exclude": ["./test/**/*", "./**/*.test.ts", "./**/*.test.tsx"], + "files": [ + "../ringcentral-widgets/typings/react.d.ts" + ], + "compilerOptions": { + "outDir": "../../build/react-hooks", + "noEmit": false, + "sourceMap": true + } +} diff --git a/packages/react-hooks/tsconfig.lint.json b/packages/react-hooks/tsconfig.lint.json new file mode 100644 index 0000000000..6fda93450d --- /dev/null +++ b/packages/react-hooks/tsconfig.lint.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "include": [ + "./**/*.ts", + "./**/*.tsx", + "./typings/**/*", + "../ringcentral-integration/typings/**/*", + "../ringcentral-widgets/typings/**/*", + "../test-utils/lib/global.d.ts" + ], + "compilerOptions": { + "checkJs": false + } +} diff --git a/packages/ringcentral-crius/README.md b/packages/ringcentral-crius/README.md index c9fb0c8b35..cfda2ff458 100644 --- a/packages/ringcentral-crius/README.md +++ b/packages/ringcentral-crius/README.md @@ -41,3 +41,25 @@ Good practice: @title('demo title desc') class Demo extends Step{} ``` + + +# Extend test case + +When you need extend case like below, also make sure add `@common` at be extended class + +```ts +@autorun(test) +@ut +@p0 +@title('demo title desc') +@common +class Demo extends Step{} + +@autorun(test) +@ut +@p0 +@title('demo2 title desc') +class Demo2 extends Demo {} +``` + +and for be extended project, you should add `COMMON=true` at `test` script, only have that, `@common` tests will be run. diff --git a/packages/ringcentral-crius/package.json b/packages/ringcentral-crius/package.json index 86d91c09ca..40a48d0fa6 100644 --- a/packages/ringcentral-crius/package.json +++ b/packages/ringcentral-crius/package.json @@ -1,6 +1,6 @@ { "name": "@ringcentral-integration/crius", - "version": "0.14.0", + "version": "0.15.0", "description": "A test lib based on Crius for RingCentral Integration", "keywords": [], "license": "ISC", @@ -10,12 +10,15 @@ "test": "jest" }, "dependencies": { - "babel-jest": "^27.4.6", + "babel-jest": "^29.7.0", "crius-is": "*", "crius-test": "*" }, "common": true, "ci": { "@ringcentral-integration/crius": "**" + }, + "nx": { + "tags": ["scope:ci-group-1"] } } diff --git a/packages/ringcentral-crius/src/combine.tsx b/packages/ringcentral-crius/src/combine.tsx index 758dd5e383..0cafc8c02b 100644 --- a/packages/ringcentral-crius/src/combine.tsx +++ b/packages/ringcentral-crius/src/combine.tsx @@ -1,4 +1,5 @@ import type { StepFunction } from 'crius-test'; + import { getProps } from './getProps'; export const combine = (fn: StepFunction) => { diff --git a/packages/ringcentral-crius/src/constant.ts b/packages/ringcentral-crius/src/constant.ts index 9919f2b89f..c3785df1c1 100644 --- a/packages/ringcentral-crius/src/constant.ts +++ b/packages/ringcentral-crius/src/constant.ts @@ -1,7 +1,7 @@ export const testTypes = ['ut', 'it', 'e2e', 'manual'] as const; -export type TestType = typeof testTypes[number]; +export type TestType = (typeof testTypes)[number]; export const priorities = ['p0', 'p1', 'p2', 'p3'] as const; -export type Priority = typeof priorities[number]; +export type Priority = (typeof priorities)[number]; diff --git a/packages/ringcentral-crius/src/decorators/status.ts b/packages/ringcentral-crius/src/decorators/status.ts index f64f9b8138..7ace1e41bd 100644 --- a/packages/ringcentral-crius/src/decorators/status.ts +++ b/packages/ringcentral-crius/src/decorators/status.ts @@ -2,7 +2,7 @@ import type { Step } from '../step'; const testStatus = ['partial', 'complete'] as const; -export type TestStatus = typeof testStatus[number]; +export type TestStatus = (typeof testStatus)[number]; export function status(value: TestStatus) { return function (target: Object) { diff --git a/packages/ringcentral-crius/src/step.tsx b/packages/ringcentral-crius/src/step.tsx index 8ec0d1be21..f05c00e02b 100644 --- a/packages/ringcentral-crius/src/step.tsx +++ b/packages/ringcentral-crius/src/step.tsx @@ -1,3 +1,4 @@ +import { isCriusNode } from 'crius-is'; import type { StepFunction as BaseStepFunction } from 'crius-test'; import { Step as BaseStep, @@ -10,7 +11,7 @@ import { And as BaseAnd, examples, } from 'crius-test'; -import { isCriusNode } from 'crius-is'; + import { combine } from './combine'; import type { TestType } from './constant'; import { testTypes } from './constant'; diff --git a/packages/ringcentral-crius/test/builder.test.tsx b/packages/ringcentral-crius/test/builder.test.tsx index 04836b9572..865e90512d 100644 --- a/packages/ringcentral-crius/test/builder.test.tsx +++ b/packages/ringcentral-crius/test/builder.test.tsx @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ + /* eslint-disable @typescript-eslint/no-use-before-define */ import type { StepFunction } from '../src'; import { diff --git a/packages/ringcentral-integration/.gitignore b/packages/ringcentral-integration/.gitignore index ba852814d2..6f72401b4d 100644 --- a/packages/ringcentral-integration/.gitignore +++ b/packages/ringcentral-integration/.gitignore @@ -32,9 +32,6 @@ node_modules # Optional REPL history .node_repl_history -# Karma Report -karma - .DS_Store release diff --git a/packages/ringcentral-integration/enums/callDirections.ts b/packages/ringcentral-integration/enums/callDirections.ts index 0a28cb2532..64e1416d96 100644 --- a/packages/ringcentral-integration/enums/callDirections.ts +++ b/packages/ringcentral-integration/enums/callDirections.ts @@ -1,8 +1,13 @@ -import { ObjectMap } from '@ringcentral-integration/core/lib/ObjectMap'; +import { + ObjectMap, + type ObjectMapValue, +} from '@ringcentral-integration/core/lib/ObjectMap'; export const callDirection = ObjectMap.fromObject({ inbound: 'Inbound', outbound: 'Outbound', } as const); +export type CallDirection = ObjectMapValue; + export default callDirection; diff --git a/packages/ringcentral-integration/enums/callResults.ts b/packages/ringcentral-integration/enums/callResults.ts index 646f9b8eff..720b78c10a 100644 --- a/packages/ringcentral-integration/enums/callResults.ts +++ b/packages/ringcentral-integration/enums/callResults.ts @@ -48,6 +48,6 @@ export const callResults = ObjectMap.fromObject({ } as const); export type CallResultsKey = ObjectMapKey; -export type CallResultsValue = typeof callResults[CallResultsKey]; +export type CallResultsValue = (typeof callResults)[CallResultsKey]; export default callResults; diff --git a/packages/ringcentral-integration/enums/issueTrackingMessages.ts b/packages/ringcentral-integration/enums/issueTrackingMessages.ts new file mode 100644 index 0000000000..58209d3205 --- /dev/null +++ b/packages/ringcentral-integration/enums/issueTrackingMessages.ts @@ -0,0 +1,6 @@ +import { ObjectMap } from '@ringcentral-integration/core/lib/ObjectMap'; + +export const issueTrackingMessages = ObjectMap.prefixKeys( + ['downloadSuccess', 'downloadFail'], + 'issueTracking', +); diff --git a/packages/ringcentral-integration/enums/subscriptionFilters.ts b/packages/ringcentral-integration/enums/subscriptionFilters.ts index eb3f5b4b6e..dd6427673c 100644 --- a/packages/ringcentral-integration/enums/subscriptionFilters.ts +++ b/packages/ringcentral-integration/enums/subscriptionFilters.ts @@ -9,7 +9,11 @@ export const subscriptionFilters = ObjectMap.fromObject({ accountExtension: '/restapi/v1.0/account/~/extension', companyContacts: '/restapi/v1.0/account/~/directory/contacts', messageStore: '/restapi/v1.0/account/~/extension/~/message-store', + instantMessage: + '/restapi/v1.0/account/~/extension/~/message-store/instant?type=SMS', telephonySessions: '/restapi/v1.0/account/~/extension/~/telephony/sessions', + startRing: '/restapi/v1.0/account/~/extension/~/start-ring', + stopRing: '/restapi/v1.0/account/~/extension/~/stop-ring', } as const); export type SubscriptionFilter = ObjectMapValue; diff --git a/packages/ringcentral-integration/enums/syncTypes.ts b/packages/ringcentral-integration/enums/syncTypes.ts index 4f2e88ccbc..d5e0b1b7e8 100644 --- a/packages/ringcentral-integration/enums/syncTypes.ts +++ b/packages/ringcentral-integration/enums/syncTypes.ts @@ -5,6 +5,6 @@ export const syncTypes = ObjectMap.fromObject({ iSync: 'ISync', } as const); -export type SyncType = typeof syncTypes[keyof typeof syncTypes]; +export type SyncType = (typeof syncTypes)[keyof typeof syncTypes]; export default syncTypes; diff --git a/packages/ringcentral-integration/enums/telephonySessionStatus.ts b/packages/ringcentral-integration/enums/telephonySessionStatus.ts index 8b9b0d4164..ed22c7bbda 100644 --- a/packages/ringcentral-integration/enums/telephonySessionStatus.ts +++ b/packages/ringcentral-integration/enums/telephonySessionStatus.ts @@ -1,6 +1,5 @@ -import { PartyStatusCode } from 'ringcentral-call-control/lib/Session'; - import { ObjectMap } from '@ringcentral-integration/core/lib/ObjectMap'; +import { PartyStatusCode } from 'ringcentral-call-control/lib/Session'; export const telephonySessionStatus = ObjectMap.fromObject(PartyStatusCode); diff --git a/packages/ringcentral-integration/enums/telephonyStatus.ts b/packages/ringcentral-integration/enums/telephonyStatus.ts index 50598017dc..c11f1043b0 100644 --- a/packages/ringcentral-integration/enums/telephonyStatus.ts +++ b/packages/ringcentral-integration/enums/telephonyStatus.ts @@ -9,6 +9,6 @@ export const telephonyStatus = ObjectMap.fromObject({ } as const); export type TelephonyStatus = - typeof telephonyStatus[keyof typeof telephonyStatus]; + (typeof telephonyStatus)[keyof typeof telephonyStatus]; export default telephonyStatus; diff --git a/packages/ringcentral-integration/enums/trackEvents.ts b/packages/ringcentral-integration/enums/trackEvents.ts index 738a88d149..ddaea1e54f 100644 --- a/packages/ringcentral-integration/enums/trackEvents.ts +++ b/packages/ringcentral-integration/enums/trackEvents.ts @@ -101,4 +101,30 @@ export const trackEvents = ObjectMap.fromObject({ executionReplyWithMessage: 'Attempt to reply message on inbound call', clickToCallInContactDetails: 'Click To Dial (Contact Details)', clickToSMSInContactDetails: 'Click To SMS (Contact Details)', + phoneConferenceCallMerge: 'Int_phone_conferenceCall_merge', + clickParticipantsIcon: 'Int_phone_conferenceCall_participantList', + clickRemoveParticipant: 'Int_phone_conferenceCall_removeParticipant', + clickConfirmRemoveParticipant: + 'Int_phone_conferenceCall_removeParticipant_remove', + getContactSearch: 'Int_Phone_contactSearch', + searchedContactClicked: 'Int_Phone_searchedContactClicked', + callMade: 'Int_Phone_callMade', + transferAskFirst: 'Int_Phone_Transfer_askFirst', + transferSwitchOnholdCall: 'Int_Phone_Transfer_switchonholdCall', + transferToVoicemail: 'Int_Phone_Transfer_toVoicemail', + transferCompleteTransfer: 'Int_Phone_Transfer_completeTransfer', + toggleOnAutoLogTexts: 'Toggle on: auto SMS logging', + newLogAutomatically: 'Call Log: New log automatically', + newLogManually: 'Call Log: New log manually', + contactSearchFromDialPad: 'Call: Contact search from dial pad', + clickAddTextLogButton: 'Click Add text log button', + clickSaveOnAddTextLog: + 'Click Save after click on Add text log when auto SMS log is on', + clickFeedback: 'Feedback: Click Feedback Button/Settings', + logSMSFromHistoryPage: 'SMS Log: Log SMS/Message history page', + logSMSFromConversationPage: 'SMS Log: Log SMS/SMS conversation page', + newSMSLogManually: 'SMS Log: New log manually', + viewLogFromCallHistory: 'Int_Phone_callHistory_viewLog', + uploadRingtone: 'Int_Audio_uploadRingtone', + deleteRingtone: 'Int_Audio_deleteRingtone', } as const); diff --git a/packages/ringcentral-integration/gulpfile.js b/packages/ringcentral-integration/gulpfile.js index a0d0c668a5..a19e669849 100644 --- a/packages/ringcentral-integration/gulpfile.js +++ b/packages/ringcentral-integration/gulpfile.js @@ -122,17 +122,16 @@ export function compile() { .src([ './**/*.js', './**/*.ts', + '!./**/*.min.js', + '!./**/pendo.xhr.js', '!./**/*.d.ts', '!./**/*.test.js', '!./**/*.test.ts', '!./*.js', '!./coverage{/**,}', '!./docs{/**,}', - '!./karma{/**,}', '!./junit{/**,}', '!./node_modules{/**,}', - '!./**/*.min.js', - '!./**/pendo.xhr.js', ]) .pipe( transformLoader({ @@ -146,13 +145,13 @@ export function compile() { } export function minifiedFileCopy() { - const localAnalyticFilePath = path.resolve( - __dirname, - './lib/Analytics', - ); + const localAnalyticFilePath = path.resolve(__dirname, './lib/Analytics'); const destPath = path.resolve(BUILD_PATH, './lib/Analytics'); return gulp - .src([`${localAnalyticFilePath}/*.min.js`, `${localAnalyticFilePath}/pendo.xhr.js`]) + .src([ + `${localAnalyticFilePath}/*.min.js`, + `${localAnalyticFilePath}/pendo.xhr.js`, + ]) .pipe(gulp.dest(destPath)); } diff --git a/packages/ringcentral-integration/helpers/meetingHelper.interface.ts b/packages/ringcentral-integration/helpers/meetingHelper.interface.ts index db59871629..0dd290cf70 100644 --- a/packages/ringcentral-integration/helpers/meetingHelper.interface.ts +++ b/packages/ringcentral-integration/helpers/meetingHelper.interface.ts @@ -6,7 +6,7 @@ const MeetingType = { PMI: 'PMI', } as const; -type MeetingTypeV = typeof MeetingType[keyof typeof MeetingType]; +type MeetingTypeV = (typeof MeetingType)[keyof typeof MeetingType]; // TODO: will remove this when google app script could support export seperately // export together because google app script not fully support export diff --git a/packages/ringcentral-integration/helpers/meetingHelper.ts b/packages/ringcentral-integration/helpers/meetingHelper.ts index addd877021..c935303f43 100644 --- a/packages/ringcentral-integration/helpers/meetingHelper.ts +++ b/packages/ringcentral-integration/helpers/meetingHelper.ts @@ -1,13 +1,14 @@ -import { pick } from 'ramda'; import type DialInNumberResource from '@rc-ex/core/lib/definitions/DialInNumberResource'; +import type { CountryCode } from '@ringcentral-integration/phone-number'; import formatPhoneNumber, { formatTypes, } from '@ringcentral-integration/phone-number/lib/format'; -import type { CountryCode } from '@ringcentral-integration/phone-number'; import { format } from '@ringcentral-integration/utils'; +import { pick } from 'ramda'; -import i18n from '../modules/Meeting/i18n'; import type { RcMMeetingModel } from '../modules/Meeting/Meeting.interface'; +import i18n from '../modules/Meeting/i18n'; + import type { MeetingTypeV } from './meetingHelper.interface'; import { MeetingType } from './meetingHelper.interface'; diff --git a/packages/ringcentral-integration/integration-test/mock/data/apiInfo.json b/packages/ringcentral-integration/integration-test/mock/data/apiInfo.json index e3142013f6..2526d86529 100644 --- a/packages/ringcentral-integration/integration-test/mock/data/apiInfo.json +++ b/packages/ringcentral-integration/integration-test/mock/data/apiInfo.json @@ -1,5 +1,5 @@ { - "uri": "https://api-up.lab.rcch.ringcentral.com/restapi/v1.0", + "uri": "https://patform.ringcentral.com/restapi/v1.0", "versionString": "1.0.31", "releaseDate": "2017-07-12T00:00:00.000Z", "uriString": "v1.0" diff --git a/packages/ringcentral-integration/integration-test/mock/index.ts b/packages/ringcentral-integration/integration-test/mock/index.ts index fd704504ad..4ef08bdb29 100644 --- a/packages/ringcentral-integration/integration-test/mock/index.ts +++ b/packages/ringcentral-integration/integration-test/mock/index.ts @@ -1,10 +1,12 @@ +import { SOCKET_MOCK_URL } from '@ringcentral-integration/test-utils/lib/socketMockUrl'; +import { SDK } from '@ringcentral/sdk'; import fetchMock from 'fetch-mock'; import WebSocket from 'isomorphic-ws'; import JestWebSocketMock from 'jest-websocket-mock'; -import { SDK } from '@ringcentral/sdk'; - import { RCV_PREFERENCES_IDS } from '../../modules/RcVideo'; + +import type { MockForLoginOptions } from './MockForLoginOptions.interface'; import accountBody from './data/accountInfo.json'; import accountPhoneNumberBody from './data/accountPhoneNumber.json'; import activeCallsBody from './data/activeCalls.json'; @@ -13,15 +15,15 @@ import apiInfoBody from './data/apiInfo.json'; import assistedUsersBody from './data/assistedUsers.json'; import authzProfileBody from './data/authzProfile.json'; import blockedNumberBody from './data/blockedNumber.json'; -import callerIdBody from './data/callerId.json'; import callLogBody from './data/callLog.json'; import callLogList from './data/callLogList.json'; +import callerIdBody from './data/callerId.json'; import conferenceCallBody from './data/conferenceCall.json'; import conferenceCallBringInBody from './data/conferenceCallBringIn.json'; import conferencingBody from './data/conferencing.json'; import deviceBody from './data/device.json'; -import dialingPlanBody from './data/dialingPlan.json'; import dialInNumbersBody from './data/dialInNumbers.json'; +import dialingPlanBody from './data/dialingPlan.json'; import discoveryExternalBody from './data/discoveryExternal.json'; import discoveryInitialBody from './data/discoveryInitial.json'; import extensionListBody from './data/extension.json'; @@ -35,11 +37,6 @@ import generateCodeBody from './data/generateCode.json'; import lockedSettingsBody from './data/lockedSettings.json'; import meetingBody from './data/meeting.json'; import meetingInvitationBody from './data/meetingInvitation.json'; -import { - RCV_INVITATION_BODY, - RCV_INVITATION_START, - RCV_INVITATION_END, -} from './data/rcvInvitation'; import meetingProviderRcmBody from './data/meetingProviderRcm.json'; import meetingProviderRcvBody from './data/meetingProviderRcv.json'; import messageItemBody from './data/messageItem.json'; @@ -51,6 +48,11 @@ import numberParserV2Body from './data/numberParserV2.json'; import phoneNumberBody from './data/phoneNumber.json'; import postRcvBridgesBody from './data/postRcvBridges.json'; import presenceBody from './data/presence.json'; +import { + RCV_INVITATION_BODY, + RCV_INVITATION_START, + RCV_INVITATION_END, +} from './data/rcvInvitation'; import rcvMeetingSettingsBody from './data/rcvMeetingSettings.json'; import ringOutBody from './data/ringOut.json'; import serviceInfoBody from './data/serviceInfo.json'; @@ -67,11 +69,10 @@ import wsConnectionDetailsBody from './data/ws/connectionDetails.json'; import wsHeartbeatResponse from './data/ws/heartbeatResponse.json'; import wsSubscriptionResponse from './data/ws/subscriptionResponse.json'; import wsTokenBody from './data/ws/wstoken.json'; -import type { MockForLoginOptions } from './MockForLoginOptions.interface'; export * from './types'; -export const mockWsServer = 'ws://whatever'; +export const mockWsServer = SOCKET_MOCK_URL; export const mockServer = 'http://whatever'; export function createSDK(options = {}) { const opts = { diff --git a/packages/ringcentral-integration/integration-test/mock/telephonySessionBuilder.ts b/packages/ringcentral-integration/integration-test/mock/telephonySessionBuilder.ts index 11ac28cdda..6b229d0346 100644 --- a/packages/ringcentral-integration/integration-test/mock/telephonySessionBuilder.ts +++ b/packages/ringcentral-integration/integration-test/mock/telephonySessionBuilder.ts @@ -2,10 +2,24 @@ import dayjs from 'dayjs'; import { PartyStatusCode } from 'ringcentral-call-control/lib/Session'; import { v4 as uuidV4 } from 'uuid'; -import callDirections from '../../enums/callDirections'; +import { callDirection, type CallDirection } from '../../enums/callDirections'; + import extensionBody from './data/extensionInfo.json'; import telephonySessionMessage from './data/telephonySessions.json'; +// "s-a4a012b34c545z18b6f423f7fzf4d9960000" +export const makeTelephonySessionId = () => `s-${uuidV4()}`; + +// "p-a4a012b34c545z18b6f423f7fzf4d9960000-1" +export const makePartyId = (telephonySessionId: string) => + `p-${telephonySessionId.substring(2)}-1`; + +// "e5c8acd0-dcc2-4767-b445-b0029bd8b85210.74.1.43-5070-b30665b0-599a-49b9-b" +export const makeWebphoneSessionId = () => uuidV4(); + +// "conf_732d613461306438313238356331617a31376662656163336664377a3830633664303030304031302e37342e31332e3132393a35303730" +export const makeVoiceCallToken = () => `conf_${uuidV4()}`; + /** * Telephony session message boy //https://developers.ringcentral.com/api-reference/Extension-Telephony-Sessions-Event * supports: @@ -20,29 +34,26 @@ export type PhoneNumber = { phoneNumber: string; }; -type CallDirectionsKeys = keyof typeof callDirections; - -type CallDirections = (typeof callDirections)[CallDirectionsKeys]; type PartyStatusCodeKeys = keyof typeof PartyStatusCode; type recordingsProps = { id?: string; active?: boolean; }; +type ConferenceRole = 'Host' | 'Participant'; + +interface PeerId { + telephonySessionId?: string; + partyId?: string; + sessionId?: string; +} + export type Party = { extensionId: string; id: string; - direction: CallDirections; - to: { - phoneNumber: string; - name: string; - extensionId: string; - }; - from: { - phoneNumber: string; - name: string; - extensionId: string; - }; + direction: CallDirection; + to: NumberData; + from: NumberData; status: { code: (typeof PartyStatusCode)[PartyStatusCodeKeys]; reason: string; @@ -53,12 +64,14 @@ export type Party = { srvLvl: string; srvLvlExt: string; }; + peerId?: PeerId; }; recordings: recordingsProps[]; missedCall: boolean; standAlone: boolean; muted: boolean; queueCall: boolean; + conferenceRole?: ConferenceRole; }; export type Origin = { @@ -79,7 +92,7 @@ export type Body = { }; let sequence = 10; -const DEFAULT_DIRECTION = callDirections.outbound; +const DEFAULT_DIRECTION = callDirection.outbound; const DEFAULT_RECORD_STATUS = false; export const DEFAULT_PHONE_NUMBER = '+16501234567'; @@ -95,16 +108,17 @@ export interface TelephonySessionInterface { } export interface NumberData { - phoneNumber: string; - name: string; - extensionId: string; + phoneNumber?: string; + name?: string; + extensionId?: string; } interface InitParams { telephonySessionId?: string; - phoneNumber?: string; - direction?: CallDirections; + partyId?: string; sessionId?: string; + phoneNumber?: string; + direction?: CallDirection; status?: PartyStatusCode; fromNumberData?: NumberData; toNumberData?: NumberData; @@ -112,25 +126,34 @@ interface InitParams { isRecording?: boolean; muteStatus?: boolean; queueCall?: boolean; + reason?: string; + originType?: string; + peerId?: PeerId; + conferenceRole?: ConferenceRole; } export const telephonySessionBuildersCache: TelephonySessionBuilder[] = []; +export const clearTelephonySessionBuilders = () => { + telephonySessionBuildersCache.length = 0; // clear +}; class TelephonySessionBuilder { - private _data: TelephonySessionInterface; - private _telephonySessionId: string; - private _phoneNumber: string; - private _direction: CallDirections; - private _sessionId: string; - private _partyStatus: PartyStatusCode; - private _partyReason: string; - private _partyId: string; - private _fromNumberData: NumberData; - private _toNumberData: NumberData; - private _startTime: string; - private _isRecording: boolean; - private _muteStatus: boolean; - private _queueCall: boolean; + private _telephonySessionId!: string; + private _phoneNumber!: string; + private _direction!: CallDirection; + private _sessionId!: string; + private _partyStatus!: PartyStatusCode; + private _partyReason!: string; + private _partyId!: string; + private _fromNumberData?: NumberData; + private _toNumberData?: NumberData; + private _startTime?: string; + private _isRecording!: boolean; + private _muteStatus!: boolean; + private _queueCall!: boolean; + private _originType!: string; + private _peerId?: PeerId; + private _conferenceRole?: ConferenceRole; relatedWebphoneSession: any; constructor(initParams: InitParams = {}) { @@ -139,10 +162,11 @@ class TelephonySessionBuilder { } _init({ - telephonySessionId = uuidV4(), + telephonySessionId = makeTelephonySessionId(), + partyId = makePartyId(telephonySessionId), + sessionId = makeWebphoneSessionId(), phoneNumber = DEFAULT_PHONE_NUMBER, direction = DEFAULT_DIRECTION, - sessionId, status = PartyStatusCode.proceeding, reason = 'AttendedTransfer', fromNumberData, @@ -151,12 +175,15 @@ class TelephonySessionBuilder { isRecording = DEFAULT_RECORD_STATUS, muteStatus = false, queueCall = false, + originType = 'Call', + peerId, + conferenceRole, }: InitParams) { this._telephonySessionId = telephonySessionId; - this._sessionId = sessionId || telephonySessionId; + this._partyId = partyId; + this._sessionId = sessionId; this._phoneNumber = phoneNumber; this._direction = direction; - this._partyId = `${telephonySessionId}-1`; this._partyStatus = status; this._partyReason = reason; this._fromNumberData = fromNumberData; @@ -165,13 +192,16 @@ class TelephonySessionBuilder { this._isRecording = isRecording; this._muteStatus = muteStatus; this._queueCall = queueCall; + this._originType = originType; + this._peerId = peerId; + this._conferenceRole = conferenceRole; } setRelatedWebphoneSession(webphoneSession: any) { this.relatedWebphoneSession = webphoneSession; } - direction(direction: CallDirections) { + direction(direction: CallDirection) { this._direction = direction; return this; } @@ -251,14 +281,36 @@ class TelephonySessionBuilder { return this; } + setPeerId(peerId?: PeerId) { + this._peerId = peerId; + return this; + } + + setConferenceRole(role: ConferenceRole) { + this._conferenceRole = role; + return this; + } + done() { return this.data; } - get telephoneSessionId() { + getSessionId() { + return this._sessionId; + } + + getPartyId() { + return this._partyId; + } + + getTelephonySessionId() { return this._telephonySessionId; } + getStatus() { + return this._partyStatus; + } + get numberData() { return { phoneNumber: this._phoneNumber, @@ -308,17 +360,19 @@ class TelephonySessionBuilder { srvLvl: '-149699523', srvLvlExt: '390', }, + peerId: this._peerId, }, recordings: this.recordings, missedCall: false, standAlone: false, muted: this._muteStatus, queueCall: this._queueCall, + conferenceRole: this._conferenceRole, }, ], recordings: this.recordings, origin: { - type: 'Call', + type: this._originType, }, }, }; @@ -328,4 +382,5 @@ class TelephonySessionBuilder { function createTelephonySession(initParams?: InitParams) { return new TelephonySessionBuilder(initParams); } -export { createTelephonySession, PartyStatusCode, TelephonySessionBuilder }; + +export { PartyStatusCode, TelephonySessionBuilder, createTelephonySession }; diff --git a/packages/ringcentral-integration/interfaces/ActiveSession.interface.ts b/packages/ringcentral-integration/interfaces/ActiveSession.interface.ts index 82cadbdf6a..61e86f4545 100644 --- a/packages/ringcentral-integration/interfaces/ActiveSession.interface.ts +++ b/packages/ringcentral-integration/interfaces/ActiveSession.interface.ts @@ -21,5 +21,13 @@ export interface ActiveCallControlSessionData extends SessionData { from: any; to: any; startTime: number; - isRecording: boolean; + isRecording?: boolean; + isConferenceCall: boolean; + conferenceParticipants: { + sessionId: string; + telephonySessionId: string; + partyId: string; + isHost?: boolean; + sessionName: string; + }[]; } diff --git a/packages/ringcentral-integration/interfaces/BasePhone.interface.ts b/packages/ringcentral-integration/interfaces/BasePhone.interface.ts index bec94074d8..c28b0c9159 100644 --- a/packages/ringcentral-integration/interfaces/BasePhone.interface.ts +++ b/packages/ringcentral-integration/interfaces/BasePhone.interface.ts @@ -16,8 +16,8 @@ import type { AppFeatures } from '../modules/AppFeatures'; import type { Auth } from '../modules/Auth'; import type { AvailabilityMonitor } from '../modules/AvailabilityMonitor'; import type { Brand } from '../modules/Brand'; -import type { CallMonitor } from '../modules/CallMonitor'; import type { Call } from '../modules/Call'; +import type { CallMonitor } from '../modules/CallMonitor'; import type { ConnectivityMonitor } from '../modules/ConnectivityMonitor'; import type { DateTimeFormat } from '../modules/DateTimeFormat'; import type { DialingPlan } from '../modules/DialingPlan'; diff --git a/packages/ringcentral-integration/interfaces/Call.interface.ts b/packages/ringcentral-integration/interfaces/Call.interface.ts index ceadadc848..24e511ba73 100644 --- a/packages/ringcentral-integration/interfaces/Call.interface.ts +++ b/packages/ringcentral-integration/interfaces/Call.interface.ts @@ -1,8 +1,12 @@ import type { CallResultsValue } from '../enums/callResults'; + +import type { + IWarmTransferInfo, + ActiveCallControlSessionData, +} from './ActiveSession.interface'; import type { Entity } from './Entity.interface'; import type { ActiveCall } from './Presence.model'; import type { NormalizedSession } from './Webphone.interface'; -import type { IWarmTransferInfo } from './ActiveSession.interface'; export interface CallerInfo { phoneNumber?: string; @@ -32,6 +36,8 @@ export interface NormalizedCall { duration?: number; warmTransferInfo?: IWarmTransferInfo; isRecording?: boolean; + isConferenceCall?: boolean; + conferenceParticipants?: ActiveCallControlSessionData['conferenceParticipants']; } export type NormalizedCalls = NormalizedCall[]; diff --git a/packages/ringcentral-integration/interfaces/CallErrors.interface.ts b/packages/ringcentral-integration/interfaces/CallErrors.interface.ts index 5b466a762c..e2b673da3a 100644 --- a/packages/ringcentral-integration/interfaces/CallErrors.interface.ts +++ b/packages/ringcentral-integration/interfaces/CallErrors.interface.ts @@ -1,4 +1,4 @@ import type { callErrors } from '../modules/Call'; export type CallErrorsKeys = keyof typeof callErrors; -export type CallErrorsType = typeof callErrors[CallErrorsKeys]; +export type CallErrorsType = (typeof callErrors)[CallErrorsKeys]; diff --git a/packages/ringcentral-integration/interfaces/Contact.model.ts b/packages/ringcentral-integration/interfaces/Contact.model.ts index 2fb7383ef9..65d6e4f5e7 100644 --- a/packages/ringcentral-integration/interfaces/Contact.model.ts +++ b/packages/ringcentral-integration/interfaces/Contact.model.ts @@ -29,6 +29,7 @@ export interface IContact extends Omit { extensionNumber?: string; profileImageUrl?: string; hidden?: boolean; + isCallQueueNumber?: boolean; presence?: ContactPresence | null; } @@ -41,6 +42,7 @@ export interface TypedPhoneNumber { contactId: string; profileImageUrl?: string; entityType: string; + isCallQueueNumber: boolean; } export interface TypedContact extends IContact { diff --git a/packages/ringcentral-integration/interfaces/Entity.interface.ts b/packages/ringcentral-integration/interfaces/Entity.interface.ts index b9f1343b18..8753e0afe7 100644 --- a/packages/ringcentral-integration/interfaces/Entity.interface.ts +++ b/packages/ringcentral-integration/interfaces/Entity.interface.ts @@ -21,4 +21,6 @@ export interface Entity { jobTitle?: string; email?: string; hidden?: boolean; + resourceType?: string; + isCallQueueNumber?: boolean; } diff --git a/packages/ringcentral-integration/interfaces/Presence.model.ts b/packages/ringcentral-integration/interfaces/Presence.model.ts index 41ad43033a..a6e92e1590 100644 --- a/packages/ringcentral-integration/interfaces/Presence.model.ts +++ b/packages/ringcentral-integration/interfaces/Presence.model.ts @@ -1,8 +1,8 @@ import type ActiveCallInfo from '@rc-ex/core/lib/definitions/ActiveCallInfo'; import type CallLogFromParty from '@rc-ex/core/lib/definitions/CallLogFromParty'; +import type CallLogRecord from '@rc-ex/core/lib/definitions/CallLogRecord'; import type CallLogToParty from '@rc-ex/core/lib/definitions/CallLogToParty'; import type GetPresenceInfo from '@rc-ex/core/lib/definitions/GetPresenceInfo'; -import type CallLogRecord from '@rc-ex/core/lib/definitions/CallLogRecord'; import type { ObjectMapValue } from '@ringcentral-integration/core/lib/ObjectMap'; import type { dndStatus } from '../enums/dndStatus'; @@ -31,10 +31,10 @@ export type ActiveCall = Pick< export interface PresenceInfoModel { activeCalls?: ActiveCall[]; - dndStatus?: ObjectMapValue | null; - meetingStatus?: GetPresenceInfo['meetingStatus'] | null; - presenceStatus?: ObjectMapValue | null; - telephonyStatus?: GetPresenceInfo['telephonyStatus'] | null; - userStatus?: GetPresenceInfo['userStatus'] | null; + dndStatus?: ObjectMapValue; + meetingStatus?: GetPresenceInfo['meetingStatus']; + presenceStatus?: ObjectMapValue; + telephonyStatus?: GetPresenceInfo['telephonyStatus']; + userStatus?: GetPresenceInfo['userStatus']; sequence?: number; } diff --git a/packages/ringcentral-integration/interfaces/Rcv.model.ts b/packages/ringcentral-integration/interfaces/Rcv.model.ts index e12e81ea45..279bfe1bc7 100644 --- a/packages/ringcentral-integration/interfaces/Rcv.model.ts +++ b/packages/ringcentral-integration/interfaces/Rcv.model.ts @@ -17,7 +17,7 @@ export interface RcvGSuiteMeetingModel { name: string; type: 0 | 1; allowJoinBeforeHost: boolean; - expiresIn: number; + expiresIn: number | null; isMeetingSecret?: boolean; meetingPassword?: string; } @@ -38,14 +38,14 @@ export interface RcVDialInNumberGET { } export interface RcVideoAPI { - id?: string; + id?: string | null; shortId?: string; extensionId?: string; accountId?: string; name: string; type: 0 | 1; startTime: Date; - expiresIn: number; + expiresIn: number | null; duration: number; allowJoinBeforeHost: boolean; muteAudio: boolean; diff --git a/packages/ringcentral-integration/interfaces/Webphone.interface.ts b/packages/ringcentral-integration/interfaces/Webphone.interface.ts index a2eaff6c38..9847f66dcc 100644 --- a/packages/ringcentral-integration/interfaces/Webphone.interface.ts +++ b/packages/ringcentral-integration/interfaces/Webphone.interface.ts @@ -1,9 +1,9 @@ -import type { WebPhoneSession as WebphoneSessionBase } from 'ringcentral-web-phone/lib/session'; -import type { Session as SessionBase } from 'ringcentral-call/lib/Session'; - import type { ObjectMapValue } from '@ringcentral-integration/core/lib/ObjectMap'; +import type { Session as SessionBase } from 'ringcentral-call/lib/Session'; +import type { WebPhoneSession as WebphoneSessionBase } from 'ringcentral-web-phone/lib/session'; import type { extendedControlStatus } from '../enums/extendedControlStatus'; + import type { Entity } from './Entity.interface'; export interface PartyData { @@ -37,7 +37,7 @@ export interface WebphoneSession extends WebphoneSessionBase { __rc_lastActiveTime: number; __rc_extendedControls?: string[]; __rc_extendedControlStatus: ObjectMapValue; - __rc_transferSessionId: string; + __rc_transferSessionId?: string; } export interface NormalizedSession { @@ -64,7 +64,7 @@ export interface NormalizedSession { recordStatus: string; contactMatch: Entity; minimized: boolean; - partyData: PartyData; + partyData: PartyData | null; lastActiveTime: number; cached: boolean; removed: boolean; diff --git a/packages/ringcentral-integration/interfaces/utilities.ts b/packages/ringcentral-integration/interfaces/utilities.ts new file mode 100644 index 0000000000..966dab1146 --- /dev/null +++ b/packages/ringcentral-integration/interfaces/utilities.ts @@ -0,0 +1,2 @@ +export type PartialRequired = Required> & + Pick>; diff --git a/packages/ringcentral-integration/jest.config.js b/packages/ringcentral-integration/jest.config.js index 8ce5a789c8..4ebdf55f6e 100644 --- a/packages/ringcentral-integration/jest.config.js +++ b/packages/ringcentral-integration/jest.config.js @@ -3,4 +3,5 @@ const baseConfig = require('@ringcentral-integration/test-utils/config/jest.conf module.exports = merge(baseConfig, { roots: ['/lib'], + setupFiles: ['/test/jest.setup.ts'], }); diff --git a/packages/ringcentral-integration/karma.conf.ts b/packages/ringcentral-integration/karma.conf.ts deleted file mode 100644 index 1c0da79fba..0000000000 --- a/packages/ringcentral-integration/karma.conf.ts +++ /dev/null @@ -1,135 +0,0 @@ -// Karma configuration -// Generated on Wed Nov 23 2016 15:54:23 GMT+0800 (CST) - -export default (config) => { - config.set({ - // base path that will be used to resolve all patterns (eg. files, exclude) - basePath: './', - - // frameworks to use - // available frameworks: https://npmjs.org/browse/keyword/karma-adapter - frameworks: ['mocha', 'chai', 'expect'], - - // list of files / patterns to load in the browser - files: [ - // 'modules/**/availabilityMonitorHelper.test.js', - 'modules/AvailabilityMonitor/*.test.js', - // 'integration-test/**/*spec.js', - ], - - // list of files to exclude - exclude: [ - 'enums/**/*.test.js', - 'lib/**/*.test.js', - // 'modules/**/*.test.js' - ], - - // preprocess matching files before serving them to the browser - // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor - preprocessors: { - 'modules/**/*.js': ['webpack'], - 'integration-test/**/*.js': ['webpack'], - }, - - // plugins: [ - // 'karma-webpack', - // 'karma-mocha', - // 'karma-chai', - // 'karma-expect', - // 'karma-mocha-reporter', - // 'karma-commonjs', - // 'karma-chrome-launcher' - // ], - - webpack: { - resolve: { - extensions: ['.js', '.ts'], - }, - module: { - rules: [ - { - test: /\.(js|ts)$/i, - use: ['babel-loader'], - exclude: /node_modules/, - }, - { - test: /\.json$/i, - use: 'json-loader', - }, - { - test: /\.ogg$/i, - use: 'url-loader?publicPath=./&name=audio/[name]_[hash].[ext]', - }, - ], - }, - }, - - webpackMiddleware: { - noInfo: true, - stats: { - chunks: false, - }, - }, - // test results reporter to use - // possible values: 'dots', 'progress' - // available reporters: https://npmjs.org/browse/keyword/karma-reporter - reporters: ['mocha', 'html', 'junit'], - - htmlReporter: { - outputDir: 'karma', // where to put the reports - templatePath: null, // set if you moved jasmine_template.html - focusOnFailures: true, // reports show failures on start - namedFiles: false, // name files instead of creating sub-directories - pageTitle: null, // page title for reports; browser info by default - urlFriendlyName: false, // simply replaces spaces with _ for files/dirs - reportName: 'report', // report summary filename; browser info by default - - // experimental - preserveDescribeNesting: false, // folded suites stay folded - foldAll: false, // reports start folded (only with preserveDescribeNesting) - }, - - junitReporter: { - outputDir: 'junit', // results will be saved as $outputDir/$browserName.xml - outputFile: undefined, // if included, results will be saved as $outputDir/$browserName/$outputFile - suite: '', // suite will become the package name attribute in xml testsuite element - useBrowserName: true, // add browser name to report and classes names - nameFormatter: undefined, // function (browser, result) to customize the name attribute in xml testcase element - classNameFormatter: undefined, // function (browser, result) to customize the classname attribute in xml testcase element - properties: {}, // key value pair of properties to add to the section of the report - }, - - // web server port - port: 9876, - - // enable / disable colors in the output (reporters and logs) - colors: true, - - // level of logging - // possible values: - // config.LOG_DISABLE || config.LOG_ERROR || - // config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG - logLevel: config.LOG_ERROR, - - // enable / disable watching file and executing tests whenever any file changes - autoWatch: true, - - // start these browsers - // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher - browsers: ['ChromeNoSandbox'], - customLaunchers: { - ChromeNoSandbox: { - base: 'ChromeHeadless', // update to Chrome if you want to run it with Chrome UI - flags: ['--no-sandbox'], - }, - }, - - // Continuous Integration mode - // if true, Karma captures browsers, runs the tests and exits - singleRun: true, - - // Concurrency level - // how many browser should be started simultaneous - concurrency: Infinity, - }); -}; diff --git a/packages/ringcentral-integration/lib/Analytics/segment.ts b/packages/ringcentral-integration/lib/Analytics/segment.ts index b601d7d0d9..114a99bc53 100644 --- a/packages/ringcentral-integration/lib/Analytics/segment.ts +++ b/packages/ringcentral-integration/lib/Analytics/segment.ts @@ -55,7 +55,6 @@ export default function init() { // stored as the first argument, so we can replay the data. analytics.factory = function (method: (...args: any[]) => void) { return function () { - // @ts-ignore // eslint-disable-next-line prefer-rest-params const args = Array.prototype.slice.call(arguments); args.unshift(method); diff --git a/packages/ringcentral-integration/lib/ChromeTransport/ClientTransport.ts b/packages/ringcentral-integration/lib/ChromeTransport/ClientTransport.ts index b6635c4441..7b85b48700 100644 --- a/packages/ringcentral-integration/lib/ChromeTransport/ClientTransport.ts +++ b/packages/ringcentral-integration/lib/ChromeTransport/ClientTransport.ts @@ -1,31 +1,65 @@ import * as uuid from 'uuid'; -import TransportBase from '../TransportBase'; +import { + TransportBase, + type BaseEventEnum, + type TransportBaseProps, +} from '../TransportBase'; + +import { CONNECT_PORT_NAME, TRANSPORT_NAME } from './constants'; /* global chrome */ +export interface ClientTransportProps + extends Omit {} + +type PromiseCallback = { + resolve: (result: any) => void; + reject: (reason: any) => void; +}; + export class ClientTransport extends TransportBase { - constructor(options) { + _port: chrome.runtime.Port; + _requests = new Map(); + + constructor(options: ClientTransportProps = {}) { super({ ...options, - name: 'ChromeTransport', + name: TRANSPORT_NAME, }); - this._requests = new Map(); - this._port = chrome.runtime.connect({ name: 'transport' }); + + this._port = chrome.runtime.connect({ name: CONNECT_PORT_NAME }); this._port.onMessage.addListener( - ({ type, payload, requestId, result, error }) => { + ({ + type, + payload, + requestId, + result, + error, + }: { + type: keyof BaseEventEnum; + payload: unknown; + requestId: string; + result?: unknown; + error?: string; + }) => { switch (type) { case this._events.push: - if (payload) { - this.emit(this._events.push, payload); + { + if (payload) { + this.emit(this._events.push, payload); + } } break; case this._events.response: - if (requestId && this._requests.has(requestId)) { - if (error) { - this._requests.get(requestId).reject(new Error(error)); - } else { - this._requests.get(requestId).resolve(result); + { + const callback = this._requests.get(requestId); + if (callback) { + if (error) { + callback.reject(new Error(error)); + } else { + callback.resolve(result); + } } } break; @@ -36,7 +70,7 @@ export class ClientTransport extends TransportBase { ); } - async request({ payload }) { + async request({ payload }: { payload: unknown }) { const requestId = uuid.v4(); let promise = new Promise((resolve, reject) => { this._requests.set(requestId, { @@ -49,9 +83,12 @@ export class ClientTransport extends TransportBase { payload, }); }); - let timeout = setTimeout(() => { + let timeout: NodeJS.Timeout | null = setTimeout(() => { timeout = null; - this._requests.get(requestId).reject(new Error(this._events.timeout)); + const callback = this._requests.get(requestId); + if (callback) { + callback.reject(new Error(this._events.timeout)); + } }, this._timeout); promise = promise .then((result) => { @@ -63,6 +100,7 @@ export class ClientTransport extends TransportBase { if (timeout) clearTimeout(timeout); this._requests.delete(requestId); return Promise.reject( + // @ts-expect-error TS(2571): Object is of type 'unknown'. new Error(`${payload.functionPath}: ${error.message}`), ); }); diff --git a/packages/ringcentral-integration/lib/ChromeTransport/ServerTransport.ts b/packages/ringcentral-integration/lib/ChromeTransport/ServerTransport.ts index dcfa60c9c8..8d30dc227f 100644 --- a/packages/ringcentral-integration/lib/ChromeTransport/ServerTransport.ts +++ b/packages/ringcentral-integration/lib/ChromeTransport/ServerTransport.ts @@ -1,24 +1,31 @@ -import { forEach, reduce } from 'ramda'; +import { proxyChrome } from '../ObjectProxy'; +import { TransportBase, type TransportBaseProps } from '../TransportBase'; -import TransportBase from '../TransportBase'; +import { CONNECT_PORT_NAME, TRANSPORT_NAME } from './constants'; /* global chrome */ +export interface ServerTransportProps + extends Omit {} + export class ServerTransport extends TransportBase { - constructor(options) { + _activeTabIds = new Set(); + _ports = new Set(); + _requests = new Map(); + + constructor(options: ServerTransportProps = {}) { super({ ...options, - name: 'ChromeTransport', + name: TRANSPORT_NAME, }); - this._ports = new Set(); - this._requests = new Map(); - // Keep active tabs up to date - this._activeTabIds = null; + // Get current tabs this._getActiveTabIds(); - chrome.tabs.onActivated.addListener(() => this._getActiveTabIds()); + // Keep active tabs up to date + proxyChrome.tabs.onActivated.addListener(() => this._getActiveTabIds()); + chrome.runtime.onConnect.addListener((port) => { - if (port.name === 'transport') { + if (port.name === CONNECT_PORT_NAME) { this._ports.add(port); port.onMessage.addListener(({ type, requestId, payload }) => { if (type === this._events.request && requestId && payload) { @@ -36,7 +43,15 @@ export class ServerTransport extends TransportBase { }); } - response({ requestId, result, error }) { + response({ + requestId, + result, + error, + }: { + requestId: string; + result?: unknown; + error?: Error | string; + }) { const port = this._requests.get(requestId); if (port) { this._requests.delete(requestId); @@ -52,46 +67,35 @@ export class ServerTransport extends TransportBase { } } - push({ payload }) { + push({ payload }: { payload: unknown }) { const message = { type: this._events.push, payload }; // Since postMessage is really expensive, // we only send messages to those ports on active tabs. - forEach((port) => { + this._ports.forEach((port) => { if ( - port.sender && - port.sender.tab && + port.sender?.tab?.id && // send to all instances if app failed to query active tabs - (!this._activeTabIds || this._activeTabIds[port.sender.tab.id]) + (!this._activeTabIds.size || this._activeTabIds.has(port.sender.tab.id)) ) { port.postMessage(message); } - }, this._ports); + }); } _getActiveTabIds() { - return new Promise((resolve) => { - try { - chrome.tabs.query({ active: true }, (tabs) => { - this._activeTabIds = Array.isArray(tabs) - ? // convert tabs array into tabs id truth mapping - reduce( - (acc, tab) => { - if (tab.id) { - acc[tab.id] = true; - } - return acc; - }, - {}, - tabs, - ) - : null; - resolve(!!this._activeTabIds); - }); - } catch (error: any /** TODO: confirm with instanceof */) { - this._activeTabIds = null; - console.log(error); - resolve(false); - } - }); + try { + proxyChrome.tabs.query({ active: true }, (tabs) => { + this._activeTabIds.clear(); + if (Array.isArray(tabs)) { + tabs.forEach((tab) => { + if (tab.id) { + this._activeTabIds.add(tab.id); + } + }); + } + }); + } catch (error) { + console.log('[ServerTransport]', error); + } } } diff --git a/packages/ringcentral-integration/lib/ChromeTransport/constants.ts b/packages/ringcentral-integration/lib/ChromeTransport/constants.ts new file mode 100644 index 0000000000..4ce9797804 --- /dev/null +++ b/packages/ringcentral-integration/lib/ChromeTransport/constants.ts @@ -0,0 +1,2 @@ +export const CONNECT_PORT_NAME = 'transport'; +export const TRANSPORT_NAME = 'ChromeTransport'; diff --git a/packages/ringcentral-integration/lib/DataMatcherV2/DataMatcher.ts b/packages/ringcentral-integration/lib/DataMatcherV2/DataMatcher.ts index 4cfc13d154..5ef69d118f 100644 --- a/packages/ringcentral-integration/lib/DataMatcherV2/DataMatcher.ts +++ b/packages/ringcentral-integration/lib/DataMatcherV2/DataMatcher.ts @@ -1,5 +1,3 @@ -import { all, filter, forEach } from 'ramda'; - import { action, computed, @@ -7,9 +5,11 @@ import { state, storage, } from '@ringcentral-integration/core'; +import { all, filter, forEach } from 'ramda'; import { Library } from '../di'; import proxify from '../proxy/proxify'; + import type { DataMatcherOptions, Deps, @@ -264,7 +264,7 @@ abstract class DataMatcher extends RcModuleV2 { let matching: MatchPromises | MatchQueue; if (!ignoreQueue && this._matchPromises.has(name)) { - // @ts-ignore + // @ts-expect-error TS(2322): Type 'MatchPromises | undefined' is not assigna... Remove this comment to see the full error message matching = this._matchPromises.get(name); promises.push(matching.promise); matching.queries.forEach((item) => { @@ -274,7 +274,7 @@ abstract class DataMatcher extends RcModuleV2 { let queue: MatchQueue; if (!ignoreQueue && this._matchQueues.has(name)) { - // @ts-ignore + // @ts-expect-error TS(2322): Type 'MatchQueue | undefined' is not assignable to... Remove this comment to see the full error message queue = this._matchQueues.get(name); promises.push(queue.promise); queue.queries.forEach((item) => { @@ -301,7 +301,7 @@ abstract class DataMatcher extends RcModuleV2 { queries: newQueries, }), ); - // @ts-ignore + // @ts-expect-error TS(2454): Variable 'matching' is used before being assigned. } else if (!matching) { matching = { promise: this._fetchMatchResult({ @@ -311,13 +311,13 @@ abstract class DataMatcher extends RcModuleV2 { queries: newQueries, }; promises.push(matching.promise); - // @ts-ignore + // @ts-expect-error TS(2454): Variable 'queue' is used before being assigned. } else if (!queue) { const promise = (async () => { await matching.promise; const promise = this._fetchMatchResult({ name, - // @ts-ignore + // @ts-expect-error TS(2454): Variable 'queue' is used before being assigned. queries: queue.queries, }); this._matchQueues.delete(name); @@ -327,7 +327,6 @@ abstract class DataMatcher extends RcModuleV2 { queries: newQueries, promise, }; - queue; this._matchQueues.set(name, queue); promises.push(queue.promise); } else { diff --git a/packages/ringcentral-integration/lib/LocalForageStorage.ts b/packages/ringcentral-integration/lib/LocalForageStorage.ts index ed4efa651e..9088b3e204 100644 --- a/packages/ringcentral-integration/lib/LocalForageStorage.ts +++ b/packages/ringcentral-integration/lib/LocalForageStorage.ts @@ -7,6 +7,7 @@ import type { AsyncStorage, StorageItem, } from '../interfaces/GenericStorage.interface'; + import { MemoryStorage } from './MemoryStorage'; export class LocalForageStorage extends EventEmitter implements AsyncStorage { diff --git a/packages/ringcentral-integration/lib/LoggerBase/LoggerBase.ts b/packages/ringcentral-integration/lib/LoggerBase/LoggerBase.ts index e538bc53c1..0dc72607c1 100644 --- a/packages/ringcentral-integration/lib/LoggerBase/LoggerBase.ts +++ b/packages/ringcentral-integration/lib/LoggerBase/LoggerBase.ts @@ -6,6 +6,7 @@ import { } from '@ringcentral-integration/core'; import { proxify } from '../proxy/proxify'; + import type { Deps, LogOptions, Options } from './LoggerBase.interface'; import { convertListToMap, defaultIdentityFunction } from './loggerBaseHelper'; diff --git a/packages/ringcentral-integration/lib/MemoryStorage/index.test.ts b/packages/ringcentral-integration/lib/MemoryStorage/index.test.ts index bd79e81c37..c049a9be15 100644 --- a/packages/ringcentral-integration/lib/MemoryStorage/index.test.ts +++ b/packages/ringcentral-integration/lib/MemoryStorage/index.test.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; - import { MemoryStorage } from '.'; +import { expect } from 'chai'; describe('MemoryStorage', () => { it('should be a constructor function', () => { diff --git a/packages/ringcentral-integration/lib/MessageTransport/EventEmitterTransporter.ts b/packages/ringcentral-integration/lib/MessageTransport/EventEmitterTransporter.ts index acccf4de2e..e5df28fe03 100644 --- a/packages/ringcentral-integration/lib/MessageTransport/EventEmitterTransporter.ts +++ b/packages/ringcentral-integration/lib/MessageTransport/EventEmitterTransporter.ts @@ -14,7 +14,7 @@ export const TRANSPORTER_DIRECTION = { } as const; export type TransporterDirection = - typeof TRANSPORTER_DIRECTION[keyof typeof TRANSPORTER_DIRECTION]; + (typeof TRANSPORTER_DIRECTION)[keyof typeof TRANSPORTER_DIRECTION]; /** * @param direction if direction is 'toExternal', meaning this instance is created in internal adapter, need in eventEmitter diff --git a/packages/ringcentral-integration/lib/MessageTransport/MessageTransport.ts b/packages/ringcentral-integration/lib/MessageTransport/MessageTransport.ts index 88087099f0..d910e3ed9e 100644 --- a/packages/ringcentral-integration/lib/MessageTransport/MessageTransport.ts +++ b/packages/ringcentral-integration/lib/MessageTransport/MessageTransport.ts @@ -1,8 +1,8 @@ import * as uuid from 'uuid'; -import TransportBase from '../TransportBase'; -import type { TransportBaseProps } from '../TransportBase/TransportBase'; +import { TransportBase, type TransportBaseProps } from '../TransportBase'; import type { TransportResponseData } from '../TransportInteractionBase'; + import type { TransporterDirection } from './MessageTransporters'; import { EventEmitterTransporter, @@ -36,7 +36,7 @@ export const TRANSPORTER_TYPES = { } as const; export type TransporterTypes = - typeof TRANSPORTER_TYPES[keyof typeof TRANSPORTER_TYPES]; + (typeof TRANSPORTER_TYPES)[keyof typeof TRANSPORTER_TYPES]; export interface MessageTransportProps { transporterDirection?: TransporterDirection; @@ -55,7 +55,7 @@ export default class MessageTransport extends TransportBase { private _addReceiver: Transporter['addReceiver']; private _createEmitter: Transporter['createEmitter']; private _targetWindow: Window; - private _origin: string; + private _origin?: string; private _myRequests: Map; private _othersRequests: Map; private _postMessage: any; @@ -175,6 +175,7 @@ export default class MessageTransport extends TransportBase { }); let timeout = setTimeout(() => { + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Timeout'. timeout = null; this._myRequests .get(requestId) diff --git a/packages/ringcentral-integration/lib/ObjectProxy/ObjectProxy.ts b/packages/ringcentral-integration/lib/ObjectProxy/ObjectProxy.ts new file mode 100644 index 0000000000..f64c954fc4 --- /dev/null +++ b/packages/ringcentral-integration/lib/ObjectProxy/ObjectProxy.ts @@ -0,0 +1,40 @@ +export class ObjectProxy { + _chainSteps: Array = []; + + constructor(private _target: Target, private _delegateTarget?: Target) { + this._target = _target ?? {}; + } + + getValue(target: any) { + let value = target; + for (const step of this._chainSteps) { + if (!value) break; + value = value[step]; + } + return value; + } + + create() { + let lastValue = this._target; + return new Proxy(this._target, { + get: (target, property, receiver) => { + // Chaining + this._chainSteps.push(property); + const value = + this.getValue(this._delegateTarget) ?? this.getValue(this._target); + // Check value + if (typeof value !== 'object' || value === null) { + // Stop chaining + this._chainSteps = []; + return typeof value === 'function' ? value.bind(lastValue) : value; + } + // Keep on chaining + lastValue = value; + return receiver; + }, + set: () => { + throw new Error('Setting properties is not allowed'); + }, + }); + } +} diff --git a/packages/ringcentral-integration/lib/ObjectProxy/index.ts b/packages/ringcentral-integration/lib/ObjectProxy/index.ts new file mode 100644 index 0000000000..18a4ffcffe --- /dev/null +++ b/packages/ringcentral-integration/lib/ObjectProxy/index.ts @@ -0,0 +1,2 @@ +export * from './ObjectProxy'; +export * from './proxyChrome'; diff --git a/packages/ringcentral-integration/lib/ObjectProxy/proxyChrome.ts b/packages/ringcentral-integration/lib/ObjectProxy/proxyChrome.ts new file mode 100644 index 0000000000..abf720144e --- /dev/null +++ b/packages/ringcentral-integration/lib/ObjectProxy/proxyChrome.ts @@ -0,0 +1,5 @@ +import { ObjectProxy } from './ObjectProxy'; + +const chromeObjectProxy = new ObjectProxy(chrome, global.apiProxy?.chrome); + +export const proxyChrome: typeof chrome = chromeObjectProxy.create(); diff --git a/packages/ringcentral-integration/lib/Pollable.ts b/packages/ringcentral-integration/lib/Pollable.ts index 993a3d2a9e..2184dbe054 100644 --- a/packages/ringcentral-integration/lib/Pollable.ts +++ b/packages/ringcentral-integration/lib/Pollable.ts @@ -1,10 +1,12 @@ -import { Library } from './di'; import RcModule from './RcModule'; +import { Library } from './di'; @Library({ deps: [{ dep: 'PollableOptions', optional: true }], }) export default class Pollable extends RcModule { + _tabManager: any; + _timeoutId: any; constructor({ ...options }) { super({ ...options, @@ -36,19 +38,23 @@ export default class Pollable extends RcModule { if (this._timeoutId) clearTimeout(this._timeoutId); } + // @ts-expect-error TS(2365): Operator '+' cannot be applied to types 'void' and... Remove this comment to see the full error message _startPolling(t = this.timestamp + this.pollingInterval + 10 - Date.now()) { this._clearTimeout(); this._timeoutId = setTimeout(() => { this._timeoutId = null; if (!this._tabManager || this._tabManager.active) { + // @ts-expect-error TS(1345): An expression of type 'void' cannot be tested for ... Remove this comment to see the full error message if (!this.timestamp || Date.now() - this.timestamp > this.ttl) { this.fetchData(); } else { this._startPolling(); } + // @ts-expect-error TS(1345): An expression of type 'void' cannot be tested for ... Remove this comment to see the full error message } else if (this.timestamp && Date.now() - this.timestamp < this.ttl) { this._startPolling(); } else { + // @ts-expect-error TS(2345): Argument of type 'void' is not assignable to param... Remove this comment to see the full error message this._startPolling(this.timeToRetry); } }, t); @@ -58,6 +64,7 @@ export default class Pollable extends RcModule { this._clearTimeout(); this._timeoutId = setTimeout(() => { this._timeoutId = null; + // @ts-expect-error TS(1345): An expression of type 'void' cannot be tested for ... Remove this comment to see the full error message if (!this.timestamp || Date.now() - this.timestamp > this.ttl) { if (!this._tabManager || this._tabManager.active) { this.fetchData(); @@ -66,6 +73,7 @@ export default class Pollable extends RcModule { this._retry(); } } + // @ts-expect-error TS(2769): No overload matches this call. }, t); } } diff --git a/packages/ringcentral-integration/lib/RcModule/index.ts b/packages/ringcentral-integration/lib/RcModule/index.ts index b09ed1a467..9d98985915 100644 --- a/packages/ringcentral-integration/lib/RcModule/index.ts +++ b/packages/ringcentral-integration/lib/RcModule/index.ts @@ -1,12 +1,11 @@ // @ts-nocheck -import type { Store } from 'redux'; - import { identifierKey, RcModuleV2, storeKey, } from '@ringcentral-integration/core'; import { ObjectMap } from '@ringcentral-integration/core/lib/ObjectMap'; +import type { Store } from 'redux'; import moduleStatuses from '../../enums/moduleStatuses'; import proxyStatuses from '../../enums/proxyStatuses'; diff --git a/packages/ringcentral-integration/lib/RingCentralClient/RingCentralClient.ts b/packages/ringcentral-integration/lib/RingCentralClient/RingCentralClient.ts index 0465413ad2..eddb9eaaf7 100644 --- a/packages/ringcentral-integration/lib/RingCentralClient/RingCentralClient.ts +++ b/packages/ringcentral-integration/lib/RingCentralClient/RingCentralClient.ts @@ -1,13 +1,12 @@ +import type { SDK } from '@ringcentral/sdk'; import { Client } from 'ringcentral-client'; +import PathSegment from 'ringcentral-client/build/PathSegment'; import Account from 'ringcentral-client/build/paths/Account'; import ClientInfo from 'ringcentral-client/build/paths/ClientInfo'; import Dictionary from 'ringcentral-client/build/paths/Dictionary'; import Glip from 'ringcentral-client/build/paths/Glip'; import NumberParser from 'ringcentral-client/build/paths/NumberParser'; import Subscription from 'ringcentral-client/build/paths/Subscription'; -import PathSegment from 'ringcentral-client/build/PathSegment'; - -import type { SDK } from '@ringcentral/sdk'; // TODO: make 'ringcentral-client' support JS SDK v4 or replace it class RestPrefix extends PathSegment { diff --git a/packages/ringcentral-integration/lib/SingleTabBroadcastChannel/SingleTabBroadcastChannel.ts b/packages/ringcentral-integration/lib/SingleTabBroadcastChannel/SingleTabBroadcastChannel.ts index 296fbabcdd..30750a2f95 100644 --- a/packages/ringcentral-integration/lib/SingleTabBroadcastChannel/SingleTabBroadcastChannel.ts +++ b/packages/ringcentral-integration/lib/SingleTabBroadcastChannel/SingleTabBroadcastChannel.ts @@ -3,7 +3,9 @@ import { waitUntilTo } from '../../utils'; type BroadcastChannelMessage = (ev: MessageEvent) => any; export class SingleTabBroadcastChannel { + // @ts-expect-error TS(2564): Property '_req' has no initializer and is not defi... Remove this comment to see the full error message private _req: BroadcastChannel; + // @ts-expect-error TS(2564): Property '_res' has no initializer and is not defi... Remove this comment to see the full error message private _res: BroadcastChannel; get tabId() { @@ -67,6 +69,7 @@ export class SingleTabBroadcastChannel { } catch (error: any /** TODO: confirm with instanceof */) { return null; } finally { + // @ts-expect-error TS(2454): Variable '_resolve' is used before being assigned. _resolve(); this._res.removeEventListener('message', listener); } diff --git a/packages/ringcentral-integration/lib/SleepDetection/SleepDetection.ts b/packages/ringcentral-integration/lib/SleepDetection/SleepDetection.ts index 514adfb13b..4f70a148d5 100644 --- a/packages/ringcentral-integration/lib/SleepDetection/SleepDetection.ts +++ b/packages/ringcentral-integration/lib/SleepDetection/SleepDetection.ts @@ -1,7 +1,6 @@ -import { EventEmitter } from 'events'; - import type { ObjectMapValue } from '@ringcentral-integration/core/lib/ObjectMap'; import { ObjectMap } from '@ringcentral-integration/core/lib/ObjectMap'; +import { EventEmitter } from 'events'; const DEFAULT_INTERVAL = 20 * 1000; // For chrome 88 timer-throttling https://developer.chrome.com/blog/timer-throttling-in-chrome-88/ diff --git a/packages/ringcentral-integration/lib/StorageBase/StorageBase.ts b/packages/ringcentral-integration/lib/StorageBase/StorageBase.ts index 2f8f6ff590..02fa028182 100644 --- a/packages/ringcentral-integration/lib/StorageBase/StorageBase.ts +++ b/packages/ringcentral-integration/lib/StorageBase/StorageBase.ts @@ -1,6 +1,3 @@ -import type { Action, Reducer, ReducersMapObject } from 'redux'; -import { combineReducers } from 'redux'; - import { action, RcModuleV2, @@ -9,18 +6,21 @@ import { stateKey, } from '@ringcentral-integration/core'; import { ObjectMap } from '@ringcentral-integration/core/lib/ObjectMap'; +import type { Action, Reducer, ReducersMapObject } from 'redux'; +import { combineReducers } from 'redux'; -import { Module } from '../di'; import { SynchronizedStorage } from '../SynchronizedStorage'; -import type { ActionTypesBase } from './actionTypesBase'; -import { actionTypesBase } from './actionTypesBase'; -import { getDataReducer } from './getStorageReducer'; +import { Module } from '../di'; + import type { Deps, IStorage, IStorageBaseOptions, StorageBaseOptions, } from './StorageBase.interface'; +import type { ActionTypesBase } from './actionTypesBase'; +import { actionTypesBase } from './actionTypesBase'; +import { getDataReducer } from './getStorageReducer'; @Module({ name: 'StorageBase', diff --git a/packages/ringcentral-integration/lib/StorageBase/getStorageReducer.ts b/packages/ringcentral-integration/lib/StorageBase/getStorageReducer.ts index 2f69b47719..78771161d0 100644 --- a/packages/ringcentral-integration/lib/StorageBase/getStorageReducer.ts +++ b/packages/ringcentral-integration/lib/StorageBase/getStorageReducer.ts @@ -1,6 +1,5 @@ -import type { Reducer, ReducersMapObject } from 'redux'; - import { usmAction } from '@ringcentral-integration/core'; +import type { Reducer, ReducersMapObject } from 'redux'; import type { ActionTypesBase } from './actionTypesBase'; diff --git a/packages/ringcentral-integration/lib/SynchronizedStorage.ts b/packages/ringcentral-integration/lib/SynchronizedStorage.ts index 3e5735e369..eaeef36a57 100644 --- a/packages/ringcentral-integration/lib/SynchronizedStorage.ts +++ b/packages/ringcentral-integration/lib/SynchronizedStorage.ts @@ -6,6 +6,7 @@ import type { GenericStorage, StorageItem, } from '../interfaces/GenericStorage.interface'; + import { MemoryStorage } from './MemoryStorage'; // TODO: experiment with a managed list of keys to watch rather than matching every event with diff --git a/packages/ringcentral-integration/lib/Tabbie.ts b/packages/ringcentral-integration/lib/Tabbie.ts index a9bf0a2ca7..c77ea35725 100644 --- a/packages/ringcentral-integration/lib/Tabbie.ts +++ b/packages/ringcentral-integration/lib/Tabbie.ts @@ -1,9 +1,8 @@ // @ts-nocheck +import { ObjectMap } from '@ringcentral-integration/core/lib/ObjectMap'; import { EventEmitter } from 'events'; import * as uuid from 'uuid'; -import { ObjectMap } from '@ringcentral-integration/core/lib/ObjectMap'; - const HEART_BEAT_INTERVAL = 1000; // heartbeat older than HEART_BEAT_EXPIRE will be gc'ed // chrome and firefox throttles intervals when inactive expire time of 2000 @@ -180,7 +179,7 @@ export class Tabbie { } private _bindUnloadListener() { - window.addEventListener('unload', () => { + window.addEventListener('pagehide', () => { clearInterval(this._gcIntervalId); clearInterval(this._heartBeatIntervalId); localStorage.removeItem(this._heartBeatKey); @@ -191,7 +190,7 @@ export class Tabbie { } send(event: string, ...args: any[]) { - if (!this.enabled) { + if (!window.document || !this.enabled) { return; } diff --git a/packages/ringcentral-integration/lib/TransportBase/TransportBase.ts b/packages/ringcentral-integration/lib/TransportBase/TransportBase.ts index 9d0f71afc6..f071d36152 100644 --- a/packages/ringcentral-integration/lib/TransportBase/TransportBase.ts +++ b/packages/ringcentral-integration/lib/TransportBase/TransportBase.ts @@ -1,6 +1,5 @@ -import { EventEmitter } from 'events'; - import { ObjectMap } from '@ringcentral-integration/core/lib/ObjectMap'; +import { EventEmitter } from 'events'; export interface TransportBaseProps { name: string; @@ -13,9 +12,10 @@ export type BaseEventEnum = Record< string >; -export default class TransportBase extends EventEmitter { +export class TransportBase extends EventEmitter { _timeout: number; _events: BaseEventEnum; + constructor({ name, prefix, timeout = 90 * 1000 }: TransportBaseProps) { super(); diff --git a/packages/ringcentral-integration/lib/TransportBase/index.ts b/packages/ringcentral-integration/lib/TransportBase/index.ts index 6585517888..d96c1b04fe 100644 --- a/packages/ringcentral-integration/lib/TransportBase/index.ts +++ b/packages/ringcentral-integration/lib/TransportBase/index.ts @@ -1,4 +1 @@ -import TransportBase from './TransportBase'; - -export default TransportBase; export * from './TransportBase'; diff --git a/packages/ringcentral-integration/lib/TransportInteractionBase/TransportInteractionBase.ts b/packages/ringcentral-integration/lib/TransportInteractionBase/TransportInteractionBase.ts index 205d2d9cfb..19405fcb4f 100644 --- a/packages/ringcentral-integration/lib/TransportInteractionBase/TransportInteractionBase.ts +++ b/packages/ringcentral-integration/lib/TransportInteractionBase/TransportInteractionBase.ts @@ -8,6 +8,7 @@ import type { export default class TransportInteractionBase { _transportEvents: transportEvents = {}; + // @ts-expect-error TS(2564): Property '_transport' has no initializer and is no... Remove this comment to see the full error message _transport: Transport; registerTransportEvent({ key, func }: TransportEvent) { @@ -34,6 +35,7 @@ export default class TransportInteractionBase { const emitData: TransportResponseData = { requestId, result: null, + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'string | Er... Remove this comment to see the full error message error: null, }; diff --git a/packages/ringcentral-integration/lib/batchApiHelper.ts b/packages/ringcentral-integration/lib/batchApiHelper.ts index b9c70839b5..0ea9894f86 100644 --- a/packages/ringcentral-integration/lib/batchApiHelper.ts +++ b/packages/ringcentral-integration/lib/batchApiHelper.ts @@ -1,7 +1,6 @@ -import * as uuid from 'uuid'; - import Client from '@ringcentral/sdk/lib/http/Client'; import type Platform from '@ringcentral/sdk/lib/platform/Platform'; +import * as uuid from 'uuid'; interface Options { headers: Record; diff --git a/packages/ringcentral-integration/lib/callLogHelpers.ts b/packages/ringcentral-integration/lib/callLogHelpers.ts index 1d99a0d16a..b2dc56c5c6 100644 --- a/packages/ringcentral-integration/lib/callLogHelpers.ts +++ b/packages/ringcentral-integration/lib/callLogHelpers.ts @@ -1,12 +1,11 @@ -import dayjs from 'dayjs'; -import { find, isEmpty, reduce } from 'ramda'; - +import type ActiveCallInfoWithoutSIP from '@rc-ex/core/lib/definitions/ActiveCallInfoWithoutSIP'; import { ObjectMap } from '@ringcentral-integration/core/lib/ObjectMap'; import { isSameLocalNumber, isValidNumber, } from '@ringcentral-integration/phone-number'; -import type ActiveCallInfoWithoutSIP from '@rc-ex/core/lib/definitions/ActiveCallInfoWithoutSIP'; +import dayjs from 'dayjs'; +import { find, isEmpty, reduce } from 'ramda'; import { callActions } from '../enums/callActions'; import callDirections from '../enums/callDirections'; @@ -28,18 +27,28 @@ export function isOutbound(call: { direction?: string } = {}) { return call.direction === callDirections.outbound; } +// get caller id name for webphone session export function getWebphoneSessionDisplayName( currentSession: Call['webphoneSession'], ) { - // TODO: just return null temporary, wait check api can be use with platform - return null; - // if (!currentSession) { - // return null; - // } + if (!currentSession) { + return null; + } + + return currentSession.direction === callDirections.inbound + ? currentSession.fromUserName + : currentSession.toUserName; +} + +// get caller id name for telephone +export function getTelephoneDisplayName(call: Call) { + if (!call) { + return null; + } - // return currentSession.direction === callDirections.inbound - // ? currentSession.fromUserName - // : currentSession.toUserName; + return call.direction === callDirections.inbound + ? call.fromName + : call.toName; } /* status helpers */ @@ -74,7 +83,7 @@ const callResultsToMissedMap = ObjectMap.fromObject( ), ); -export function isMissed(call: Partial = {}) { +export function isMissed(call: Pick = {}) { return !!callResultsToMissedMap[call.result!]; } @@ -239,7 +248,7 @@ export function removeInboundRingOutLegs(calls: ActiveCall[]) { if ( inbound.from && // TODO: should confirm that type, not met - // @ts-expect-error ts-migrate(2306) FIXME: type not match + // @ts-expect-error TS(2769): No overload matches this call. isValidNumber(inbound.from?.phoneNumber) && isSameLocalNumber( inbound.from.phoneNumber, diff --git a/packages/ringcentral-integration/lib/channel.ts b/packages/ringcentral-integration/lib/channel.ts new file mode 100644 index 0000000000..e9e4e36412 --- /dev/null +++ b/packages/ringcentral-integration/lib/channel.ts @@ -0,0 +1,70 @@ +import { proxyChrome } from '@ringcentral-integration/commons/lib/ObjectProxy'; + +type Handler = (message: any, sender: chrome.runtime.MessageSender) => void; + +type Packet = { + [s: string]: any; +}; + +export class Channel { + _mux: Record = {}; + _type: string; + + constructor(type: string) { + this._type = type; + this._make(); + } + + select(actionType: string, handler: Handler) { + this._mux[actionType] = handler; + return this; + } + + async send(packet: Packet) { + return await chrome.runtime.sendMessage({ + type: this._type, + ...packet, + }); + } + + async broadcast(packet: Packet) { + const tabs = await proxyChrome.tabs.query({ discarded: false }); + // Keep behavior of ignoring exception + await Promise.allSettled( + tabs.map((tab) => + proxyChrome.tabs.sendMessage(tab.id!, { + type: this._type, + ...packet, + }), + ), + ); + } + + _make() { + chrome.runtime.onMessage.addListener( + ( + message: any, + sender: chrome.runtime.MessageSender, + sendResponse: (response?: any) => void, + ) => { + const { type, action } = message; + if (type === this._type) { + const handler = this._mux[action]; + if (typeof handler === 'function') { + Promise.resolve(handler(message, sender)) + .then((value) => { + sendResponse(value); + }) + .catch((err) => { + console.error(err); + sendResponse(); + }); + // Async + return true; + } + } + return false; + }, + ); + } +} diff --git a/packages/ringcentral-integration/lib/contactHelper.ts b/packages/ringcentral-integration/lib/contactHelper.ts index a7df395731..ef7490827b 100644 --- a/packages/ringcentral-integration/lib/contactHelper.ts +++ b/packages/ringcentral-integration/lib/contactHelper.ts @@ -1,6 +1,5 @@ -import { reduce } from 'ramda'; - import { formatSameSiteExtension } from '@ringcentral-integration/phone-number/lib/format'; +import { reduce } from 'ramda'; import { phoneTypes } from '../enums/phoneTypes'; import type { @@ -10,6 +9,7 @@ import type { TypedContact, TypedPhoneNumber, } from '../interfaces/Contact.model'; + import { isBlank } from './isBlank'; export const AllContactSourceName = 'all'; @@ -203,6 +203,7 @@ export function getSearchForPhoneNumbers({ name: contact.name || `${contact.firstName} ${contact.lastName}`, type: contact.type, phoneNumber: phoneNumber!, + isCallQueueNumber: !!contact.isCallQueueNumber, phoneType: phoneType!.replace('Phone', ''), profileImageUrl: contact.profileImage?.uri, entityType, diff --git a/packages/ringcentral-integration/lib/createApp.ts b/packages/ringcentral-integration/lib/createApp.ts index 641d8a6c64..5072a980b0 100644 --- a/packages/ringcentral-integration/lib/createApp.ts +++ b/packages/ringcentral-integration/lib/createApp.ts @@ -1,7 +1,6 @@ -import type { StoreEnhancer } from 'redux'; - import type { RcModuleV2 } from '@ringcentral-integration/core'; import { createApp as createAppWithRcModuleV2 } from '@ringcentral-integration/core'; +import type { StoreEnhancer } from 'redux'; import { Injector } from './di'; diff --git a/packages/ringcentral-integration/lib/createRefreshTokenHelper.ts b/packages/ringcentral-integration/lib/createRefreshTokenHelper.ts new file mode 100644 index 0000000000..e36c9808e4 --- /dev/null +++ b/packages/ringcentral-integration/lib/createRefreshTokenHelper.ts @@ -0,0 +1,152 @@ +import type { SDK, ApiError } from '@ringcentral/sdk'; + +import validateIsOffline from './validateIsOffline'; + +type Platform = ReturnType; + +export type CheckRefreshTokenResult = { + refreshTokenValid: boolean; + isOffline: boolean; + resStatus: number; + refreshTokenExpiresTime?: number; + errorMessage?: string; +}; + +/** + * follow the logic in sdk, but more logic to prevent error logout + */ +export const createRefreshTokenHelper = ( + getPlatform: () => Platform, + logger: Console, +) => { + const getRefreshTokenState = async ( + error: ApiError, + ): Promise => { + const isOffline = validateIsOffline(error?.message); + const resStatus = Number(error?.response?.status); + + const platform = getPlatform(); + const authData = await platform.auth().data(); + const tokenDataValid = await platform.auth().refreshTokenValid(); + + logger.log('check platform.auth().refreshTokenValid():', tokenDataValid); + + const refreshTokenValid = Boolean( + (isOffline || resStatus >= 500) && tokenDataValid, + ); + return { + refreshTokenValid, + isOffline, + resStatus, + refreshTokenExpiresTime: authData?.refresh_token_expire_time, + errorMessage: error?.message?.substring?.(0, 100), // avoid too long msg to be sent to analytics + }; + }; + + const clearExpiredToken = () => { + const platform = getPlatform(); + try { + platform + .auth() + .setData({ access_token: '', expires_in: '-1', refresh_token: '' }); + logger.log('Expired token data clear'); + } catch (error: any) { + logger.error('clearExpiredToken|error', error?.message); + } + }; + + /** + * almost same as sdk platform.loggedIn(), but with onError callback, and when network not ready should not go into logout state + * @param onCompleted callback when refresh token completed + */ + const loggedIn = async ( + onCompleted?: (state: CheckRefreshTokenResult) => void, + ) => { + // let refreshDelayMs = 0; + const platform = getPlatform(); + try { + // TODO: those will be open in next MR, we ensure all exist be pass. + // * when use that loggedIn method will set that delay be zero 0, using that to avoid first check loggedIn need delay 100ms + // in SDK node_modules/@ringcentral/sdk/src/platform/Platform.ts:565 _refresh inner delay + // refreshDelayMs = platform['_refreshDelayMs']; + // platform['_refreshDelayMs'] = 0; + + if (platform['_authProxy']) { + await platform.get('/restapi/v1.0/client-info'); + return true; + } + + await platform.ensureLoggedIn(); + return true; + } catch (error) { + // normal error after logout, skip this + if ( + !(error instanceof Error) || + error?.message === 'Refresh token is missing' + ) { + return false; + } + + logger.error( + 'Auth::ensureLoggedIn', + error?.message, + (error as ApiError)?.response?.status, + ); + + const state = await getRefreshTokenState(error); + + logger.log('ensureLoggedIn.State:', state); + + onCompleted?.(state); + + if (error?.message === 'Refresh token has expired') { + clearExpiredToken(); + } + + return state.refreshTokenValid; + } finally { + // * restore the delay + // platform['_refreshDelayMs'] = refreshDelayMs; + } + }; + + /** + * process refresh token error + * @returns the state of session expired + */ + const processRefreshError = async ({ + error, + refreshTokenValid, + resStatus, + onSessionExpired, + }: Partial & { + error: ApiError; + onSessionExpired: () => void; + }) => { + const platform = getPlatform(); + + const isAARError = + resStatus === 403 && + (await error.response?.clone().json())?.errors?.some( + ({ errorCode = '' } = {}) => errorCode === 'OAU-167', + ); + + if ( + !isAARError && + !refreshTokenValid && + (await platform.auth().data()).access_token !== '' + ) { + onSessionExpired(); + // clean the cache so the error doesn't show again + platform['_cache'].clean(); + return true; + } + }; + + return { + getRefreshTokenState, + clearExpiredToken, + loggedIn, + processRefreshError, + }; +}; diff --git a/packages/ringcentral-integration/lib/createSimpleReducer.ts b/packages/ringcentral-integration/lib/createSimpleReducer.ts index bc6283c1a4..c746266887 100644 --- a/packages/ringcentral-integration/lib/createSimpleReducer.ts +++ b/packages/ringcentral-integration/lib/createSimpleReducer.ts @@ -1,5 +1,5 @@ -export default function createSimpleReducer(type, data) { - return (state = null, action) => { +export default function createSimpleReducer(type: any, data: any) { + return (state = null, action: any) => { if (action.type === type) return action[data]; return state; }; diff --git a/packages/ringcentral-integration/lib/di/index.ts b/packages/ringcentral-integration/lib/di/index.ts index 92861d1eba..05017a690c 100644 --- a/packages/ringcentral-integration/lib/di/index.ts +++ b/packages/ringcentral-integration/lib/di/index.ts @@ -1,4 +1,5 @@ import Library from './decorators/library'; + /** * Module Dependency Injection */ diff --git a/packages/ringcentral-integration/lib/di/injector.ts b/packages/ringcentral-integration/lib/di/injector.ts index 1e8b58a52b..130f55241b 100644 --- a/packages/ringcentral-integration/lib/di/injector.ts +++ b/packages/ringcentral-integration/lib/di/injector.ts @@ -1,11 +1,10 @@ // @ts-nocheck -import { combineReducers } from 'redux'; - import { identifierKey, RcModuleV2, usmAction, } from '@ringcentral-integration/core'; +import { combineReducers } from 'redux'; import Container from './container'; import { diff --git a/packages/ringcentral-integration/lib/di/registry/registry.ts b/packages/ringcentral-integration/lib/di/registry/registry.ts index e4ab12fcdd..af1e63be4c 100644 --- a/packages/ringcentral-integration/lib/di/registry/registry.ts +++ b/packages/ringcentral-integration/lib/di/registry/registry.ts @@ -1,6 +1,7 @@ // @ts-nocheck import { isArray, isFunction, isObject } from '../utils/is_type'; import { assert, getParentClass } from '../utils/utils'; + import ModuleRegistry from './module_registry'; import ProviderRegistry from './provider_registry'; diff --git a/packages/ringcentral-integration/lib/ensureExist.ts b/packages/ringcentral-integration/lib/ensureExist.ts index 3562d936bc..0d6d433fa4 100644 --- a/packages/ringcentral-integration/lib/ensureExist.ts +++ b/packages/ringcentral-integration/lib/ensureExist.ts @@ -1,6 +1,11 @@ -export default function ensureExist(module: T, moduleName: keyof T) { +export default function ensureExist( + this: any, + module: T, + moduleName: keyof T, +) { if (!module) { throw new Error( + // @ts-expect-error TS(2731): Implicit conversion of a 'symbol' to a 'string' wi... Remove this comment to see the full error message `'${moduleName}' is a required dependency for '${this.constructor.name}'`, ); } diff --git a/packages/ringcentral-integration/lib/formatDuration/formatDuration.test.ts b/packages/ringcentral-integration/lib/formatDuration/formatDuration.test.ts index 00d12c3bd8..531c9a0d47 100644 --- a/packages/ringcentral-integration/lib/formatDuration/formatDuration.test.ts +++ b/packages/ringcentral-integration/lib/formatDuration/formatDuration.test.ts @@ -1,6 +1,5 @@ -import { expect } from 'chai'; - import { formatDuration } from '.'; +import { expect } from 'chai'; describe('formatDuration', () => { it('should be a function', () => { diff --git a/packages/ringcentral-integration/lib/formatNumber/formatNumber.ts b/packages/ringcentral-integration/lib/formatNumber/formatNumber.ts index e93e062465..3203574427 100644 --- a/packages/ringcentral-integration/lib/formatNumber/formatNumber.ts +++ b/packages/ringcentral-integration/lib/formatNumber/formatNumber.ts @@ -17,7 +17,7 @@ export type FormatNumberParams = { * @function * @description Format phone numbers */ -export default function formatNumber({ +export function formatNumber({ phoneNumber, removeExtension = false, countryCode = 'US', diff --git a/packages/ringcentral-integration/lib/formatNumber/index.ts b/packages/ringcentral-integration/lib/formatNumber/index.ts index c566a4fe72..6064bf6972 100644 --- a/packages/ringcentral-integration/lib/formatNumber/index.ts +++ b/packages/ringcentral-integration/lib/formatNumber/index.ts @@ -1,4 +1 @@ -import formatNumber from './formatNumber'; - -export default formatNumber; -export { formatNumber }; +export * from './formatNumber'; diff --git a/packages/ringcentral-integration/lib/getCSPDomains.ts b/packages/ringcentral-integration/lib/getCSPDomains.ts index 4c9bb36bac..1b5f69181b 100644 --- a/packages/ringcentral-integration/lib/getCSPDomains.ts +++ b/packages/ringcentral-integration/lib/getCSPDomains.ts @@ -1,13 +1,10 @@ -import url from 'url'; - // remove this when dynamic configs has been phased out -const DYNAMIC_CONFIG_DOMAIN = 'https://apps.ringcentral.com'; - -export const getCSPDomains = (loaderBaseUrl: string) => { - const { protocol, hostname, port } = url.parse(loaderBaseUrl); - let domains = `${protocol}//${hostname}${port ? `:${port}` : ''}`; - if (domains !== DYNAMIC_CONFIG_DOMAIN) { - domains = `${domains} ${DYNAMIC_CONFIG_DOMAIN}`; +export const getCSPDomains = (loaderBaseUrl?: string) => { + const domains = new Set(); + if (loaderBaseUrl) { + const urlObj = new URL(loaderBaseUrl); + domains.add(urlObj.origin); } - return domains; + domains.add('https://apps.ringcentral.com'); + return Array.from(domains).join(' '); }; diff --git a/packages/ringcentral-integration/lib/getCallingOption.ts b/packages/ringcentral-integration/lib/getCallingOption.ts new file mode 100644 index 0000000000..02d81d9732 --- /dev/null +++ b/packages/ringcentral-integration/lib/getCallingOption.ts @@ -0,0 +1,17 @@ +import { callingModes } from '@ringcentral-integration/commons/modules/CallingSettings'; + +export const getCallingOption = (callingMode: string | null) => { + switch (callingMode) { + case callingModes.softphone: + return 'RingCentral Phone'; + case callingModes.ringout: + return 'RingOut'; + case callingModes.webphone: + return 'Browser'; + case callingModes.jupiter: + case callingModes.jupiterUniversalLink: + return 'RingCentral App'; + default: + return null; + } +}; diff --git a/packages/ringcentral-integration/lib/getIntlDateTimeFormatter/getIntlDateTimeFormatter.ts b/packages/ringcentral-integration/lib/getIntlDateTimeFormatter/getIntlDateTimeFormatter.ts index e346867189..50c0f7c2aa 100644 --- a/packages/ringcentral-integration/lib/getIntlDateTimeFormatter/getIntlDateTimeFormatter.ts +++ b/packages/ringcentral-integration/lib/getIntlDateTimeFormatter/getIntlDateTimeFormatter.ts @@ -54,7 +54,10 @@ export default function getIntlDateTimeFormatter({ type = isToday(utcTimestamp) ? 'time' : 'date', }: DateTimeFormatterParams) => { if (!utcTimestamp) { - console.warn('timestamp should not be empty'); + // Too much helpless message. Ignore it for test env. + if (process.env.NODE_ENV !== 'test') { + console.warn('timestamp should not be empty'); + } return null; } switch (type) { diff --git a/packages/ringcentral-integration/lib/getModuleStatusReducer/index.ts b/packages/ringcentral-integration/lib/getModuleStatusReducer/index.ts index 0869519674..5af1a1037c 100644 --- a/packages/ringcentral-integration/lib/getModuleStatusReducer/index.ts +++ b/packages/ringcentral-integration/lib/getModuleStatusReducer/index.ts @@ -1,7 +1,7 @@ import moduleStatuses from '../../enums/moduleStatuses'; -export default function getModuleStatusReducer(types) { - return (state = moduleStatuses.pending, { type }) => { +export default function getModuleStatusReducer(types: any) { + return (state = moduleStatuses.pending, { type }: any) => { switch (type) { case types.init: return moduleStatuses.initializing; diff --git a/packages/ringcentral-integration/lib/getProxyStatusReducer/index.ts b/packages/ringcentral-integration/lib/getProxyStatusReducer/index.ts index 214187585e..0788f8dcb5 100644 --- a/packages/ringcentral-integration/lib/getProxyStatusReducer/index.ts +++ b/packages/ringcentral-integration/lib/getProxyStatusReducer/index.ts @@ -1,7 +1,7 @@ import proxyStatuses from '../../enums/proxyStatuses'; -export default function getProxyStatusReducer(types) { - return (state = proxyStatuses.pending, { type }) => { +export default function getProxyStatusReducer(types: any) { + return (state = proxyStatuses.pending, { type }: any) => { switch (type) { case types.proxyInit: return proxyStatuses.initializing; diff --git a/packages/ringcentral-integration/lib/getter.ts b/packages/ringcentral-integration/lib/getter.ts index d7e9fccc92..3b536955ef 100644 --- a/packages/ringcentral-integration/lib/getter.ts +++ b/packages/ringcentral-integration/lib/getter.ts @@ -4,30 +4,40 @@ const WRAPPER = Symbol('wrapper'); * @deprecated */ export default function getter( - prototype, - property, - { initializer, value, get }, + prototype: any, + property: any, + { initializer, value, get }: any, ) { console.warn('"@getter" is deprecated. Use "@computed()" instead.'); return { configurable: true, enumerable: true, + // @ts-expect-error TS(7023): 'get' implicitly has return type 'any' because it ... Remove this comment to see the full error message get() { + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message if (!this[WRAPPER]) { + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message this[WRAPPER] = {}; } + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message if (!this[WRAPPER][property]) { const targetSymbol = Symbol(`${property}-target`); + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message this[targetSymbol] = initializer ? initializer.call(this) : value || get; + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message this[WRAPPER][property] = + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message typeof this[targetSymbol] === 'function' - ? () => this[targetSymbol]() - : () => this[targetSymbol]; + ? // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message + () => this[targetSymbol]() + : // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message + () => this[targetSymbol]; } + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message return this[WRAPPER][property](); }, }; diff --git a/packages/ringcentral-integration/lib/messageHelper/index.test.js b/packages/ringcentral-integration/lib/messageHelper/index.test.js index e4a15712e3..6624025729 100644 --- a/packages/ringcentral-integration/lib/messageHelper/index.test.js +++ b/packages/ringcentral-integration/lib/messageHelper/index.test.js @@ -1,8 +1,18 @@ import { expect } from 'chai'; -import * as messageHelper from '.'; - -const { sortByDate } = messageHelper; +import { + filterNumbers, + getMyNumberFromMessage, + getRecipientNumbersFromMessage, + getRecipients, + messageIsAcceptable, + messageIsDeleted, + messageIsFax, + messageIsTextMessage, + messageIsVoicemail, + normalizeInstantEvent, + sortByDate, +} from '.'; describe('filterNumbers', () => { it('should return filtered numbers with phoneNumber', () => { @@ -13,7 +23,7 @@ describe('filterNumbers', () => { const filterNumber = { phoneNumber: '+1234567890', }; - const result = messageHelper.filterNumbers(numbers, filterNumber); + const result = filterNumbers(numbers, filterNumber); expect(result).to.deep.equal([{ phoneNumber: '+1234567891' }]); }); @@ -22,7 +32,7 @@ describe('filterNumbers', () => { const filterNumber = { extensionNumber: '1234', }; - const result = messageHelper.filterNumbers(numbers, filterNumber); + const result = filterNumbers(numbers, filterNumber); expect(result).to.deep.equal([{ extensionNumber: '12345' }]); }); }); @@ -30,19 +40,19 @@ describe('filterNumbers', () => { describe('messageIsDeleted', () => { it('should return true when message is deleted', () => { const message = { availability: 'Deleted' }; - const result = messageHelper.messageIsDeleted(message); + const result = messageIsDeleted(message); expect(result).to.equal(true); }); it('should return false when message is Alive', () => { const message = { availability: 'Alive' }; - const result = messageHelper.messageIsDeleted(message); + const result = messageIsDeleted(message); expect(result).to.equal(false); }); it('should return true when message is Purged', () => { const message = { availability: 'Purged' }; - const result = messageHelper.messageIsDeleted(message); + const result = messageIsDeleted(message); expect(result).to.equal(true); }); }); @@ -50,25 +60,25 @@ describe('messageIsDeleted', () => { describe('messageIsTextMessage', () => { it('should return true when message type is SMS', () => { const message = { type: 'SMS' }; - const result = messageHelper.messageIsTextMessage(message); + const result = messageIsTextMessage(message); expect(result).to.equal(true); }); it('should return true when message type is Pager', () => { const message = { type: 'Pager' }; - const result = messageHelper.messageIsTextMessage(message); + const result = messageIsTextMessage(message); expect(result).to.equal(true); }); it('should return false when message type is Fax', () => { const message = { type: 'Fax' }; - const result = messageHelper.messageIsTextMessage(message); + const result = messageIsTextMessage(message); expect(result).to.equal(false); }); it('should return false when message type is VoiceMail', () => { const message = { type: 'VoiceMail' }; - const result = messageHelper.messageIsTextMessage(message); + const result = messageIsTextMessage(message); expect(result).to.equal(false); }); }); @@ -76,13 +86,13 @@ describe('messageIsTextMessage', () => { describe('messageIsFax', () => { it('should return true when message type is Fax', () => { const message = { type: 'Fax', availability: 'Alive' }; - const result = messageHelper.messageIsFax(message); + const result = messageIsFax(message); expect(result).to.equal(true); }); it('should return false when message type is SMS', () => { const message = { type: 'SMS', availability: 'Alive' }; - const result = messageHelper.messageIsFax(message); + const result = messageIsFax(message); expect(result).to.equal(false); }); }); @@ -90,13 +100,13 @@ describe('messageIsFax', () => { describe('messageIsVoicemail', () => { it('should return true when message type is VoiceMail', () => { const message = { type: 'VoiceMail', availability: 'Alive' }; - const result = messageHelper.messageIsVoicemail(message); + const result = messageIsVoicemail(message); expect(result).to.equal(true); }); it('should return false when message type is SMS', () => { const message = { type: 'SMS', availability: 'Alive' }; - const result = messageHelper.messageIsVoicemail(message); + const result = messageIsVoicemail(message); expect(result).to.equal(false); }); }); @@ -104,43 +114,43 @@ describe('messageIsVoicemail', () => { describe('messageIsAcceptable', () => { it('should return true when message type is SMS and Alive', () => { const message = { type: 'SMS', availability: 'Alive' }; - const result = messageHelper.messageIsAcceptable(message); + const result = messageIsAcceptable(message); expect(result).to.equal(true); }); it('should return true when message type is Pager and Alive', () => { const message = { type: 'Pager', availability: 'Alive' }; - const result = messageHelper.messageIsAcceptable(message); + const result = messageIsAcceptable(message); expect(result).to.equal(true); }); it('should return true when message type is Fax and Alive', () => { const message = { type: 'Fax', availability: 'Alive' }; - const result = messageHelper.messageIsAcceptable(message); + const result = messageIsAcceptable(message); expect(result).to.equal(true); }); it('should return true when message type is VoiceMail and Alive', () => { const message = { type: 'VoiceMail', availability: 'Alive' }; - const result = messageHelper.messageIsAcceptable(message); + const result = messageIsAcceptable(message); expect(result).to.equal(true); }); it('should return true when message type is SMS and Deleted', () => { const message = { type: 'SMS', availability: 'Deleted' }; - const result = messageHelper.messageIsAcceptable(message); + const result = messageIsAcceptable(message); expect(result).to.equal(false); }); it('should return true when message type is Pager and Deleted', () => { const message = { type: 'Pager', availability: 'Deleted' }; - const result = messageHelper.messageIsAcceptable(message); + const result = messageIsAcceptable(message); expect(result).to.equal(false); }); it('should return false when message type is Fax and Deleted', () => { const message = { type: 'Fax', availability: 'Deleted' }; - const result = messageHelper.messageIsAcceptable(message); + const result = messageIsAcceptable(message); expect(result).to.equal(false); }); @@ -150,7 +160,7 @@ describe('messageIsAcceptable', () => { availability: 'Alive', direction: 'Inbound', }; - const result = messageHelper.messageIsAcceptable(message); + const result = messageIsAcceptable(message); expect(result).to.equal(true); }); @@ -160,7 +170,7 @@ describe('messageIsAcceptable', () => { direction: 'Outbound', messageStatus: 'Queued', }; - const result = messageHelper.messageIsAcceptable(message); + const result = messageIsAcceptable(message); expect(result).to.equal(false); }); it('should return false when message type is Fax and sending failed', () => { @@ -169,12 +179,12 @@ describe('messageIsAcceptable', () => { direction: 'Outbound', messageStatus: 'SendingFailed', }; - const result = messageHelper.messageIsAcceptable(message); + const result = messageIsAcceptable(message); expect(result).to.equal(false); }); it('should return false when message type is VoiceMail and Deleted', () => { const message = { type: 'VoiceMail', availability: 'Deleted' }; - const result = messageHelper.messageIsAcceptable(message); + const result = messageIsAcceptable(message); expect(result).to.equal(false); }); }); @@ -191,7 +201,7 @@ describe('getMyNumberFromMessage', () => { ], from: { phoneNumber: '+1234567891' }, }; - const result = messageHelper.getMyNumberFromMessage({ + const result = getMyNumberFromMessage({ message, myExtensionNumber: '1234', }); @@ -209,7 +219,7 @@ describe('getMyNumberFromMessage', () => { ], from: { phoneNumber: '+1234567891' }, }; - const result = messageHelper.getMyNumberFromMessage({ + const result = getMyNumberFromMessage({ message, myExtensionNumber: '1234', }); @@ -227,7 +237,7 @@ describe('getMyNumberFromMessage', () => { ], from: { extensionNumber: '1234' }, }; - const result = messageHelper.getMyNumberFromMessage({ + const result = getMyNumberFromMessage({ message, myExtensionNumber: '1234', }); @@ -245,7 +255,7 @@ describe('getMyNumberFromMessage', () => { ], from: { extensionNumber: '12345' }, }; - const result = messageHelper.getMyNumberFromMessage({ + const result = getMyNumberFromMessage({ message, myExtensionNumber: '1234', }); @@ -266,7 +276,7 @@ describe('getRecipientNumbersFromMessage', () => { from: { phoneNumber: '+1234567891' }, }; const myNumber = { phoneNumber: '+1234567891' }; - const result = messageHelper.getRecipientNumbersFromMessage({ + const result = getRecipientNumbersFromMessage({ message, myNumber, }); @@ -285,7 +295,7 @@ describe('getRecipientNumbersFromMessage', () => { from: { phoneNumber: '+1234567891' }, }; const myNumber = { phoneNumber: '+1234567890' }; - const result = messageHelper.getRecipientNumbersFromMessage({ + const result = getRecipientNumbersFromMessage({ message, myNumber, }); @@ -304,7 +314,7 @@ describe('getRecipientNumbersFromMessage', () => { from: { extensionNumber: '1234' }, }; const myNumber = { extensionNumber: '1234' }; - const result = messageHelper.getRecipientNumbersFromMessage({ + const result = getRecipientNumbersFromMessage({ message, myNumber, }); @@ -323,7 +333,7 @@ describe('getRecipientNumbersFromMessage', () => { from: { extensionNumber: '12345' }, }; const myNumber = { extensionNumber: '1234' }; - const result = messageHelper.getRecipientNumbersFromMessage({ + const result = getRecipientNumbersFromMessage({ message, myNumber, }); @@ -342,7 +352,7 @@ describe('getRecipientNumbersFromMessage', () => { from: { extensionNumber: '1234' }, }; const myNumber = { extensionNumber: '1234' }; - const result = messageHelper.getRecipientNumbersFromMessage({ + const result = getRecipientNumbersFromMessage({ message, myNumber, }); @@ -362,7 +372,7 @@ describe('getRecipients', () => { ], from: { phoneNumber: '+1234567891' }, }; - const result = messageHelper.getRecipients({ + const result = getRecipients({ message, myExtensionNumber: '1234', }); @@ -380,7 +390,7 @@ describe('getRecipients', () => { ], from: { extensionNumber: '1234' }, }; - const result = messageHelper.getRecipients({ + const result = getRecipients({ message, myExtensionNumber: '1234', }); @@ -405,3 +415,22 @@ describe('sortByDate', () => { }); }); }); + +describe('normalizeInstantEvent', () => { + it('should be a function', () => { + expect(normalizeInstantEvent).to.be.a('function'); + }); + it('should convert the "id" and "conversationId" property to number', () => { + const mockObj = { + body: { + id: '123', + conversationId: '456', + }, + }; + const normalizedObj = normalizeInstantEvent(mockObj); + expect(normalizedObj.id).to.be.a('number'); + expect(normalizedObj.id).to.equal(123); + expect(normalizedObj.conversationId).to.be.a('number'); + expect(normalizedObj.conversationId).to.equal(456); + }); +}); diff --git a/packages/ringcentral-integration/lib/messageHelper/messageHelper.interface.ts b/packages/ringcentral-integration/lib/messageHelper/messageHelper.interface.ts index 84c0e42993..7df91de4ab 100644 --- a/packages/ringcentral-integration/lib/messageHelper/messageHelper.interface.ts +++ b/packages/ringcentral-integration/lib/messageHelper/messageHelper.interface.ts @@ -15,5 +15,5 @@ export interface FaxAttachment { export interface SortEntity { matchOrder?: number; - creationTime: number; + creationTime?: number; } diff --git a/packages/ringcentral-integration/lib/messageHelper/messageHelper.ts b/packages/ringcentral-integration/lib/messageHelper/messageHelper.ts index 06e9146dbb..82cb5fefd0 100644 --- a/packages/ringcentral-integration/lib/messageHelper/messageHelper.ts +++ b/packages/ringcentral-integration/lib/messageHelper/messageHelper.ts @@ -1,10 +1,12 @@ import type GetMessageInfoResponse from '@rc-ex/core/lib/definitions/GetMessageInfoResponse'; +import type InstantMessageEvent from '@rc-ex/core/lib/definitions/InstantMessageEvent'; import type MessageAttachmentInfo from '@rc-ex/core/lib/definitions/MessageAttachmentInfo'; import type MessageStoreCallerInfoResponseFrom from '@rc-ex/core/lib/definitions/MessageStoreCallerInfoResponseFrom'; import type MessageStoreCallerInfoResponseTo from '@rc-ex/core/lib/definitions/MessageStoreCallerInfoResponseTo'; import { messageTypes } from '../../enums/messageTypes'; import type { Message } from '../../interfaces/MessageStore.model'; + import type { Correspondent, FaxAttachment, @@ -243,8 +245,8 @@ export function getNumbersFromMessage({ } export function sortByDate(a: SortEntity, b: SortEntity) { - const ta = new Date(a.creationTime).getTime(); - const tb = new Date(b.creationTime).getTime(); + const ta = new Date(a.creationTime!).getTime(); + const tb = new Date(b.creationTime!).getTime(); return tb - ta; } @@ -328,6 +330,18 @@ export function sortByCreationTime( : 1; } +export function normalizeInstantEvent( + event: InstantMessageEvent, +): GetMessageInfoResponse { + const { id = '', conversationId = '', type, ...message } = event.body!; + return { + ...message, + id: Number(id), + conversationId: Number(conversationId), + type: type as any, + }; +} + export function normalizeRecord(record: GetMessageInfoResponse): Message { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { uri, ...newRecord } = record; diff --git a/packages/ringcentral-integration/lib/parseCallbackUri.ts b/packages/ringcentral-integration/lib/parseCallbackUri.ts deleted file mode 100644 index 8db27cff57..0000000000 --- a/packages/ringcentral-integration/lib/parseCallbackUri.ts +++ /dev/null @@ -1,20 +0,0 @@ -import url from 'url'; - -/** - * @function - * @param {String} callbackUri - * @return {String} code - */ -export default function parseCallbackUri(callbackUri) { - const { query } = url.parse(callbackUri, true); - if (query.error) { - const error = new Error(query.error); - for (const key in query) { - if (Object.prototype.hasOwnProperty.call(query, key)) { - error[key] = query[key]; - } - } - throw error; - } - return query.code; -} diff --git a/packages/ringcentral-integration/lib/phoneTypeHelper.ts b/packages/ringcentral-integration/lib/phoneTypeHelper.ts index 50e95ee50a..3ac6721608 100644 --- a/packages/ringcentral-integration/lib/phoneTypeHelper.ts +++ b/packages/ringcentral-integration/lib/phoneTypeHelper.ts @@ -1,6 +1,6 @@ -import { addIndex, filter, reduce, sort } from 'ramda'; import type PhoneNumberResource from '@rc-ex/core/lib/definitions/PhoneNumberResource'; import { ObjectMap } from '@ringcentral-integration/core/lib/ObjectMap'; +import { addIndex, filter, reduce, sort } from 'ramda'; import { phoneTypes } from '../enums/phoneTypes'; import type { PhoneNumberModel } from '../interfaces/PhoneNumber.model'; diff --git a/packages/ringcentral-integration/lib/processI18n.ts b/packages/ringcentral-integration/lib/processI18n.ts index 19e0a37686..67380797ed 100644 --- a/packages/ringcentral-integration/lib/processI18n.ts +++ b/packages/ringcentral-integration/lib/processI18n.ts @@ -1,5 +1,3 @@ -import { keys, map, reduce } from 'ramda'; - import I18n, { DEFAULT_LOCALE, PSEUDO_LOCALE, @@ -7,6 +5,7 @@ import I18n, { import { getLanguageFromLocale } from '@ringcentral-integration/i18n/lib/getLanguageFromLocale'; import toPseudoString from '@ringcentral-integration/i18n/lib/toPseudoString'; import type { LocaleCode } from '@ringcentral-integration/locale-settings'; +import { keys, map, reduce } from 'ramda'; import type { I18nStrings } from '../modules/Brand/BrandConfig.interface'; import { I18nFlag } from '../modules/Brand/BrandConfig.interface'; diff --git a/packages/ringcentral-integration/lib/proxy/getProxyClient.ts b/packages/ringcentral-integration/lib/proxy/getProxyClient.ts index a11732ce8c..b64471a31d 100644 --- a/packages/ringcentral-integration/lib/proxy/getProxyClient.ts +++ b/packages/ringcentral-integration/lib/proxy/getProxyClient.ts @@ -1,19 +1,24 @@ import * as uuid from 'uuid'; -import ensureExist from '../ensureExist'; import RcModule from '../RcModule'; +import ensureExist from '../ensureExist'; + import baseActionTypes from './baseActionTypes'; import getProxyClientReducer from './getProxyClientReducer'; import { pushStates } from './handleProxyAction'; -const defaultVerifyModuleFunc = (module) => module instanceof RcModule; +const defaultVerifyModuleFunc = (module: any) => module instanceof RcModule; export default function getProxyClient( - createTarget, + createTarget: any, verifyModuleFunc = defaultVerifyModuleFunc, ) { return class extends RcModule { - constructor({ transport, ...options }) { + _id: any; + _syncPromise: any; + _target: any; + _transport: any; + constructor({ transport, ...options }: any) { super({ ...options, actionTypes: baseActionTypes, @@ -31,6 +36,7 @@ export default function getProxyClient( // getState: () => this.state.target, // getProxyState: () => this.state.proxy, // }); + // @ts-expect-error TS(2345): Argument of type 'string' is not assignable to par... Remove this comment to see the full error message this._transport = ensureExist.call(this, transport, 'transport'); this._setTransport(this._target); for (const subModule in this._target) { @@ -45,7 +51,9 @@ export default function getProxyClient( return this._target[subModule]; }, }); - this[subModule]._getStateV2 = (state, key) => state.target[key]; + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message + this[subModule]._getStateV2 = (state: any, key: any) => + state.target[key]; } } @@ -57,7 +65,7 @@ export default function getProxyClient( }); } - _setTransport(target) { + _setTransport(target: any) { target._transport = this._transport; target._proxyActionTypes = this.actionTypes; target._suppressInit = true; @@ -98,7 +106,7 @@ export default function getProxyClient( return this._syncPromise; } - _initialize(target) { + _initialize(target: any) { if ( typeof target.initializeProxy === 'function' && !target._proxyInitialized @@ -119,11 +127,12 @@ export default function getProxyClient( } } + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message async initialize() { // initialize the instance before sync to avoid history object from // becoming out of sync this._initialize(this._target); - this._transport.on(this._transport.events.push, async (payload) => { + this._transport.on(this._transport.events.push, async (payload: any) => { if (payload.type === this.actionTypes.action) { if (this._syncPromise) await this._syncPromise; if (payload.actionNumber === this.state.actionNumber + 1) { diff --git a/packages/ringcentral-integration/lib/proxy/getProxyClientReducer.ts b/packages/ringcentral-integration/lib/proxy/getProxyClientReducer.ts index 52384ca25d..ce2dacb141 100644 --- a/packages/ringcentral-integration/lib/proxy/getProxyClientReducer.ts +++ b/packages/ringcentral-integration/lib/proxy/getProxyClientReducer.ts @@ -2,8 +2,8 @@ import { combineReducers } from 'redux'; import { dropStates } from './handleProxyAction'; -export function getLastActionReducer({ types }) { - return (state = null, { type, action, lastAction }) => { +export function getLastActionReducer({ types }: any) { + return (state = null, { type, action, lastAction }: any) => { switch (type) { case types.action: return dropStates(action); @@ -14,8 +14,8 @@ export function getLastActionReducer({ types }) { } }; } -export function getActionNumberReducer({ types }) { - return (state = -1, { type, actionNumber }) => { +export function getActionNumberReducer({ types }: any) { + return (state = -1, { type, actionNumber }: any) => { switch (type) { case types.action: case types.sync: @@ -25,10 +25,10 @@ export function getActionNumberReducer({ types }) { } }; } -export function getTargetReducer({ targetReducer, types }) { +export function getTargetReducer({ targetReducer, types }: any) { return ( state = targetReducer(undefined, { type: types.initModule }), - { type, target, action }, + { type, target, action }: any, ) => { switch (type) { case types.action: @@ -45,7 +45,7 @@ export default function getProxyClientReducer({ targetReducer, proxyReducer, types, -}) { +}: any) { return combineReducers({ target: getTargetReducer({ targetReducer, types }), proxy: proxyReducer, diff --git a/packages/ringcentral-integration/lib/proxy/getProxyServer.ts b/packages/ringcentral-integration/lib/proxy/getProxyServer.ts index a10136a11f..1628ab5fd5 100644 --- a/packages/ringcentral-integration/lib/proxy/getProxyServer.ts +++ b/packages/ringcentral-integration/lib/proxy/getProxyServer.ts @@ -1,16 +1,20 @@ -import ensureExist from '../ensureExist'; import RcModule from '../RcModule'; +import ensureExist from '../ensureExist'; + import baseActionTypes from './baseActionTypes'; import getProxyServerReducer from './getProxyServerReducer'; -const defaultVerifyModuleFunc = (module) => module instanceof RcModule; +const defaultVerifyModuleFunc = (module: any) => module instanceof RcModule; +// @ts-expect-error TS(4094): Property '_getState' of exported class expression ... Remove this comment to see the full error message export default function getProxyServer( - createTarget, + createTarget: any, verifyModuleFunc = defaultVerifyModuleFunc, ) { return class extends RcModule { - constructor({ transport, ...options }) { + _target: any; + _transport: any; + constructor({ transport, ...options }: any) { super({ ...options, actionTypes: baseActionTypes, @@ -31,10 +35,13 @@ export default function getProxyServer( return this._target[subModule]; }, }); - this[subModule]._getStateV2 = (state, key) => state.target[key]; + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message + this[subModule]._getStateV2 = (state: any, key: any) => + state.target[key]; } } + // @ts-expect-error TS(2345): Argument of type 'string' is not assignable to par... Remove this comment to see the full error message this._transport = ensureExist.call(this, transport, 'transport'); this._reducer = getProxyServerReducer({ moduleReducer: this._target.reducer, @@ -47,14 +54,14 @@ export default function getProxyServer( async ({ requestId, payload: { type, functionPath, args, actionNumber }, - }) => { + }: any) => { switch (type) { case this.actionTypes.execute: { const [...pathTokens] = functionPath.split('.').slice(1); const fnName = pathTokens.pop(); let target = this._target; - pathTokens.forEach((token) => { + pathTokens.forEach((token: any) => { target = target[token]; }); try { diff --git a/packages/ringcentral-integration/lib/proxy/getProxyServerReducer.ts b/packages/ringcentral-integration/lib/proxy/getProxyServerReducer.ts index 2dacec16f9..3f60a8a4b4 100644 --- a/packages/ringcentral-integration/lib/proxy/getProxyServerReducer.ts +++ b/packages/ringcentral-integration/lib/proxy/getProxyServerReducer.ts @@ -7,7 +7,7 @@ export default function getProxyServerReducer({ moduleReducer, transport, prefix, -}) { +}: any) { const actionTypes = ObjectMap.prefixValues(baseActionTypes, prefix); return ( state = { @@ -17,7 +17,7 @@ export default function getProxyServerReducer({ lastAction: null, actionNumber: -1, }, - action, + action: any, ) => { if (!action) return state; const nextActionNumber = state.actionNumber + 1; diff --git a/packages/ringcentral-integration/lib/proxy/handleProxyAction.ts b/packages/ringcentral-integration/lib/proxy/handleProxyAction.ts index 026e7984ed..e629e87ea5 100644 --- a/packages/ringcentral-integration/lib/proxy/handleProxyAction.ts +++ b/packages/ringcentral-integration/lib/proxy/handleProxyAction.ts @@ -15,9 +15,11 @@ export const pushStates = (target: RcModuleV2, action: any) => { if (action._usm === usmAction) { // restore changes states for reduction of serialized data from `patches` const _state = applyPatches( + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message target[action.type]._getLastState(), action._patches, ); + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message target[action.type]._handleState(_state); return { ...action, diff --git a/packages/ringcentral-integration/lib/rateLimitThrottle/rateLimitThrottle.ts b/packages/ringcentral-integration/lib/rateLimitThrottle/rateLimitThrottle.ts index 8892bb7e3d..09204db0f1 100644 --- a/packages/ringcentral-integration/lib/rateLimitThrottle/rateLimitThrottle.ts +++ b/packages/ringcentral-integration/lib/rateLimitThrottle/rateLimitThrottle.ts @@ -8,6 +8,7 @@ export const rateLimitThrottle = Promise>({ pool, poolWindow, }: RateLimitThrottleOptions & { fn: F }) => { + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Promise = null; let count = 0; @@ -15,6 +16,7 @@ export const rateLimitThrottle = Promise>({ if (!resetPromise) { resetPromise = new Promise((resolve) => { setTimeout(() => { + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Promise this[targetSymbol]()); } return proto.get(property)(); diff --git a/packages/ringcentral-integration/lib/sliceExecute.ts b/packages/ringcentral-integration/lib/sliceExecute.ts index efa9e12dd9..d262b6ecf5 100644 --- a/packages/ringcentral-integration/lib/sliceExecute.ts +++ b/packages/ringcentral-integration/lib/sliceExecute.ts @@ -1,4 +1,4 @@ -const sliceExecute = async ({ array, threshold, handler }) => { +const sliceExecute = async ({ array, threshold, handler }: any) => { let index = 0; const loop = async () => { const items = array.slice(index, index + threshold); diff --git a/packages/ringcentral-integration/lib/throttle.ts b/packages/ringcentral-integration/lib/throttle.ts index 8e8ebfd9f3..3e79f63673 100644 --- a/packages/ringcentral-integration/lib/throttle.ts +++ b/packages/ringcentral-integration/lib/throttle.ts @@ -15,6 +15,7 @@ export default function throttle( throw new Error('First argument of throttle function should be a function'); } + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Timeout'. let timer: NodeJS.Timeout = null; let firstTime = true; @@ -34,6 +35,7 @@ export default function throttle( timer = setTimeout(() => { clearTimeout(timer); + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Timeout'. timer = null; func.apply(context, args); }, threshold); diff --git a/packages/ringcentral-integration/lib/validateIsOffline.ts b/packages/ringcentral-integration/lib/validateIsOffline.ts index 0553ef6056..38467a565e 100644 --- a/packages/ringcentral-integration/lib/validateIsOffline.ts +++ b/packages/ringcentral-integration/lib/validateIsOffline.ts @@ -1,18 +1,21 @@ +const offlineMessagesList = [ + 'Failed to fetch', + 'Network Error', + 'Unable to access the network', + 'Your connection was interrupted', + 'The Internet connection appears to be offline.', + 'NetworkError when attempting to fetch resource.', + 'A server with the specified hostname could not be found.', // DNS matching failed + 'Network request failed', // IE + 'Type error', // Safari + 'The request timed out.', // Safari + 'Load failed', // iOS = Failed to fetch +]; + export default (message: string) => { if (!message) { return false; } - const offlineMessagesList = [ - 'Failed to fetch', - 'Network Error', - 'Unable to access the network', - 'Your connection was interrupted', - 'The Internet connection appears to be offline.', - 'NetworkError when attempting to fetch resource.', - 'A server with the specified hostname could not be found.', // DNS matching failed - 'Network request failed', // IE - 'Type error', // Safari - 'The request timed out.', // Safari - ]; + return !!offlineMessagesList.find((item) => message.indexOf(item) > -1); }; diff --git a/packages/ringcentral-integration/lib/validateNumbers.ts b/packages/ringcentral-integration/lib/validateNumbers.ts index 8dca294361..de95f766bb 100644 --- a/packages/ringcentral-integration/lib/validateNumbers.ts +++ b/packages/ringcentral-integration/lib/validateNumbers.ts @@ -31,10 +31,10 @@ function numberFormat({ allowRegionSettings: boolean; }) { const errors: { phoneNumber: string; type: string }[] = []; - phoneNumbers.map((phoneNumber) => { + phoneNumbers.forEach((phoneNumber) => { if (!isValidNumber({ input: phoneNumber, countryCode })) { errors.push({ phoneNumber, type: 'noToNumber' }); - return null; + return; } if ( allowRegionSettings && @@ -46,7 +46,6 @@ function numberFormat({ ) { errors.push({ phoneNumber, type: 'noAreaCode' }); } - return null; }); return { result: errors.length === 0, diff --git a/packages/ringcentral-integration/modules/AccountContacts/AccountContacts.interface.ts b/packages/ringcentral-integration/modules/AccountContacts/AccountContacts.interface.ts index 6e18524c53..b90e3dcab8 100644 --- a/packages/ringcentral-integration/modules/AccountContacts/AccountContacts.interface.ts +++ b/packages/ringcentral-integration/modules/AccountContacts/AccountContacts.interface.ts @@ -54,6 +54,7 @@ export type Presences = Record; export interface Contact extends IContact { presence?: Presence['presence']; contactStatus?: string; + isCallQueueNumber?: boolean; } export interface DirectoryContacts { diff --git a/packages/ringcentral-integration/modules/AccountContacts/AccountContacts.ts b/packages/ringcentral-integration/modules/AccountContacts/AccountContacts.ts index eb2b3f34c4..0a010252de 100644 --- a/packages/ringcentral-integration/modules/AccountContacts/AccountContacts.ts +++ b/packages/ringcentral-integration/modules/AccountContacts/AccountContacts.ts @@ -1,4 +1,3 @@ -import { filter, forEach, join, keys, map, reduce } from 'ramda'; import type PresenceInfoResponse from '@rc-ex/core/lib/definitions/PresenceInfoResponse'; import type ValidationError from '@rc-ex/core/lib/definitions/ValidationError'; import { @@ -7,6 +6,7 @@ import { RcModuleV2, state, } from '@ringcentral-integration/core'; +import { filter, forEach, join, keys, map, reduce } from 'ramda'; import { phoneSources } from '../../enums/phoneSources'; import { phoneTypes } from '../../enums/phoneTypes'; @@ -30,6 +30,7 @@ import { isSupportedPhoneNumber, } from '../../lib/phoneTypeHelper'; import { proxify } from '../../lib/proxy/proxify'; + import type { Contact, Deps, @@ -482,6 +483,7 @@ export class AccountContacts extends RcModuleV2 implements ContactSource { this.profileImages[id] && this.profileImages[id].imageUrl, presence: this.presences[id] && this.presences[id].presence, contactStatus: item.status, + isCallQueueNumber: item.type === 'Department', }; if (item.phoneNumbers && item.phoneNumbers.length > 0) { diff --git a/packages/ringcentral-integration/modules/AccountInfo/AccountInfo.ts b/packages/ringcentral-integration/modules/AccountInfo/AccountInfo.ts index b1bc9cc3fb..b9911afc96 100644 --- a/packages/ringcentral-integration/modules/AccountInfo/AccountInfo.ts +++ b/packages/ringcentral-integration/modules/AccountInfo/AccountInfo.ts @@ -1,12 +1,24 @@ +import type BrandInfo from '@rc-ex/core/lib/definitions/BrandInfo'; import type GetAccountInfoResponse from '@rc-ex/core/lib/definitions/GetAccountInfoResponse'; +import type ServiceInfo from '@rc-ex/core/lib/definitions/ServiceInfo'; import { computed, track } from '@ringcentral-integration/core'; import { permissionsMessages } from '../../enums/permissionsMessages'; import { Module } from '../../lib/di'; import { loginStatus } from '../Auth'; import { DataFetcherV2Consumer, DataSource } from '../DataFetcherV2'; + import type { Deps } from './AccountInfo.interface'; +type ServiceInfoWithUBrand = ServiceInfo & { uBrand?: BrandInfo }; + +export const subBrands = [ + '3000.Zayo', + '3000.NWNC', + '2000.Optus', + '3000.Brightspeed', +]; + @Module({ name: 'AccountInfo', deps: [ @@ -58,7 +70,7 @@ export class AccountInfo extends DataFetcherV2Consumer< } @track((that: AccountInfo) => (analytics) => { - // @ts-ignore + // @ts-expect-error TS(2339): Property 'identify' does not exist on type 'IAnaly... Remove this comment to see the full error message analytics.identify?.({ userId: that._deps.auth?.ownerId, accountId: that.id, @@ -69,6 +81,10 @@ export class AccountInfo extends DataFetcherV2Consumer< }) override onInitSuccess() {} + get isCRMEnabled() { + return this._deps.tierChecker?.isCRMEnabled; + } + @computed(({ data }: AccountInfo) => [data]) get info() { return this.data ?? {}; @@ -108,4 +124,15 @@ export class AccountInfo extends DataFetcherV2Consumer< get maxExtensionNumberLength() { return this.info.limits?.maxExtensionNumberLength ?? 6; } + + @computed(({ serviceInfo }: AccountInfo) => [serviceInfo]) + get userBrandId() { + const serviceInfo = this.serviceInfo as ServiceInfoWithUBrand; + const uBrandId = serviceInfo.uBrand?.id; + const brandId = + uBrandId && subBrands.includes(uBrandId) + ? uBrandId + : serviceInfo.brand?.id; + return brandId; + } } diff --git a/packages/ringcentral-integration/modules/ActiveCallControl/ActiveCallControl.interface.ts b/packages/ringcentral-integration/modules/ActiveCallControl/ActiveCallControl.interface.ts index 3117a22cb8..b882ed9249 100644 --- a/packages/ringcentral-integration/modules/ActiveCallControl/ActiveCallControl.interface.ts +++ b/packages/ringcentral-integration/modules/ActiveCallControl/ActiveCallControl.interface.ts @@ -1,15 +1,15 @@ +import type { IPickUpCallParams as OriginalPickUpCallParams } from 'ringcentral-call'; import type { Direction } from 'ringcentral-call-control/lib/Session'; import { Session } from 'ringcentral-call/lib/Session'; import { WebPhoneSession } from 'ringcentral-web-phone/lib/session'; -import type { IPickUpCallParams as OriginalPickUpCallParams } from 'ringcentral-call'; import type { RouterInteraction } from '../../../ringcentral-widgets/modules/RouterInteraction'; import type { TelephonyStatus } from '../../enums/telephonyStatus'; -import type { NormalizedSession } from '../../interfaces/Webphone.interface'; import type { IWarmTransferInfo, ActiveCallControlSessionData, } from '../../interfaces/ActiveSession.interface'; +import type { NormalizedSession } from '../../interfaces/Webphone.interface'; import type { RingCentralClient } from '../../lib/RingCentralClient'; import type { AccountInfo } from '../AccountInfo'; import type { Alert } from '../Alert'; diff --git a/packages/ringcentral-integration/modules/ActiveCallControl/ActiveCallControl.ts b/packages/ringcentral-integration/modules/ActiveCallControl/ActiveCallControl.ts index e0d5b8bdb8..5cd731ddc6 100644 --- a/packages/ringcentral-integration/modules/ActiveCallControl/ActiveCallControl.ts +++ b/packages/ringcentral-integration/modules/ActiveCallControl/ActiveCallControl.ts @@ -1,13 +1,3 @@ -import { filter, find, forEach, isEmpty } from 'ramda'; -import type { ActiveCallInfo, MakeCallParams } from 'ringcentral-call'; -import { events as callEvents, RingCentralCall } from 'ringcentral-call'; -import type { ReplyWithTextParams } from 'ringcentral-call-control/lib/Session'; -import { - PartyStatusCode, - ReplyWithPattern, -} from 'ringcentral-call-control/lib/Session'; -import { events as eventsEnum } from 'ringcentral-call/lib/Session'; -import { v4 as uuidV4 } from 'uuid'; import type ExtensionTelephonySessionsEvent from '@rc-ex/core/lib/definitions/ExtensionTelephonySessionsEvent'; import { action, @@ -19,6 +9,16 @@ import { watch, } from '@ringcentral-integration/core'; import { sleep } from '@ringcentral-integration/utils'; +import { filter, find, forEach, isEmpty } from 'ramda'; +import type { ActiveCallInfo, MakeCallParams } from 'ringcentral-call'; +import { events as callEvents, RingCentralCall } from 'ringcentral-call'; +import type { ReplyWithTextParams } from 'ringcentral-call-control/lib/Session'; +import { + PartyStatusCode, + ReplyWithPattern, +} from 'ringcentral-call-control/lib/Session'; +import { events as eventsEnum } from 'ringcentral-call/lib/Session'; +import { v4 as uuidV4 } from 'uuid'; import { callDirection } from '../../enums/callDirections'; // eslint-disable-next-line import/no-named-as-default @@ -36,7 +36,11 @@ import { callErrors } from '../Call/callErrors'; import type { MessageBase } from '../Subscription'; import { sessionStatus } from '../Webphone/sessionStatus'; import { webphoneErrors } from '../Webphone/webphoneErrors'; -import { normalizeSession as normalizeWebphoneSession } from '../Webphone/webphoneHelper'; +import { + extractHeadersData, + normalizeSession as normalizeWebphoneSession, +} from '../Webphone/webphoneHelper'; + import type { ActiveCallControlSessionData, ActiveSession, @@ -59,6 +63,7 @@ import { normalizeSession, normalizeTelephonySession, getWebphoneReplyMessageOption, + isGoneSession, } from './helpers'; const DEFAULT_TTL = 30 * 60 * 1000; @@ -336,7 +341,7 @@ export class ActiveCallControl extends RcModuleV2 { extensionInfo: { ...this._deps.extensionInfo.info, // TODO: add info type in 'AccountInfo' - // @ts-ignore + // @ts-expect-error TS(2322): Type 'GetAccountInfoResponse' is not assignable to... Remove this comment to see the full error message account: this._deps.accountInfo.info, }, }, @@ -353,7 +358,7 @@ export class ActiveCallControl extends RcModuleV2 { ); // TODO: workaround of bug: // WebRTC outbound call with wrong sequences of telephony sessions then call log section will not show - // @ts-ignore + // @ts-expect-error TS(2341): Property '_callControl' is private and only access... Remove this comment to see the full error message rcCall._callControl?.on('new', (session: Session) => this._onNewCall(session), ); @@ -595,9 +600,9 @@ export class ActiveCallControl extends RcModuleV2 { const callControlSessions = (this._rcCall?.sessions || []) .filter((session) => filterDisconnectedCalls(session)) .map((session) => { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'NormalizedSession | undefined' is not assign... Remove this comment to see the full error message currentDeviceCallsMap[session.telephonySessionId] = - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'WebPhoneSession' is not assignab... Remove this comment to see the full error message normalizeWebphoneSession(session.webphoneSession); return { @@ -616,9 +621,12 @@ export class ActiveCallControl extends RcModuleV2 { telephonySessionId: session.telephonySessionId, telephonySession: normalizeTelephonySession(session.telephonySession), to: session.to, - }; + } as ActiveCallControlSessionData; }); - this._updateActiveSessions(currentDeviceCallsMap, callControlSessions); + this._updateActiveSessions( + currentDeviceCallsMap, + callControlSessions.filter((x) => !isGoneSession(x)), + ); } @action @@ -679,18 +687,18 @@ export class ActiveCallControl extends RcModuleV2 { */ const normalizedWebphoneSession = normalizeWebphoneSession(session); if ( - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. !normalizedWebphoneSession.startTime && - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. !normalizedWebphoneSession.isToVoicemail && - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. !normalizedWebphoneSession.isForwarded && - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. !normalizedWebphoneSession.isReplied ) { return; } - // @ts-expect-error + // @ts-expect-error TS(2339): Property 'partyData' does not exist on type 'Norma... Remove this comment to see the full error message const { partyData } = normalizedWebphoneSession; if (!partyData) return; if (this.lastEndedSessionIds.indexOf(partyData.sessionId) === -1) { @@ -1165,7 +1173,7 @@ export class ActiveCallControl extends RcModuleV2 { if (!validatedResult.result) { validatedResult.errors.forEach(async (error) => { const isHAError = - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type '{ phoneNumber: string; type: "sp... Remove this comment to see the full error message !!(await this._deps.availabilityMonitor?.checkIfHAError(error)); if (!isHAError) { // TODO: fix `callErrors` type @@ -1284,7 +1292,7 @@ export class ActiveCallControl extends RcModuleV2 { } catch (e: any /** TODO: confirm with instanceof */) { console.error(e); this._deps.alert.warning({ - message: webphoneErrors.forwardError, + message: webphoneErrors.unknownError, }); return false; } @@ -1427,6 +1435,7 @@ export class ActiveCallControl extends RcModuleV2 { const { webphoneSession } = session; const deviceId = this._deps.webphone?.device?.id; if (webphoneSession) { + this._deps.webphone?.initWebphoneSessionEvents(webphoneSession); await session.answer({ deviceId }); } else { await this.pickUpCall({ @@ -1621,7 +1630,8 @@ export class ActiveCallControl extends RcModuleV2 { sdkMakeCallParams, )) as Session; this._activeSession = session; - session.webphoneSession.on('progress', () => { + this._deps.webphone?.initWebphoneSessionEvents(session.webphoneSession); + session.webphoneSession.on('progress', (incomingResponse) => { if ( session.telephonySessionId && this.activeSessionId !== session.telephonySessionId @@ -1817,7 +1827,7 @@ export class ActiveCallControl extends RcModuleV2 { @track((that: ActiveCallControl, path: string) => { return (analytics) => { - // @ts-expect-error + // @ts-expect-error TS(2339): Property 'getTrackTarget' does not exist on type '... Remove this comment to see the full error message const target = analytics.getTrackTarget(); return [ trackEvents.openEntityDetailLink, diff --git a/packages/ringcentral-integration/modules/ActiveCallControl/callControlAlerts.ts b/packages/ringcentral-integration/modules/ActiveCallControl/callControlAlerts.ts new file mode 100644 index 0000000000..8aa0c03cc8 --- /dev/null +++ b/packages/ringcentral-integration/modules/ActiveCallControl/callControlAlerts.ts @@ -0,0 +1,6 @@ +import { ObjectMap } from '@ringcentral-integration/core/lib/ObjectMap'; + +export const callControlAlerts = ObjectMap.prefixKeys( + ['callsMerged', 'somethingWentWrong', 'tooManyParticipants'], + 'callControlAlerts', +); diff --git a/packages/ringcentral-integration/modules/ActiveCallControl/helpers.ts b/packages/ringcentral-integration/modules/ActiveCallControl/helpers.ts index cee41ad387..05b5bc691c 100644 --- a/packages/ringcentral-integration/modules/ActiveCallControl/helpers.ts +++ b/packages/ringcentral-integration/modules/ActiveCallControl/helpers.ts @@ -1,4 +1,5 @@ // eslint-disable-next-line import/no-named-as-default +import type CallRecording from '@rc-ex/core/lib/definitions/CallRecording'; import { find } from 'ramda'; import type { Party, @@ -10,15 +11,15 @@ import { ReplyWithPattern, } from 'ringcentral-call-control/lib/Session'; import type { Session } from 'ringcentral-call/lib/Session'; -import type CallRecording from '@rc-ex/core/lib/definitions/CallRecording'; // eslint-disable-next-line import/no-named-as-default import activeCallControlStatus from '../../enums/activeCallControlStatus'; -import callDirections, { callDirection } from '../../enums/callDirections'; +import { callDirection } from '../../enums/callDirections'; // eslint-disable-next-line import/no-named-as-default import callResults from '../../enums/callResults'; -import { recordStatus } from '../Webphone/recordStatus'; import { telephonyStatus } from '../../enums/telephonyStatus'; +import { recordStatus } from '../Webphone/recordStatus'; + import type { ActiveCallControlSessionData } from './ActiveCallControl.interface'; // telephony session status match presence telephonyStatus @@ -50,7 +51,7 @@ export function isRejectCode({ code: string; }) { return ( - direction === callDirections.inbound && + direction === callDirection.inbound && (code === activeCallControlStatus.setUp || code === activeCallControlStatus.proceeding) ); @@ -85,7 +86,7 @@ export function normalizeSession({ toUserName: to?.name, id: session.id, sessionId, - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'PartyStatusCode | undefined' is ... Remove this comment to see the full error message callStatus: mapTelephonyStatus(status?.code), startTime: new Date(creationTime).getTime(), creationTime, @@ -98,12 +99,12 @@ export function normalizeSession({ isToVoicemail: false, lastHoldingTime: 0, minimized: false, - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'Recording[] | undefined' is not ... Remove this comment to see the full error message recordStatus: isOnRecording(recordings) ? recordStatus.recording : recordStatus.idle, removed: false, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'import("/Users/declan.zou/Projects/rc/integr... Remove this comment to see the full error message isReject: isRejectCode({ direction, code: status?.code }), }; return formatValue; @@ -131,7 +132,7 @@ export function isRinging( telephonySession && (telephonySession.status === PartyStatusCode.proceeding || telephonySession.status === PartyStatusCode.setup) && - telephonySession.direction === callDirections.inbound + telephonySession.direction === callDirection.inbound ); } @@ -141,7 +142,7 @@ export function isHolding(telephonySession: ActiveCallControlSessionData) { export function isRecording(session: ActiveCallControlSessionData) { const { party } = session; - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'Recording[] | undefined' is not ... Remove this comment to see the full error message return isOnRecording(party.recordings); } @@ -162,7 +163,7 @@ export function isAtMainNumberPromptToneStage(session: Session) { if (!session) return false; const { otherParties = [], direction, status } = session; if ( - direction === callDirections.outbound && + direction === callDirection.outbound && status === PartyStatusCode.answered && !otherParties.length ) { @@ -175,9 +176,9 @@ export function getInboundSwitchedParty(parties: Party[]) { if (!parties.length) return false; const result = find((party: Party) => { return ( - party.direction === callDirections.inbound && + party.direction === callDirection.inbound && party.status?.code === PartyStatusCode.disconnected && - // @ts-expect-error + // @ts-expect-error TS(2339): Property 'reason' does not exist on type 'PartySta... Remove this comment to see the full error message party.status?.reason === 'CallSwitch' ); }, parties); @@ -204,17 +205,21 @@ export function filterDisconnectedCalls( return session.status !== PartyStatusCode.disconnected; } +export function isGoneSession(session: Session | ActiveCallControlSessionData) { + return session.status === PartyStatusCode.gone; +} + export function normalizeTelephonySession( session?: TelephonySession, ): ActiveCallControlSessionData { if (!session) { - // @ts-expect-error + // @ts-expect-error TS(2740): Type '{}' is missing the following properties from... Remove this comment to see the full error message return {}; } return { accountId: session.accountId, creationTime: session.creationTime, - // @ts-expect-error + // @ts-expect-error TS(2322): Type '{ accountId: any; creationTime: any; data: a... Remove this comment to see the full error message data: session.data, extensionId: session.extensionId, id: session.id, diff --git a/packages/ringcentral-integration/modules/ActiveCallControl/index.ts b/packages/ringcentral-integration/modules/ActiveCallControl/index.ts index 8595e46832..f35d1991bc 100644 --- a/packages/ringcentral-integration/modules/ActiveCallControl/index.ts +++ b/packages/ringcentral-integration/modules/ActiveCallControl/index.ts @@ -1,4 +1,5 @@ export * from './ActiveCallControl'; export * from './helpers'; export * from './ActiveCallControl.interface'; +export * from './callControlAlerts'; export * from './callControlError'; diff --git a/packages/ringcentral-integration/modules/ActiveCalls/ActiveCalls.ts b/packages/ringcentral-integration/modules/ActiveCalls/ActiveCalls.ts index 17274bf765..62defeb4c5 100644 --- a/packages/ringcentral-integration/modules/ActiveCalls/ActiveCalls.ts +++ b/packages/ringcentral-integration/modules/ActiveCalls/ActiveCalls.ts @@ -1,8 +1,8 @@ -import { map, sort } from 'ramda'; -import type { Unsubscribe } from 'redux'; -import type DetailedExtensionPresenceEvent from '@rc-ex/core/lib/definitions/DetailedExtensionPresenceEvent'; import type CallLogRecord from '@rc-ex/core/lib/definitions/CallLogRecord'; +import type DetailedExtensionPresenceEvent from '@rc-ex/core/lib/definitions/DetailedExtensionPresenceEvent'; import { computed, watch } from '@ringcentral-integration/core'; +import { map, sort } from 'ramda'; +import type { Unsubscribe } from 'redux'; import { subscriptionFilters } from '../../enums/subscriptionFilters'; import { normalizeStartTime, sortByStartTime } from '../../lib/callLogHelpers'; @@ -11,6 +11,7 @@ import { debounce } from '../../lib/debounce-throttle'; import { Module } from '../../lib/di'; import fetchList from '../../lib/fetchList'; import { DataFetcherV2Consumer, DataSource } from '../DataFetcherV2'; + import type { Deps } from './ActiveCalls.interface'; const presenceRegExp = /\/presence\?detailedTelephonyState=true/; diff --git a/packages/ringcentral-integration/modules/ActivityMatcher/ActivityMatcher.ts b/packages/ringcentral-integration/modules/ActivityMatcher/ActivityMatcher.ts index 190edff92d..5e286ceed3 100644 --- a/packages/ringcentral-integration/modules/ActivityMatcher/ActivityMatcher.ts +++ b/packages/ringcentral-integration/modules/ActivityMatcher/ActivityMatcher.ts @@ -1,6 +1,7 @@ import { DataMatcher } from '../../lib/DataMatcherV2'; import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; + import type { Deps } from './ActivityMatcher.interface'; @Module({ diff --git a/packages/ringcentral-integration/modules/AddressBook/AddressBook.ts b/packages/ringcentral-integration/modules/AddressBook/AddressBook.ts index 3bbb9fc730..a9f8ea08d4 100644 --- a/packages/ringcentral-integration/modules/AddressBook/AddressBook.ts +++ b/packages/ringcentral-integration/modules/AddressBook/AddressBook.ts @@ -1,4 +1,3 @@ -import { forEach, map } from 'ramda'; import { action, computed, @@ -7,6 +6,8 @@ import { } from '@ringcentral-integration/core'; import { sleep } from '@ringcentral-integration/utils'; import type { ApiError } from '@ringcentral/sdk'; +import { forEach, map } from 'ramda'; + import { availabilityTypes } from '../../enums/availabilityTypes'; import { phoneSources } from '../../enums/phoneSources'; import type { @@ -22,6 +23,7 @@ import { import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; import { DataFetcherV2Consumer, DataSource } from '../DataFetcherV2'; + import type { AddressBookData, Deps, @@ -32,6 +34,12 @@ import { getSyncParams, processAddressBookResponse } from './helpers'; export const DEFAULT_FETCH_INTERVAL = 1000; export const DEFAULT_CONTACTS_PER_PAGE = 250; +// reference: https://developers.ringcentral.com/api-reference/External-Contacts/syncAddressBook +const INVALID_TOKEN_ERROR_CODES = [ + // 400 CMN-101 Parameter [${parameterName}] value is invalid. + 'CMN-101', +]; + @Module({ name: 'AddressBook', deps: [ @@ -109,7 +117,7 @@ export class AddressBook return this.data?.syncToken; } - protected async _fetch(perPage: number, syncToken: string, pageId?: number) { + protected async _fetch(perPage: number, syncToken?: string, pageId?: number) { const params = getSyncParams({ perPage, syncToken, @@ -127,18 +135,18 @@ export class AddressBook protected _processISyncData(records: PersonalContactResource[]) { if (records?.length > 0) { const updatedRecords: PersonalContactResource[] = []; - // @ts-expect-error + // @ts-expect-error TS(2344): Type 'number | undefined' does not satisfy the con... Remove this comment to see the full error message const processedIDMap: Record = {}; forEach((record) => { if (record.availability === availabilityTypes.alive) { // Only keep entries that is 'alive', omit 'purged' and 'deleted' updatedRecords.push(record); } - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. processedIDMap[record.id] = true; }, records); forEach((record) => { - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. if (!processedIDMap[record.id]) { // record has no updates updatedRecords.push(record); @@ -149,32 +157,51 @@ export class AddressBook return this.data.records; } + protected async _fetchAll(syncToken?: string) { + const perPage = this._perPage; + let records: PersonalContactResource[] = []; + let response = await this._fetch(perPage, syncToken); + records = records.concat(response.records ?? []); + while (response.nextPageId) { + await sleep(this._fetchInterval); + response = await this._fetch(perPage, syncToken, response.nextPageId); + records = records.concat(response.records ?? []); + } + if (response.syncInfo!.syncType === 'ISync') { + // @ts-expect-error TS(2322): Type 'PersonalContactResource[] | undefined' is no... Remove this comment to see the full error message + records = this._processISyncData(records); + } + return { + syncToken: response.syncInfo!.syncToken, + records, + }; + } + protected async _sync(): Promise { try { - const syncToken = this.syncToken; - const perPage = this._perPage; - let records: PersonalContactResource[] = []; - // @ts-expect-error - let response = await this._fetch(perPage, syncToken); - records = records.concat(response.records ?? []); - while (response.nextPageId) { - await sleep(this._fetchInterval); - // @ts-expect-error - response = await this._fetch(perPage, syncToken, response.nextPageId); - records = records.concat(response.records ?? []); - } - if (response.syncInfo!.syncType === 'ISync') { - // @ts-expect-error - records = this._processISyncData(records); - } - return { - syncToken: response.syncInfo!.syncToken, - records, - }; - } catch (error: any /** TODO: confirm with instanceof */) { - if ((error as ApiError)?.response?.status === 403) { + const data = await this._fetchAll(this.syncToken); + return data; + } catch (e: unknown) { + const error = e as ApiError; + + // 403 Forbidden + if (error.response?.status === 403) { return {} as AddressBookData; } + + // try Full Sync + const responseResult = await error.response?.clone().json(); + if ( + responseResult?.errors?.some(({ errorCode = '' } = {}) => + INVALID_TOKEN_ERROR_CODES.includes(errorCode), + ) + ) { + const data = await this._fetchAll(); + return data; + } + + // exception + console.error('[AddressBook] > _sync', error.response?.status, error); throw error; } } diff --git a/packages/ringcentral-integration/modules/AddressBook/helpers.ts b/packages/ringcentral-integration/modules/AddressBook/helpers.ts index efe5fb995b..63b10960ff 100644 --- a/packages/ringcentral-integration/modules/AddressBook/helpers.ts +++ b/packages/ringcentral-integration/modules/AddressBook/helpers.ts @@ -1,7 +1,8 @@ -import { forEach } from 'ramda'; import type AddressBookSync from '@rc-ex/core/lib/definitions/AddressBookSync'; +import { forEach } from 'ramda'; import { syncTypes } from '../../enums/syncTypes'; + import type { SyncParameters } from './AddressBook.interface'; const REGX_DECODE = /&\w+;/g; diff --git a/packages/ringcentral-integration/modules/Alert/Alert.interface.ts b/packages/ringcentral-integration/modules/Alert/Alert.interface.ts index 35dc03c9e4..50368846a5 100644 --- a/packages/ringcentral-integration/modules/Alert/Alert.interface.ts +++ b/packages/ringcentral-integration/modules/Alert/Alert.interface.ts @@ -1,6 +1,7 @@ import type { DOMAttributes } from 'react'; import type { GlobalStorage } from '../GlobalStorage'; + import type { AlertLevelType } from './alertLevels'; export interface Deps { diff --git a/packages/ringcentral-integration/modules/Alert/Alert.ts b/packages/ringcentral-integration/modules/Alert/Alert.ts index 139853543b..82dc6fcadc 100644 --- a/packages/ringcentral-integration/modules/Alert/Alert.ts +++ b/packages/ringcentral-integration/modules/Alert/Alert.ts @@ -1,14 +1,14 @@ -import * as uuid from 'uuid'; - import { action, globalStorage, RcModuleV2, state, } from '@ringcentral-integration/core'; +import * as uuid from 'uuid'; import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; + import type { AlertItem, AllowDuplicates, diff --git a/packages/ringcentral-integration/modules/Analytics/Analytics.interface.ts b/packages/ringcentral-integration/modules/Analytics/Analytics.interface.ts new file mode 100644 index 0000000000..313d140b6e --- /dev/null +++ b/packages/ringcentral-integration/modules/Analytics/Analytics.interface.ts @@ -0,0 +1,67 @@ +export interface TrackProps { + appName: string; + appVersion: string; + brand: string; + 'App Language': string; + 'Browser Language': string; + 'Extension Type': string; + 'App Init Time': number; + id?: string; +} + +export interface TrackTarget { + eventPostfix: string; + router: string; +} + +export interface TrackPayload { + pathname: string; +} + +export interface TrackAction { + type: string; + payload?: TrackPayload; + curIdx?: number; + playing?: boolean; + fromType?: string; + callSettingMode?: string; + phoneNumber?: string; + recipient?: any; +} + +export interface TrackLog { + timeStamp: string; + event: string; + trackProps: TrackProps; +} + +export interface TrackImpl { + (action: TrackAction): void; +} + +export interface TrackItem { + tagName: string; + funcName: string; + funcImpl: TrackImpl; +} + +export interface PendoAgent { + visitor: { + id: string; + appName: string; + appVersion: string; + appBrand: string; + plaBrand: string; + countryCode: string; + [key: string]: string; + }; + account: { + id: string; + [key: string]: string; + }; +} + +export interface IdentifyOptions { + userId: string; + [K: string]: any; +} diff --git a/packages/ringcentral-integration/modules/Analytics/Analytics.ts b/packages/ringcentral-integration/modules/Analytics/Analytics.ts index c1e5a97610..443dcb1fe2 100644 --- a/packages/ringcentral-integration/modules/Analytics/Analytics.ts +++ b/packages/ringcentral-integration/modules/Analytics/Analytics.ts @@ -1,88 +1,36 @@ -import { sleep } from '@ringcentral-integration/utils'; +import { computed } from '@ringcentral-integration/core'; +import { sleep, getOsInfo } from '@ringcentral-integration/utils'; +import mixpanel from 'mixpanel-browser'; -import type RouterInteraction from '../../../ringcentral-widgets/modules/RouterInteraction'; +import type { RouterInteraction } from '../../../ringcentral-widgets/modules/RouterInteraction'; import moduleStatuses from '../../enums/moduleStatuses'; import { Pendo, Segment } from '../../lib/Analytics'; -import { Module } from '../../lib/di'; import RcModule from '../../lib/RcModule'; +import { Module } from '../../lib/di'; import saveBlob from '../../lib/saveBlob'; import { callingModes } from '../CallingSettings/callingModes'; +import { Environment } from '../Environment'; import type { ExtensionInfo } from '../ExtensionInfo'; + +import { + TrackProps, + TrackTarget, + TrackAction, + TrackLog, + TrackImpl, + TrackItem, + PendoAgent, + IdentifyOptions, +} from './Analytics.interface'; import type { AnalyticsActionTypes } from './actionTypes'; import { analyticsActionTypes } from './actionTypes'; import getAnalyticsReducer from './getAnalyticsReducer'; -export interface TrackProps { - appName: string; - appVersion: string; - brand: string; - 'App Language': string; - 'Browser Language': string; - 'Extension Type': string; -} - -export interface TrackTarget { - eventPostfix: string; - router: string; -} - -export interface TrackPayload { - pathname: string; -} - -export interface TrackAction { - type: string; - payload?: TrackPayload; - curIdx?: number; - playing?: boolean; - fromType?: string; - callSettingMode?: string; - phoneNumber?: string; - recipient?: any; -} - -export interface TrackLog { - timeStamp: string; - event: string; - trackProps: TrackProps; -} - -export interface TrackImpl { - (action: TrackAction): void; -} - -export interface TrackItem { - tagName: string; - funcName: string; - funcImpl: TrackImpl; -} - -export interface PendoAgent { - visitor: { - id: string; - appName: string; - appVersion: string; - appBrand: string; - plaBrand: string; - countryCode: string; - [key: string]: string; - }; - account: { - id: string; - [key: string]: string; - }; -} - -interface IdentifyOptions { - userId: string; - [K: string]: any; -} - function warn() { console.warn('Do NOT call this directly.'); } -const TRACK_LIST: TrackItem[] = []; +export const TRACK_LIST: TrackItem[] = []; export function track(tagName: string) { return function _track( @@ -109,7 +57,6 @@ export function track(tagName: string) { export const DEFAULT_TAG_NAME = 'default'; export const tracking = track(DEFAULT_TAG_NAME); -// TODO: refactoring the module against `https://docs.google.com/spreadsheets/d/1xufV6-C-RJR6OJgwFYHYzNQwhIdN4BXXCo8ABs7RT-8/edit#gid=1480480736` /** * @class * @description Analytics module. @@ -123,6 +70,7 @@ export const tracking = track(DEFAULT_TAG_NAME); { dep: 'Call', optional: true }, { dep: 'CallingSettings', optional: true }, { dep: 'AccountInfo', optional: true }, + { dep: 'Environment', optional: true }, { dep: 'ExtensionInfo', optional: true }, { dep: 'CallHistory', optional: true }, { dep: 'CallMonitor', optional: true }, @@ -141,12 +89,15 @@ export const tracking = track(DEFAULT_TAG_NAME); { dep: 'ActiveCallControl', optional: true }, { dep: 'DialerUI', optional: true }, { dep: 'TierChecker', optional: true }, + { dep: 'Brand', optional: true }, + { dep: 'ExtensionFeatures', optional: true }, ], }) export class Analytics extends RcModule< Record, AnalyticsActionTypes > { + appInitTime = Date.now(); // TODO: add state interface // AnalyticsOptions private _analyticsKey: string; @@ -161,6 +112,7 @@ export class Analytics extends RcModule< protected _callingSettings: any; protected _accountInfo: any; protected _extensionInfo: ExtensionInfo; + protected _environment: Environment; protected _callHistory: any; protected _callMonitor: any; protected _conference: any; @@ -175,6 +127,8 @@ export class Analytics extends RcModule< protected _meeting: any; protected _rcVideo: any; protected _tierChecker: any; + protected _brand: any; + protected _extensionFeatures: any; private _dialerUI: any; private _segment: any; @@ -184,15 +138,20 @@ export class Analytics extends RcModule< private _logs: TrackLog[] = []; private _lingerThreshold: number; - private _lingerTimeout?: NodeJS.Timeout = null; + private _lingerTimeout?: NodeJS.Timeout; private _promise?: Promise; private _callLogSection: any; private _activeCallControl: any; private _enablePendo: boolean; + private _enableMixpanel: boolean; private _waitPendoCount: number; - private _pendoTimeout: ReturnType; + private _pendoTimeout?: ReturnType; private _env: string; private _useLocalPendoJS: any; + private _OSInfo: { + OS: string; + Device: string; + }; constructor({ analyticsOptions, @@ -220,15 +179,19 @@ export class Analytics extends RcModule< webphone, locale, meeting, + environment, rcVideo, dialerUI, tierChecker, - // settinigs + brand, + extensionFeatures, + // settings useLog = false, lingerThreshold = 1000, callLogSection, activeCallControl, enablePendo = false, + enableMixpanel = false, env = 'dev', ...options }: Record) { @@ -238,6 +201,8 @@ export class Analytics extends RcModule< actionTypes: analyticsActionTypes, }); + this._OSInfo = getOsInfo(); + // config this._analyticsKey = analyticsKey; this._pendoApiKey = pendoApiKey; @@ -252,6 +217,7 @@ export class Analytics extends RcModule< this._callingSettings = callingSettings; this._accountInfo = accountInfo; this._extensionInfo = extensionInfo; + this._environment = environment; this._callHistory = callHistory; this._callMonitor = callMonitor; this._conference = conference; @@ -269,18 +235,29 @@ export class Analytics extends RcModule< this._activeCallControl = activeCallControl; this._dialerUI = dialerUI; this._tierChecker = tierChecker; + this._brand = brand; + this._extensionFeatures = extensionFeatures; // init this._reducer = getAnalyticsReducer(this.actionTypes); - this._segment = Segment(); this._trackList = [...TRACK_LIST]; this._useLog = useLog; this._lingerThreshold = lingerThreshold; this._enablePendo = enablePendo; this._pendo = null; this._waitPendoCount = 0; - this._pendoTimeout = null; this._env = env; + this._analyticsKey = analyticsKey; this._useLocalPendoJS = analyticsOptions?.useLocalPendoJS ?? false; + this._enableMixpanel = !!(enableMixpanel && analyticsKey); + this._segment = this._enableMixpanel ? null : Segment(); + if (this.enableMixpanel) { + mixpanel.init(this._analyticsKey); + // According to EU policy, we had to disable mixpanel to upload IP addresses + mixpanel.set_config({ ip: false }); + // ready + this.onAnalyticsReady(); + } + if (this._enablePendo && this._pendoApiKey) { Pendo.init( this._pendoApiKey, @@ -292,24 +269,35 @@ export class Analytics extends RcModule< } } + /** Hook to be override by subclass */ + protected onAnalyticsReady() {} + identify(options: IdentifyOptions) { this._identify(options); } protected _identify({ userId, ...props }: IdentifyOptions) { - if (this.analytics) { + if (this.enableMixpanel) { + // @ts-expect-error TS(2345): Argument of type '{ env: string; userId: string; }... Remove this comment to see the full error message + this._mixpanelInitialize({ userId, ...props, env: this._env }); + } else if (this.analytics) { this.analytics.ready(() => { // According to EU policy, we had to disable mixpanel to upload IP addresses + // @ts-expect-error TS(2339): Property 'mixpanel' does not exist on type 'Window... Remove this comment to see the full error message if (typeof window.mixpanel !== 'undefined') { + // @ts-expect-error TS(2339): Property 'mixpanel' does not exist on type 'Window... Remove this comment to see the full error message window.mixpanel.set_config({ + // @ts-expect-error TS(2339): Property 'mixpanel' does not exist on type 'Window... Remove this comment to see the full error message ...window.mixpanel.config, ip: false, }); } else { console.error( - 'Mixpanel is not defined, and failure to disable IP address upload', + 'mixpanel is not defined, and failure to disable IP address upload', ); } + // ready + this.onAnalyticsReady(); }); this.analytics.identify(userId, props, { integrations: { @@ -323,6 +311,14 @@ export class Analytics extends RcModule< } } + protected _mixpanelInitialize({ userId }: { userId: string }) { + if (!userId || mixpanel.get_distinct_id?.() === userId) { + return; + } + console.log('mixpanel identify'); + mixpanel.identify(userId); + } + protected _pendoInitialize({ userId, ...props @@ -332,6 +328,7 @@ export class Analytics extends RcModule< } if (this._pendoTimeout) { clearTimeout(this._pendoTimeout); + this._pendoTimeout = undefined; } if (this._waitPendoCount > 3) { return; @@ -367,7 +364,7 @@ export class Analytics extends RcModule< } track(event: string, properties: any = {}) { - if (!this.analytics) { + if (!this.analytics && !this.enableMixpanel) { return; } @@ -376,12 +373,23 @@ export class Analytics extends RcModule< ...properties, }; - this.analytics.track(event, trackProps, { - integrations: { - All: true, - Mixpanel: true, - }, - }); + if (this.enableMixpanel) { + // NOTE: Data tracking has been migrated from Segment to Mixpanel. + // Add id to identify in Mixpanel, so the usage data can be filtered same as before. + if (this._auth?.ownerId) { + trackProps.id = this._auth.ownerId; + } + mixpanel.track(event, trackProps); + } + + if (this.analytics) { + this.analytics.track(event, trackProps, { + integrations: { + All: true, + Mixpanel: true, + }, + }); + } if (this._useLog) { this._logs.push({ @@ -438,7 +446,7 @@ export class Analytics extends RcModule< this.track('Meeting: Click Schedule/Meeting schedule page', trackProps); } - async _onStateChange() { + override async _onStateChange() { if (this.pending) { this.store.dispatch({ type: this.actionTypes.init, @@ -469,6 +477,7 @@ export class Analytics extends RcModule< this.store.dispatch({ type: this.actionTypes.clear, }); + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Promise { - this._lingerTimeout = null; + this._lingerTimeout = undefined; if (target && this._routerInteraction.currentPath === path) { this.trackLinger(target); } @@ -659,16 +668,6 @@ export class Analytics extends RcModule< } } - @tracking - private _coldTransfer(action: TrackAction) { - if ( - this._webphone?.isOnTransfer === true && - this._webphone?.actionTypes.updateSessions === action.type - ) { - this.track('Cold Transfer Call'); - } - } - @tracking private _textClickToDial(action: TrackAction) { if ( @@ -945,10 +944,12 @@ export class Analytics extends RcModule< getTrackTarget(path = this._routerInteraction?.currentPath): TrackTarget { if (!path) { + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'TrackTarget... Remove this comment to see the full error message return null; } const routes = path.split('/'); + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'string'. let formatRoute: string = null; const needMatchSecondRoutes = ['calls']; if (routes.length >= 3 && needMatchSecondRoutes.indexOf(routes[1]) !== -1) { @@ -1017,6 +1018,7 @@ export class Analytics extends RcModule< ]; const target = targets.find((target) => formatRoute === target.router); + // @ts-expect-error TS(2322): Type 'TrackTarget | undefined' is not assignable t... Remove this comment to see the full error message return target; } @@ -1172,6 +1174,7 @@ export class Analytics extends RcModule< private _dialerPlaceRingOutCall(action: TrackAction) { if ( this._dialerUI?.actionTypes.call === action.type && + // @ts-expect-error TS(2532): Object is possibly 'undefined'. (action.phoneNumber?.length > 0 || action.recipient) && this._callingSettings.callingMode !== callingModes.webphone ) { @@ -1181,10 +1184,18 @@ export class Analytics extends RcModule< } } + toggleDebug() { + this.mixpanel.set_config({ debug: !this.mixpanel.get_config('debug') }); + } + get trackList(): TrackItem[] { return this._trackList; } + get mixpanel() { + return mixpanel; + } + get analytics() { return (global as any).analytics; } @@ -1193,26 +1204,79 @@ export class Analytics extends RcModule< return this.state.lastActions; } + // @ts-expect-error TS(2416): Property 'status' in type 'Analytics' is not assig... Remove this comment to see the full error message get status(): string { return this.state.status; } + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message get ready(): boolean { return this.status === moduleStatuses.ready; } + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message get pending(): boolean { return this.status === moduleStatuses.pending; } + @computed((that: Analytics) => [ + that._brand.brandConfig, + that._accountInfo?.id, + that._extensionInfo?.country, + that._extensionFeatures?.features, + ]) + private get trackedUserInfo(): TrackProps { + const userInfo: Record = { + BrandId: this._brand.brandConfig.id, + AccountID: this._accountInfo?.id, + BrandName: this._brand.brandConfig.name, + CRMEnabled: this._accountInfo?.isCRMEnabled, + servicePlanId: this._accountInfo?.servicePlan.id, + edition: this._accountInfo?.servicePlan.edition, + }; + + const features = this._extensionFeatures?.features; + const isCallingEnabled = + features?.RingOut?.available || features?.WebPhone?.available; + const hasSmsPermission = + features?.PagesReceiving?.available || features?.SMSReceiving?.available; + const hasFaxPermission = features?.FaxReceiving?.available; + const hasGlipPermission = features?.Glip?.available; + + const properties = [ + { name: 'PhoneService', value: isCallingEnabled }, + { name: 'SMSService', value: hasSmsPermission }, + { name: 'FaxService', value: hasFaxPermission }, + { name: 'MessageService', value: hasGlipPermission }, + ]; + + properties.forEach(({ name, value }) => { + if (value !== undefined) { + userInfo[name] = value ? 'ON' : 'OFF'; + } + }); + + return userInfo as TrackProps; + } + get trackProps(): TrackProps { return { + ...this.trackedUserInfo, + ...this._OSInfo, appName: this._appName, appVersion: this._appVersion, brand: this._brandCode, 'App Language': this._locale?.currentLocale || '', 'Browser Language': this._locale?.browserLocale || '', 'Extension Type': this._extensionInfo?.info.type || '', + 'App Init Time': this.appInitTime, }; } + + get enableMixpanel() { + return !!( + this._enableMixpanel && + (!this._environment || this._environment.allowDataTracking) + ); + } } diff --git a/packages/ringcentral-integration/modules/Analytics/getAnalyticsReducer.ts b/packages/ringcentral-integration/modules/Analytics/getAnalyticsReducer.ts index 6b68f019e2..5772ce87c7 100644 --- a/packages/ringcentral-integration/modules/Analytics/getAnalyticsReducer.ts +++ b/packages/ringcentral-integration/modules/Analytics/getAnalyticsReducer.ts @@ -2,8 +2,8 @@ import { combineReducers } from 'redux'; import getModuleStatusReducer from '../../lib/getModuleStatusReducer'; -export function getLastActions(types) { - return (state = [], action) => { +export function getLastActions(types: any) { + return (state = [], action: any) => { if (action.type === types.clear) { return []; } @@ -14,7 +14,7 @@ export function getLastActions(types) { }; } -export default function getAnalyticsReducer(types) { +export default function getAnalyticsReducer(types: any) { return combineReducers({ status: getModuleStatusReducer(types), lastActions: getLastActions(types), diff --git a/packages/ringcentral-integration/modules/Analytics/index.ts b/packages/ringcentral-integration/modules/Analytics/index.ts index a136c36b9e..6ab50837fa 100644 --- a/packages/ringcentral-integration/modules/Analytics/index.ts +++ b/packages/ringcentral-integration/modules/Analytics/index.ts @@ -1,2 +1,3 @@ export * from './Analytics'; +export * from './Analytics.interface'; export * from './trackEvents'; diff --git a/packages/ringcentral-integration/modules/AnalyticsV2/Analytics.interface.ts b/packages/ringcentral-integration/modules/AnalyticsV2/Analytics.interface.ts index 83f4bc28e7..4c1ff95e15 100644 --- a/packages/ringcentral-integration/modules/AnalyticsV2/Analytics.interface.ts +++ b/packages/ringcentral-integration/modules/AnalyticsV2/Analytics.interface.ts @@ -1,5 +1,7 @@ import type { AccountInfo } from '../AccountInfo'; import type { Brand } from '../Brand'; +import { Environment } from '../Environment'; +import type { ExtensionFeatures } from '../ExtensionFeatures'; import type { ExtensionInfo } from '../ExtensionInfo'; import type { Locale } from '../Locale'; @@ -15,6 +17,8 @@ interface Auth { export interface Deps { auth: Auth; brand: Brand; + environment: Environment; + extensionFeatures: ExtensionFeatures; analyticsOptions: AnalyticsOptions; accountInfo?: AccountInfo; extensionInfo?: ExtensionInfo; @@ -59,6 +63,8 @@ export interface AnalyticsOptions { /** Self-hosting Analytics js for applications with strict CSP (e.g. chrome extension mv3) */ useLocalAnalyticsJS?: boolean; + /** migrate to mixpanel */ + enableMixpanel?: boolean; } export interface TrackProps { @@ -68,6 +74,8 @@ export interface TrackProps { 'App Language': string; 'Browser Language': string; 'Extension Type': string; + 'App Init Time': number; + id?: string; } export type TrackRouter = diff --git a/packages/ringcentral-integration/modules/AnalyticsV2/Analytics.ts b/packages/ringcentral-integration/modules/AnalyticsV2/Analytics.ts index 42edb12a7a..1581eb0682 100644 --- a/packages/ringcentral-integration/modules/AnalyticsV2/Analytics.ts +++ b/packages/ringcentral-integration/modules/AnalyticsV2/Analytics.ts @@ -1,10 +1,13 @@ -import { RcModuleV2, watch } from '@ringcentral-integration/core'; +import { computed, RcModuleV2, watch } from '@ringcentral-integration/core'; import type { IAnalytics } from '@ringcentral-integration/core/lib/track'; +import { getOsInfo } from '@ringcentral-integration/utils'; +import mixpanel from 'mixpanel-browser'; import { Pendo, Segment } from '../../lib/Analytics'; import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; import saveBlob from '../../lib/saveBlob'; + import type { Deps, IdentifyOptions, @@ -16,14 +19,15 @@ import type { } from './Analytics.interface'; import { trackRouters } from './analyticsRouters'; -// TODO: refactoring the module against `https://docs.google.com/spreadsheets/d/1xufV6-C-RJR6OJgwFYHYzNQwhIdN4BXXCo8ABs7RT-8/edit#gid=1480480736` // TODO: if use `dialerUI`/`callLogSection`/`adapter`, make sure they should all be RcModuleV2 @Module({ name: 'Analytics', deps: [ 'Auth', 'Brand', + 'ExtensionFeatures', 'AnalyticsOptions', + { dep: 'Environment', optional: true }, { dep: 'AccountInfo', optional: true }, { dep: 'ExtensionInfo', optional: true }, { dep: 'RouterInteraction', optional: true }, @@ -34,6 +38,8 @@ export class Analytics extends RcModuleV2 implements IAnalytics { + appInitTime = Date.now(); + protected _useLog = this._deps.analyticsOptions.useLog ?? false; protected _lingerThreshold = @@ -41,6 +47,10 @@ export class Analytics protected _enablePendo = this._deps.analyticsOptions.enablePendo ?? false; + protected _analyticsKey = this._deps.analyticsOptions.analyticsKey; + protected _enableMixpanel = + this._deps.analyticsOptions.enableMixpanel ?? false; + protected _pendoApiKey = this._deps.analyticsOptions.pendoApiKey ?? ''; protected _trackRouters = @@ -65,12 +75,24 @@ export class Analytics this._deps.analyticsOptions.useLocalPendoJS ?? false; private _useLocalAnalyticsJS = this._deps.analyticsOptions.useLocalAnalyticsJS ?? false; + protected _identifyMixpanelPromise: Promise | undefined; + protected _identifyMixpanelResolve?: () => void; + + private _OSInfo: { OS: string; Device: string }; constructor(deps: T) { super({ deps, }); - this._segment = Segment(); + this._OSInfo = getOsInfo(); + this._segment = + this._enableMixpanel && this._analyticsKey ? null : Segment(); + if (this.enableMixpanel) { + mixpanel.init(this._analyticsKey); + // According to EU policy, we had to disable mixpanel to upload IP addresses + mixpanel.set_config({ ip: false }); + console.log('mixpanel init'); + } if (this._enablePendo && this._pendoApiKey) { Pendo.init( this._pendoApiKey, @@ -83,9 +105,12 @@ export class Analytics } override onInitOnce() { - if (this._deps.analyticsOptions.analyticsKey && this._segment) { + this._identifyMixpanelPromise = new Promise((resolve) => { + this._identifyMixpanelResolve = resolve; + }); + if (this._analyticsKey && this._segment) { this._segment.load( - this._deps.analyticsOptions.analyticsKey, + this._analyticsKey, { integrations: { All: true, @@ -96,8 +121,6 @@ export class Analytics ); } if (this._deps.routerInteraction) { - // make sure that track if refresh app - this.trackRouter(); watch( this, () => this._deps.routerInteraction!.currentPath, @@ -108,7 +131,10 @@ export class Analytics } } - trackRouter(currentPath = this._deps.routerInteraction?.currentPath) { + async trackRouter(currentPath = this._deps.routerInteraction?.currentPath) { + if (this.enableMixpanel) { + await this._identifyMixpanelPromise; + } const target = this.getTrackTarget(currentPath); if (target) { this.trackNavigation(target); @@ -136,18 +162,31 @@ export class Analytics } protected _identify({ userId, ...props }: IdentifyOptions) { - this.analytics?.identify(userId, props, { - integrations: { - All: true, - Mixpanel: true, - Pendo: this._enablePendo, - }, - }); + if (this.enableMixpanel) { + this._mixpanelInitialize({ userId }); + } else if (this.analytics) { + this.analytics.identify(userId, props, { + integrations: { + All: true, + Mixpanel: true, + Pendo: this._enablePendo, + }, + }); + } if (this._enablePendo && this._pendoApiKey) { this._pendoInitialize({ userId, ...props, env: this._env }); } } + protected _mixpanelInitialize({ userId }: { userId: string }) { + if (!userId || mixpanel.get_distinct_id?.() === userId) { + return; + } + console.log('mixpanel identify'); + mixpanel.identify(userId); + this._identifyMixpanelResolve?.(); + } + pendoIdentify({ userId, ...props @@ -200,7 +239,7 @@ export class Analytics @proxify async track(event: string, properties: any = {}) { - if (!this.analytics) { + if (!this.analytics && !this.enableMixpanel) { return; } @@ -209,14 +248,24 @@ export class Analytics ...properties, ...this.extendedProps.get(event), }; + if (this.enableMixpanel) { + // NOTE: Data tracking has been migrated from Segment to Mixpanel. + // Add id to identify in Mixpanel, so the usage data can be filtered same as before. + if (this._deps.auth?.ownerId) { + trackProps.id = this._deps.auth.ownerId; + } + mixpanel.track(event, trackProps); + } - this.analytics.track(event, trackProps, { - integrations: { - All: true, - Mixpanel: true, - Pendo: this._enablePendo, - }, - }); + if (this.analytics) { + this.analytics.track(event, trackProps, { + integrations: { + All: true, + Mixpanel: true, + Pendo: this._enablePendo, + }, + }); + } if (this._useLog) { this._logs.push({ @@ -304,6 +353,10 @@ export class Analytics }); } + toggleDebug() { + this.mixpanel.set_config({ debug: !this.mixpanel.get_config('debug') }); + } + get extendedProps() { return this._eventExtendedPropsMap; } @@ -312,18 +365,73 @@ export class Analytics return (global as any).analytics; } + get mixpanel() { + return mixpanel; + } + + @computed((that: Analytics) => [ + that._deps.brand.brandConfig, + that._deps.accountInfo?.id, + that._deps.extensionInfo?.country, + that._deps.extensionFeatures?.features, + ]) + private get trackedUserInfo(): TrackProps { + const userInfo: Record = { + BrandId: this._deps.brand.brandConfig.id, + AccountID: this._deps.accountInfo?.id, + BrandName: this._deps.brand.brandConfig.name, + CRMEnabled: this._deps.accountInfo?.isCRMEnabled, + servicePlanId: this._deps.accountInfo?.servicePlan.id, + edition: this._deps.accountInfo?.servicePlan.edition, + }; + + const features = this._deps.extensionFeatures?.features; + const isCallingEnabled = + features?.RingOut?.available || features?.WebPhone?.available; + const hasSmsPermission = + features?.PagesReceiving?.available || features?.SMSReceiving?.available; + const hasFaxPermission = features?.FaxReceiving?.available; + const hasGlipPermission = features?.Glip?.available; + + const properties = [ + { name: 'PhoneService', value: isCallingEnabled }, + { name: 'SMSService', value: hasSmsPermission }, + { name: 'FaxService', value: hasFaxPermission }, + { name: 'MessageService', value: hasGlipPermission }, + ]; + + properties.forEach(({ name, value }) => { + if (value !== undefined) { + userInfo[name] = value ? 'ON' : 'OFF'; + } + }); + + return userInfo as TrackProps; + } + get trackProps(): TrackProps { return { + ...this.trackedUserInfo, + ...this._OSInfo, appName: this._deps.brand.defaultConfig.appName, appVersion: this._deps.analyticsOptions.appVersion, brand: this._deps.brand.defaultConfig.code, 'App Language': this._deps.locale?.currentLocale || '', 'Browser Language': this._deps.locale?.browserLocale || '', 'Extension Type': this._deps.extensionInfo?.info.type || '', + 'App Init Time': this.appInitTime, }; } get pendo() { return this._pendo; } + + get enableMixpanel() { + return !!( + this._enableMixpanel && + this._analyticsKey && + (!this._deps.environment || this._deps.environment.allowDataTracking) + ); + } } diff --git a/packages/ringcentral-integration/modules/AppFeatures/AppFeatures.ts b/packages/ringcentral-integration/modules/AppFeatures/AppFeatures.ts index 87fd457a01..c2db9f1838 100644 --- a/packages/ringcentral-integration/modules/AppFeatures/AppFeatures.ts +++ b/packages/ringcentral-integration/modules/AppFeatures/AppFeatures.ts @@ -1,6 +1,7 @@ import { computed, RcModuleV2 } from '@ringcentral-integration/core'; import { Module } from '../../lib/di'; + import type { Deps, FeatureConfiguration } from './AppFeatures.interface'; // Required helps to make sure that the default config aligns with the interface diff --git a/packages/ringcentral-integration/modules/AudioSettings/AudioSettings.interface.ts b/packages/ringcentral-integration/modules/AudioSettings/AudioSettings.interface.ts index e51306a664..f021655573 100644 --- a/packages/ringcentral-integration/modules/AudioSettings/AudioSettings.interface.ts +++ b/packages/ringcentral-integration/modules/AudioSettings/AudioSettings.interface.ts @@ -4,17 +4,18 @@ import type { Auth } from '../Auth'; import type { Storage } from '../Storage'; export interface AudioSettingsData { - dialButtonVolume: number; - dialButtonMuted: boolean; ringtoneVolume: number; - ringtoneMuted: boolean; callVolume: number; outputDeviceId: string; inputDeviceId: string; + ringtoneDeviceId: string; hasAutoPrompted: boolean; + isAGCEnabled: boolean; } -export interface AudioSettingsOptions {} +export interface AudioSettingsOptions { + showCheckMediaAlert?: boolean; +} export interface Deps { auth: Auth; diff --git a/packages/ringcentral-integration/modules/AudioSettings/AudioSettings.ts b/packages/ringcentral-integration/modules/AudioSettings/AudioSettings.ts index 85c6114aca..9d42ff5ab6 100644 --- a/packages/ringcentral-integration/modules/AudioSettings/AudioSettings.ts +++ b/packages/ringcentral-integration/modules/AudioSettings/AudioSettings.ts @@ -1,15 +1,17 @@ -import { filter, find } from 'ramda'; - import { action, computed, RcModuleV2, state, storage, + watch, } from '@ringcentral-integration/core'; +import { OmitFunctions } from '@ringcentral-integration/utils/src/typeFunctions/OmitFunctions'; +import { filter, find } from 'ramda'; import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; + import type { AudioSettingsData, Deps } from './AudioSettings.interface'; import { audioSettingsErrors } from './audioSettingsErrors'; @@ -17,26 +19,44 @@ function polyfillGetUserMedia() { if (navigator.mediaDevices === undefined) { Object.assign(navigator, { mediaDevices: {} }); } - // @ts-expect-error + // @ts-expect-error TS(2339): Property 'getUserMedia' does not exist on type 'Na... Remove this comment to see the full error message navigator.getUserMedia = - // @ts-expect-error + // @ts-expect-error TS(2339): Property 'getUserMedia' does not exist on type 'Na... Remove this comment to see the full error message navigator.getUserMedia || (navigator as any).webkitGetUserMedia || (navigator as any).mozGetUserMedia; if ( navigator.mediaDevices.getUserMedia === undefined && - // @ts-expect-error + // @ts-expect-error TS(2339): Property 'getUserMedia' does not exist on type 'Na... Remove this comment to see the full error message navigator.getUserMedia ) { navigator.mediaDevices.getUserMedia = (constraints) => new Promise((resolve, reject) => { - // @ts-expect-error + // @ts-expect-error TS(2339): Property 'getUserMedia' does not exist on type 'Na... Remove this comment to see the full error message navigator.getUserMedia.call(navigator, constraints, resolve, reject); }); } } polyfillGetUserMedia(); +const DEFAULT_VALUE = { + // TODO: Remember to discuss migration plans if we change these properties. Changes that cause the volume settings to change can upset users. + ringtoneVolume: 0.5, + callVolume: 0.5, + outputDeviceId: 'default', + inputDeviceId: 'default', + ringtoneDeviceId: 'default', + hasAutoPrompted: false, + /** + * automatic gain control (AGC) + * Automatic gain control is a feature in which a sound source automatically manages + * changes in the volume of its source media to maintain a steady overall volume level. + * This feature is typically used on microphones, although it can be provided by other + * input sources as well. + */ + isAGCEnabled: false, +}; + @Module({ name: 'AudioSettings', deps: [ @@ -49,6 +69,7 @@ polyfillGetUserMedia(); }) export class AudioSettings extends RcModuleV2 { protected _getUserMediaPromise: Promise | null = null; + private _showCheckMediaAlert: boolean; constructor(deps: Deps) { super({ @@ -56,23 +77,48 @@ export class AudioSettings extends RcModuleV2 { storageKey: 'AudioSettings', enableCache: true, }); + + this._showCheckMediaAlert = + this._deps.audioSettingsOptions?.showCheckMediaAlert ?? false; + } + + override onInitOnce(): void | Promise { + // We add more properties to the data object + // need to check is there any key not exist value + // if so assign the data to default value + if ( + Object.keys(DEFAULT_VALUE).some( + (key) => this.data[key as keyof typeof DEFAULT_VALUE] === undefined, + ) + ) { + this._setData({ + ringtoneVolume: this.ringtoneVolume ?? DEFAULT_VALUE.ringtoneVolume, + callVolume: this.callVolume ?? DEFAULT_VALUE.callVolume, + outputDeviceId: this.outputDeviceId ?? DEFAULT_VALUE.outputDeviceId, + inputDeviceId: this.inputDeviceId ?? DEFAULT_VALUE.inputDeviceId, + isAGCEnabled: this.isAGCEnabled ?? DEFAULT_VALUE.isAGCEnabled, + ringtoneDeviceId: + this.ringtoneDeviceId ?? DEFAULT_VALUE.ringtoneDeviceId, + }); + } + watch( + this, + () => [this.isAGCEnabled, this.hasUserMedia], + () => { + if (this.hasUserMedia) { + this.setAutoGainControl(this.isAGCEnabled); + } + }, + { multiple: true }, + ); } @storage @state - data: AudioSettingsData = { - dialButtonVolume: 1, - dialButtonMuted: false, - ringtoneVolume: 0.3, - ringtoneMuted: false, - callVolume: 1, - outputDeviceId: 'default', - inputDeviceId: 'default', - hasAutoPrompted: false, - }; + data: AudioSettingsData = DEFAULT_VALUE; @state - availableDevices: MediaDeviceInfo[] = []; + availableDevices: OmitFunctions[] = []; @state hasUserMedia = false; @@ -88,6 +134,7 @@ export class AudioSettings extends RcModuleV2 { this.availableDevices = []; this.data.outputDeviceId = 'default'; this.data.inputDeviceId = 'default'; + this.data.ringtoneDeviceId = 'default'; } @action @@ -96,7 +143,7 @@ export class AudioSettings extends RcModuleV2 { } @action - setAvailableDevices(devices: MediaDeviceInfo[]) { + setAvailableDevices(devices: OmitFunctions[]) { this.availableDevices = devices; const isOutputDeviceExist = find( @@ -118,8 +165,10 @@ export class AudioSettings extends RcModuleV2 { ); if (!hasDefaultDevice && firstDevice) { this.data.outputDeviceId = firstDevice.deviceId; + this.data.ringtoneDeviceId = firstDevice.deviceId; } else { this.data.outputDeviceId = 'default'; + this.data.ringtoneDeviceId = 'default'; } } @@ -150,25 +199,23 @@ export class AudioSettings extends RcModuleV2 { @action _setData({ - dialButtonVolume = this.dialButtonVolume, - dialButtonMuted = this.dialButtonMuted, ringtoneVolume = this.ringtoneVolume, - ringtoneMuted = this.ringtoneMuted, callVolume = this.callVolume, outputDeviceId = this.outputDeviceId, inputDeviceId = this.inputDeviceId, + ringtoneDeviceId = this.ringtoneDeviceId, + isAGCEnabled = this.isAGCEnabled, }) { this.data.outputDeviceId = outputDeviceId; this.data.inputDeviceId = inputDeviceId; - this.data.dialButtonVolume = Math.min(1, Math.max(0, dialButtonVolume)); - this.data.dialButtonMuted = !!dialButtonMuted; + this.data.isAGCEnabled = isAGCEnabled; + this.data.ringtoneDeviceId = ringtoneDeviceId; this.data.ringtoneVolume = Math.min(1, Math.max(0, ringtoneVolume)); - this.data.ringtoneMuted = !!ringtoneMuted; - this.data.callVolume = Math.min(1, Math.max(0.1, callVolume)); + this.data.callVolume = Math.min(1, Math.max(0, callVolume)); } override initializeProxy() { - // Check audio permissions everytime app client starts + // Check audio permissions every time app client starts if (this.supportDevices) { this._checkDevices(); } @@ -211,12 +258,29 @@ export class AudioSettings extends RcModuleV2 { } } + @proxify + async setAutoGainControl(isAGCEnabled: boolean) { + try { + await navigator.mediaDevices.getUserMedia({ + audio: { + autoGainControl: isAGCEnabled, + }, + }); + } catch (err) { + console.warn(`setAutoGainControl error:`, err); + } + } + @proxify async _checkDevices() { const devices = await navigator.mediaDevices.enumerateDevices(); this.setAvailableDevices( - // TODO: formatting for devices info instances and replace JSON APIs. - devices.map((d) => JSON.parse(JSON.stringify(d))), + devices.map((d) => ({ + deviceId: d.deviceId, + kind: d.kind, + label: d.label, + groupId: d.groupId, + })), ); } @@ -250,7 +314,9 @@ export class AudioSettings extends RcModuleV2 { @proxify async _onGetUserMediaSuccess() { const userMediaAlert = find( - (item) => item.message === audioSettingsErrors.userMediaPermission, + (item) => + item.message === audioSettingsErrors.userMediaPermission || + item.message === audioSettingsErrors.checkMediaPermission, this._deps.alert.messages, ); if (userMediaAlert) { @@ -260,44 +326,60 @@ export class AudioSettings extends RcModuleV2 { await this._checkDevices(); } + @proxify + async showPermissionAlert(ttl?: number) { + if (this._showCheckMediaAlert) { + this._deps.alert.warning({ + message: audioSettingsErrors.checkMediaPermission, + allowDuplicates: false, + ttl: 0, + }); + } else { + this._deps.alert.danger({ + message: audioSettingsErrors.userMediaPermission, + allowDuplicates: false, + ttl, + }); + } + } + @proxify async onGetUserMediaError() { this.setUserMediaError(); - this._deps.alert.danger({ - message: audioSettingsErrors.userMediaPermission, - allowDuplicates: false, - }); + this.showPermissionAlert(); + } + + @proxify + async checkAudioAvailable() { + if (!this.userMedia) { + this.showPermissionAlert(30 * 1000); + } + this.getUserMedia(); } @proxify async showAlert() { if (!this.userMedia) { - this._deps.alert.danger({ - message: audioSettingsErrors.userMediaPermission, - allowDuplicates: false, - ttl: 30 * 1000, - }); + this.showPermissionAlert(30 * 1000); } } @proxify async setData({ - dialButtonVolume = this.dialButtonVolume, - dialButtonMuted = this.dialButtonMuted, ringtoneVolume = this.ringtoneVolume, - ringtoneMuted = this.ringtoneMuted, callVolume = this.callVolume, outputDeviceId = this.outputDeviceId, inputDeviceId = this.inputDeviceId, - }) { + ringtoneDeviceId = this.ringtoneDeviceId, + isAGCEnabled = this.isAGCEnabled, + }: AudioSettingsData) { this._setData({ - dialButtonVolume, - dialButtonMuted, ringtoneVolume, - ringtoneMuted, callVolume, outputDeviceId, inputDeviceId, + ringtoneDeviceId, + isAGCEnabled, }); } @@ -318,6 +400,10 @@ export class AudioSettings extends RcModuleV2 { return this.data.inputDeviceId; } + get isAGCEnabled() { + return this.data.isAGCEnabled; + } + get inputDevice() { return find( (device) => @@ -326,6 +412,10 @@ export class AudioSettings extends RcModuleV2 { ); } + get ringtoneDeviceId() { + return this.data.ringtoneDeviceId; + } + get supportDevices() { return !!( navigator.mediaDevices && navigator.mediaDevices.enumerateDevices @@ -341,29 +431,34 @@ export class AudioSettings extends RcModuleV2 { } @computed(({ availableDevices }: AudioSettings) => [availableDevices]) - get availableInputDevices() { - return filter( - (device) => device.kind === 'audioinput', + get availableRingtoneDevices() { + const ringtoneDevices = filter( + (device) => device.kind === 'audiooutput', this.availableDevices, ); - } - get dialButtonVolume() { - return this.data.dialButtonVolume; + return ringtoneDevices.length > 0 + ? ringtoneDevices.concat({ + deviceId: 'off', + groupId: '', + kind: 'audiooutput', + label: '', + }) + : []; } - get dialButtonMuted() { - return this.data.dialButtonMuted; + @computed(({ availableDevices }: AudioSettings) => [availableDevices]) + get availableInputDevices() { + return filter( + (device) => device.kind === 'audioinput', + this.availableDevices, + ); } get ringtoneVolume() { return this.data.ringtoneVolume; } - get ringtoneMuted() { - return this.data.ringtoneMuted; - } - get callVolume() { return this.data.callVolume; } @@ -383,4 +478,14 @@ export class AudioSettings extends RcModuleV2 { this.availableDevices.length && this.availableDevices[0].label !== '' ); } + + get isSupportAGC() { + try { + const constraints = navigator.mediaDevices.getSupportedConstraints(); + return !!constraints.autoGainControl; + } catch (err) { + console.error('failed to get autoGainControl support:', err); + return false; + } + } } diff --git a/packages/ringcentral-integration/modules/AudioSettings/audioSettingsErrors.ts b/packages/ringcentral-integration/modules/AudioSettings/audioSettingsErrors.ts index 05d2f1b32c..b42f0e421c 100644 --- a/packages/ringcentral-integration/modules/AudioSettings/audioSettingsErrors.ts +++ b/packages/ringcentral-integration/modules/AudioSettings/audioSettingsErrors.ts @@ -1,6 +1,13 @@ import { ObjectMap } from '@ringcentral-integration/core/lib/ObjectMap'; export const audioSettingsErrors = ObjectMap.prefixKeys( - ['userMediaPermission'], + [ + 'userMediaPermission', + 'ringtoneSizeOverLimit', + 'duplicateRingtone', + 'uploadRingtoneFailed', + 'deleteRingtoneFailed', + 'checkMediaPermission', + ], 'audioSettings', ); diff --git a/packages/ringcentral-integration/modules/Auth/Auth.interface.ts b/packages/ringcentral-integration/modules/Auth/Auth.interface.ts index 137d7c7c11..59315543bb 100644 --- a/packages/ringcentral-integration/modules/Auth/Auth.interface.ts +++ b/packages/ringcentral-integration/modules/Auth/Auth.interface.ts @@ -1,6 +1,7 @@ import type BaseTokenInfo from '@rc-ex/core/lib/definitions/TokenInfo'; import type { LoginUrlOptions as SdkLoginUrlOptions } from '@ringcentral/sdk'; +import type { RingCentralClient } from '../../lib/RingCentralClient'; import type { Alert } from '../Alert'; import type { Environment } from '../Environment'; import type { Locale } from '../Locale'; @@ -9,6 +10,7 @@ import type { TabManager } from '../TabManager'; export interface TokenInfo extends BaseTokenInfo { expire_time?: number; + refresh_token_expire_time?: number; } export interface Token { @@ -19,6 +21,7 @@ export interface Token { expireTime?: TokenInfo['expire_time']; expiresIn?: TokenInfo['expires_in']; scope?: TokenInfo['scope']; + refresh_token_expire_time?: TokenInfo['refresh_token_expire_time']; } export interface AuthOptions { @@ -26,7 +29,7 @@ export interface AuthOptions { } export interface Deps { - client: any; + client: RingCentralClient; alert: Alert; locale: Locale; tabManager?: TabManager; @@ -55,3 +58,15 @@ export interface LoginUrlOptions extends SdkLoginUrlOptions { redirectUri?: string; force?: boolean; } + +export interface BeforeLogoutHandler { + (): Promise | unknown | void; +} + +export interface AfterLoggedInHandler { + (): void; +} + +export interface RefreshErrorHandler { + (refreshTokenValid: boolean): Promise | void; +} diff --git a/packages/ringcentral-integration/modules/Auth/Auth.ts b/packages/ringcentral-integration/modules/Auth/Auth.ts index c487ddd3f4..3ddd2f7faa 100644 --- a/packages/ringcentral-integration/modules/Auth/Auth.ts +++ b/packages/ringcentral-integration/modules/Auth/Auth.ts @@ -1,5 +1,3 @@ -import url from 'url'; -import type GetExtensionInfoResponse from '@rc-ex/core/lib/definitions/GetExtensionInfoResponse'; import { action, RcModuleV2, @@ -10,16 +8,21 @@ import { import type { ApiError } from '@ringcentral/sdk'; import { trackEvents } from '../../enums/trackEvents'; +import { createRefreshTokenHelper } from '../../lib/createRefreshTokenHelper'; import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; -import validateIsOffline from '../../lib/validateIsOffline'; + import type { Deps, LoginOptions, LoginUrlOptions, Token, TokenInfo, + BeforeLogoutHandler, + AfterLoggedInHandler, + RefreshErrorHandler, } from './Auth.interface'; +import { matchKnownRequestErrors } from './authErrors'; import { authMessages } from './authMessages'; import { loginStatus } from './loginStatus'; @@ -40,12 +43,16 @@ export const TriggerSyncTokenEvent = 'triggerSyncTokenEvent'; }) class Auth extends RcModuleV2 { private _loggedIn = false; - _beforeLogoutHandlers: Set = new Set(); - _afterLoggedInHandlers: Set<() => any> = new Set(); + _beforeLogoutHandlers: Set = new Set(); + _afterLoggedInHandlers: Set = new Set(); + _onRefreshErrorHandlers: Set = new Set(); _unbindEvents?: () => void; _lastEnvironmentCounter = 0; - _proxyUri?: string; - _redirectUri?: string; + + private refreshTokenHelper = createRefreshTokenHelper( + () => this._deps.client.service.platform(), + console, + ); constructor(deps: Deps) { super({ @@ -80,7 +87,7 @@ class Auth extends RcModuleV2 { } @track(() => (analytics) => { - // @ts-expect-error + // @ts-expect-error TS(2339): Property 'setUserId' does not exist on type 'IAnal... Remove this comment to see the full error message analytics.setUserId(); return [trackEvents.authentication]; }) @@ -160,24 +167,28 @@ class Auth extends RcModuleV2 { _bindEvents() { if (this._unbindEvents) this._unbindEvents(); const platform = this._deps.client.service.platform(); - const client = this._deps.client.service._client; + const client = this._deps.client.service.client(); const onRequestError = async (error: ApiError) => { - if (error.response?.status === 403) { - const { errors = [] } = (await error.response?.clone().json()) || {}; - // @ts-expect-error - if (errors.some(({ errorCode } = {}) => errorCode === 'OAU-167')) { - this.logout(); - this._deps.alert.warning({ - message: authMessages.siteAccessForbidden, - payload: error, - ttl: 0, - }); - return; - } - } - if (error instanceof Error && error.message === 'Token revoked') { - this.logout(); + const matches = await matchKnownRequestErrors(error); + // logout solution + const logoutRequired = matches.some( + ([_0, _1, solutions]) => solutions?.logout, + ); + if (logoutRequired && this.loginStatus === loginStatus.loggedIn) { + await this.logout(); } + // alert solution + const alerts = matches + .map(([_0, _1, solutions]) => solutions?.alert) + .filter((x) => !!x) // remove empty + .filter((x, index, array) => array.indexOf(x) === index); // remove duplicates + alerts.forEach((alert) => { + this._deps.alert.warning({ + message: alert!, + payload: error, + ttl: 0, + }); + }); }; const onLoginSuccess = async () => { @@ -204,36 +215,33 @@ class Auth extends RcModuleV2 { }; const onRefreshError = async (error: ApiError) => { // user is still considered logged in if the refreshToken is still valid - const isOffline = validateIsOffline(error.message); - const resStatus = Number(error.response?.status); + const { refreshTokenValid, resStatus } = + await this.refreshTokenHelper.getRefreshTokenState(error); - const refreshTokenValid = Boolean( - (isOffline || resStatus >= 500) && - (await platform.auth().refreshTokenValid()), + const handlers = [...this._onRefreshErrorHandlers]; + const results = await Promise.allSettled( + handlers.map(async (handler) => await handler(refreshTokenValid)), ); + results.forEach((x) => { + if (x.status === 'rejected') { + console.warn('Trigger [RefreshErrorHandler] failed', x.reason); + } + }); this.setRefreshError(refreshTokenValid); - const isAARError = - resStatus === 403 && - (await error.response?.clone().json())?.error?.some( - ({ errorCode = '' } = {}) => errorCode === 'OAU-167', - ); - - if ( - !isAARError && - !refreshTokenValid && - (await platform.auth().data()).access_token !== '' - ) { - this._deps.alert.danger({ - message: authMessages.sessionExpired, - payload: error, - ttl: 0, - }); - // clean the cache so the error doesn't show again - platform._cache.clean(); - return; - } + await this.refreshTokenHelper.processRefreshError({ + error, + refreshTokenValid, + resStatus, + onSessionExpired: () => { + this._deps.alert.danger({ + message: authMessages.sessionExpired, + payload: error, + ttl: 0, + }); + }, + }); }; platform.addListener(platform.events.loginSuccess, onLoginSuccess); platform.addListener(platform.events.loginError, onLoginError); @@ -291,8 +299,7 @@ class Auth extends RcModuleV2 { } override async onInit() { - const platform = this._deps.client.service.platform(); - this._loggedIn = await platform.loggedIn(); + this._loggedIn = await this.refreshTokenHelper.loggedIn(); this._bindEvents(); watch( this, @@ -312,6 +319,9 @@ class Auth extends RcModuleV2 { } }, ); + + // must check token from storage before that module ready, put that inside onInit lifeCycle + await this.fetchToken(); } async fetchToken() { @@ -325,18 +335,6 @@ class Auth extends RcModuleV2 { }); } - override async onInitSuccess() { - await this.fetchToken(); - } - - get redirectUri() { - return url.resolve(window.location.href, this._redirectUri!); - } - - get proxyUri() { - return this._proxyUri; - } - get ownerId() { return this.token.ownerId; } @@ -379,10 +377,7 @@ class Auth extends RcModuleV2 { refresh_token_expires_in: expiresIn, scope, }); - const extensionData: GetExtensionInfoResponse = await this._deps.client - .account() - .extension() - .get(); + const extensionData = await this._deps.client.account().extension().get(); ownerId = extensionData.id; } // TODO: support to set redirectUri in js sdk v4 login function @@ -472,11 +467,12 @@ class Auth extends RcModuleV2 { } /** - * @function - * @param {Function} handler - * @returns {Function} return that delete handler event, call that will delete that event + * Add handler on "before logout" event + * - Return anything not empty in the handler to cancel the logout as needed + * @param handler event handler function + * @returns cancel current handler, call that will delete the handler from that event */ - addBeforeLogoutHandler(handler: Function): Function { + addBeforeLogoutHandler(handler: BeforeLogoutHandler) { this._beforeLogoutHandlers.add(handler); return () => { this.removeBeforeLogoutHandler(handler); @@ -484,20 +480,53 @@ class Auth extends RcModuleV2 { } /** - * @function - * @param {Function} handler + * Remove handler from "before logout" event + * @param handler event handler function */ - removeBeforeLogoutHandler(handler: Function) { + removeBeforeLogoutHandler(handler: BeforeLogoutHandler) { this._beforeLogoutHandlers.delete(handler); } - addAfterLoggedInHandler(handler: () => any) { + /** + * Add handler on "after logged in" event + * @param handler event handler function + * @returns cancel current handler, call that will delete the handler from that event + */ + addAfterLoggedInHandler(handler: AfterLoggedInHandler) { this._afterLoggedInHandlers.add(handler); return () => { - this._afterLoggedInHandlers.delete(handler); + this.removeAfterLoggedInHandler(handler); + }; + } + + /** + * Remove handler from "after logged in" event + * @param handler event handler function + */ + removeAfterLoggedInHandler(handler: AfterLoggedInHandler) { + this._afterLoggedInHandlers.delete(handler); + } + + /** + * Add handler on "refresh error" event + * @param handler event handler function + * @returns cancel current handler, call that will delete the handler from that event + */ + addRefreshErrorHandler(handler: RefreshErrorHandler) { + this._onRefreshErrorHandlers.add(handler); + return () => { + this.removeRefreshErrorHandler(handler); }; } + /** + * Remove handler from "refresh error" event + * @param handler event handler function + */ + removeRefreshErrorHandler(handler: RefreshErrorHandler) { + this._onRefreshErrorHandlers.delete(handler); + } + @proxify async refreshImplicitToken({ tokenType, @@ -511,10 +540,7 @@ class Auth extends RcModuleV2 { endpointId: TokenInfo['endpoint_id']; }) { try { - const extensionData: GetExtensionInfoResponse = await this._deps.client - .account() - .extension() - .get(); + const extensionData = await this._deps.client.account().extension().get(); const ownerId = String(extensionData.id); if (ownerId !== String(this.ownerId)) { return; diff --git a/packages/ringcentral-integration/modules/Auth/authErrors.ts b/packages/ringcentral-integration/modules/Auth/authErrors.ts new file mode 100644 index 0000000000..60fcafd046 --- /dev/null +++ b/packages/ringcentral-integration/modules/Auth/authErrors.ts @@ -0,0 +1,295 @@ +import type { ApiError } from '@ringcentral/sdk'; + +import { type AuthMessage, authMessages } from './authMessages'; + +export interface Solutions { + logout?: boolean; + alert?: AuthMessage; +} + +export type AuthErrorConfig = [ + httpCode: number, + errorCode: string, + solutions?: Solutions, + endpoints?: RegExp[], +]; + +/** + * Authentication related error codes + * https://developers.ringcentral.com/guide/basics/errors + */ +export const AUTH_ERRORS: Record = { + /** + * 'Parameter [brandId] is invalid' + */ + 'OAU-101': [ + 403, + 'OAU-101', + { logout: true, alert: authMessages.accessDenied }, + ], + + /** + * 'Unable to issue authorization code' + */ + 'OAU-102': [403, 'OAU-102', { alert: authMessages.accessDenied }], + + /** + * 'Login for ${extensionType} extension is not allowed.' + */ + 'OAU-105': [403, 'OAU-105', { alert: authMessages.accessDenied }], + + /** + * 'Invalid authorization code' + */ + 'OAU-106': [403, 'OAU-106', { alert: authMessages.accessDenied }], + + /** + * 'Authorization code is expired' + */ + 'OAU-108': [403, 'OAU-108', { alert: authMessages.accessDenied }], + + /** + * 'Redirect URIs do not match' + */ + 'OAU-109': [403, 'OAU-109', { alert: authMessages.accessDenied }], + + /** + * 'Authorization code was not issued for this application' + */ + 'OAU-110': [403, 'OAU-110', { alert: authMessages.accessDenied }], + + /** + * 'Request parameter duplication detected' + */ + 'OAU-111': [400, 'OAU-111', { alert: authMessages.accessDenied }], + + /** + * 'The client is unauthorized for the required grant type: [${grant_type}]', + */ + 'OAU-112': [403, 'OAU-112', { alert: authMessages.accessDenied }], + + /** + * 'No redirect uri is registered for the client' + */ + 'OAU-113': [403, 'OAU-113', { alert: authMessages.accessDenied }], + + /** + * 'Invalid authorization method' + */ + 'OAU-116': [403, 'OAU-116', { alert: authMessages.accessDenied }], + + /** + * 'The scope of requesting application cannot be narrower than the target application' + */ + 'OAU-117': [403, 'OAU-117', { alert: authMessages.accessDenied }], + + /** + * 'International Virtual number cannot be used to login' + */ + 'OAU-119': [403, 'OAU-119', { alert: authMessages.accessDenied }], + + /** + * 'Wrong Application ID' + */ + 'OAU-120': [401, 'OAU-120', { alert: authMessages.internalError }], + + /** + * 'Wrong Application' + */ + 'OAU-121': [401, 'OAU-121', { alert: authMessages.internalError }], + + /** + * 'Invalid Authorization header value: ${parameter}' + */ + 'OAU-123': [401, 'OAU-123', { alert: authMessages.internalError }], + + /** + * 'Grant type is not allowed for application.' + */ + 'OAU-125': [401, 'OAU-125', { alert: authMessages.internalError }], + + /** + * 'Invalid application release.' + */ + 'OAU-127': [401, 'OAU-127', { alert: authMessages.internalError }], + + /** + * 'Access token expired.' + */ + 'OAU-128': [401, 'OAU-128', {}], + + /** + * 'Access token corrupted.' + */ + 'OAU-129': [401, 'OAU-129', {}], + + /** + * 'Invalid Authorization header.' + */ + 'OAU-134': [401, 'OAU-134', { alert: authMessages.internalError }], + + /** + * 'Extension not found.' + */ + 'OAU-136': [ + 401, + 'OAU-136', + { logout: true, alert: authMessages.internalError }, + ], + + /** + * 'Invalid resource owner credentials.' + */ + 'OAU-140': [401, 'OAU-140', { alert: authMessages.internalError }], + + /** + * 'Login for extension in current state is not allowed.' + */ + 'OAU-141': [ + 401, + 'OAU-141', + { logout: true, alert: authMessages.internalError }, + [/\/restapi\/oauth\/token$/i], // matches "*/restapi/oauth/token" + ], + + /** + * 'Login to account in current state is not allowed.' + */ + 'OAU-142': [ + 401, + 'OAU-142', + { logout: true, alert: authMessages.internalError }, + [/\/restapi\/oauth\/token$/i], // matches "*/restapi/oauth/token" + ], + + /** + * 'Invalid client credentials' + */ + 'OAU-146': [401, 'OAU-146', { alert: authMessages.internalError }], + + /** + * 'The account is locked out due to multiple unsuccessful logon attempts.' + */ + 'OAU-147': [400, 'OAU-147', { alert: authMessages.internalError }], + + /** + * 'The account is locked out due to multiple unsuccessful logon attempts. Please use Single Sign-on way to authenticate.' + */ + 'OAU-148': [400, 'OAU-148', { alert: authMessages.internalError }], + + /** + * 'Unparsable access token' + */ + 'OAU-149': [401, 'OAU-149', { alert: authMessages.internalError }], + + /** + * 'The value of query parameter [${queryParameterName}] should be equal to parameter [${requestParameterName}] in request body' + */ + 'OAU-150': [400, 'OAU-150', { alert: authMessages.internalError }], + + /** + * 'Authorization method not supported' + */ + 'OAU-151': [401, 'OAU-151', { alert: authMessages.internalError }], + + /** + * 'Password grant is not allowed because MFA is required.' + */ + 'OAU-168': [401, 'OAU-168', { alert: authMessages.internalError }], + + /** + * 'Token not found' + * - https://rc-wiki-domian/pages/viewpage.action?pageId=476097866 + */ + 'OAU-213': [ + -1, + 'OAU-213', + { logout: true, alert: authMessages.internalError }, + [/\/restapi\/oauth\/token$/i], // matches "*/restapi/oauth/token" + ], + + /** + * 'Extension not found' + * - https://rc-wiki-domian/pages/viewpage.action?pageId=476097866 + */ + 'OAU-232': [ + -1, + 'OAU-232', + { logout: true, alert: authMessages.internalError }, + ], + + /** + * 'Extension is disabled or frozen' + * - https://rc-wiki-domian/pages/viewpage.action?pageId=476097866 + */ + 'OAU-236': [ + -1, + 'OAU-236', + { logout: true, alert: authMessages.internalError }, + ], + + /** + * 'Sandbox client is not allowed: ${client_id}' + * - https://rc-wiki-domian/pages/viewpage.action?pageId=476097866 + */ + 'OAU-165': [-1, 'OAU-165', { alert: authMessages.internalError }], + + /** + * 'Account does not exist' + * - https://rc-wiki-domian/pages/viewpage.action?pageId=476097866 + */ + 'OAU-222': [ + -1, + 'OAU-222', + { logout: true, alert: authMessages.internalError }, + ], + + /** + * 'Site access forbidden' + * - Migrate from old implementation + */ + 'OAU-167': [ + 403, + 'OAU-167', + { logout: true, alert: authMessages.siteAccessForbidden }, + ], +}; + +export type ResponseErrorCode = keyof typeof AUTH_ERRORS; + +export interface ResponseErrorInfo { + errorCode: ResponseErrorCode; + message: string; + additionalInfo?: string; +} + +export const matchKnownRequestErrors = async (apiError: ApiError) => { + const matches: AuthErrorConfig[] = []; + if (apiError.response) { + const url = apiError.request?.url ?? apiError.response?.url; + // try to parse the response to get error code + try { + // clone the response to process to avoid affecting other error handling logic + ( + (await apiError.response.clone().json()) as { + errors?: ResponseErrorInfo[]; + } + ).errors?.forEach((err) => { + const errorConfig = AUTH_ERRORS[err.errorCode]; + if (errorConfig) { + const [, , , endpoints] = errorConfig; + if ( + !endpoints?.length || + endpoints.some((endpoint) => endpoint.test(url)) + ) { + matches.push(errorConfig); + } + } + }); + } catch (_) { + // ignore + } + } + + return matches; +}; diff --git a/packages/ringcentral-integration/modules/Auth/authMessages.ts b/packages/ringcentral-integration/modules/Auth/authMessages.ts index 7195201643..a5c8d6bc17 100644 --- a/packages/ringcentral-integration/modules/Auth/authMessages.ts +++ b/packages/ringcentral-integration/modules/Auth/authMessages.ts @@ -1,4 +1,7 @@ -import { ObjectMap } from '@ringcentral-integration/core/lib/ObjectMap'; +import { + ObjectMap, + type ObjectMapValue, +} from '@ringcentral-integration/core/lib/ObjectMap'; export const authMessages = ObjectMap.prefixKeys( [ @@ -13,3 +16,5 @@ export const authMessages = ObjectMap.prefixKeys( ], 'authMessages', ); + +export type AuthMessage = ObjectMapValue; diff --git a/packages/ringcentral-integration/modules/Auth/index.ts b/packages/ringcentral-integration/modules/Auth/index.ts index ce6e917c73..31f73d59b3 100644 --- a/packages/ringcentral-integration/modules/Auth/index.ts +++ b/packages/ringcentral-integration/modules/Auth/index.ts @@ -2,3 +2,4 @@ export * from './Auth'; export * from './Auth.interface'; export * from './authMessages'; export * from './loginStatus'; +export * from './authErrors'; diff --git a/packages/ringcentral-integration/modules/AvailabilityMonitor/AvailabilityMonitor.ts b/packages/ringcentral-integration/modules/AvailabilityMonitor/AvailabilityMonitor.ts index f5a2942bf6..29bb7d0de9 100644 --- a/packages/ringcentral-integration/modules/AvailabilityMonitor/AvailabilityMonitor.ts +++ b/packages/ringcentral-integration/modules/AvailabilityMonitor/AvailabilityMonitor.ts @@ -1,5 +1,3 @@ -import { pathOr } from 'ramda'; - import { action, computed, @@ -8,10 +6,12 @@ import { watch, } from '@ringcentral-integration/core'; import type { ApiError } from '@ringcentral/sdk'; +import { pathOr } from 'ramda'; import { promisedThrottle } from '../../lib/debounce-throttle'; import { Module } from '../../lib/di'; import validateIsOffline from '../../lib/validateIsOffline'; + import type { Deps, ErrorMessages, @@ -52,9 +52,9 @@ export class AvailabilityMonitor extends RcModuleV2 { protected _promise: Promise | null = null; - protected _healthRetryTime = HEALTH_CHECK_INTERVAL; + _healthRetryTime = HEALTH_CHECK_INTERVAL; - protected _unbindHandlers: (() => void) | null = null; + _unbindHandlers: (() => void) | null = null; constructor(deps: Deps) { super({ @@ -226,13 +226,8 @@ export class AvailabilityMonitor extends RcModuleV2 { * */ _requestErrorHandler = async (error: ApiError) => { - if ( - error.response && - !(error.response as ApiError['response'] & ErrorMessages)._json - ) { - (error.response as ApiError['response'] & ErrorMessages)._json = - await error.response.clone().json(); - } + await this.attachErrorResponse(error); + const requestUrl = pathOr('', ['request', 'url'], error); const extractedUrl = extractUrl({ url: requestUrl, @@ -341,7 +336,7 @@ export class AvailabilityMonitor extends RcModuleV2 { // Client app can even continue use expired access token with this API - backend will pass such requests through. // The result of the API call is unpredictable when it is called without access token! // - // Reference: https://wiki.ringcentral.com/display/PLAT/High+Availability+Guidelines+for+API+Clients + // Reference: https://wiki_domain/display/PLAT/High+Availability+Guidelines+for+API+Clients const res: Response = await this._deps.client.service .platform() @@ -390,7 +385,7 @@ export class AvailabilityMonitor extends RcModuleV2 { // The idea is that if the server is down temporarily, // it is not overwhelmed with requests hitting at the same time when it comes back up. // - // Reference: https://wiki.ringcentral.com/display/PLAT/Error+Handling+Guidelines+for+API+Clients + // Reference: https://wiki_domain/display/PLAT/Error+Handling+Guidelines+for+API+Clients this._randomTime = this._randomTime || generateRandomNumber(); // Generate random seconds (1 ~ 121) this._normalTimeout = setTimeout(() => { @@ -414,13 +409,23 @@ export class AvailabilityMonitor extends RcModuleV2 { */ async checkIfHAError(error: ApiError) { const errMessage = pathOr(null, ['message'], error); - if (error.response) { - (error.response as ApiError['response'] & ErrorMessages)._json = - await error.response.clone().json(); - } + await this.attachErrorResponse(error); + return isHAError(error) || errMessage === errorMessages.serviceLimited; } + private async attachErrorResponse(error: ApiError) { + const response = error.response as ApiError['response'] & ErrorMessages; + if (response && !response._json) { + try { + response._json = await response.clone().json(); + } catch (err) { + // ignore response json error + console.error('error from response.json()', { error, err }); + } + } + } + /** * Is App in limited mode */ @@ -472,7 +477,7 @@ export class AvailabilityMonitor extends RcModuleV2 { this._retrieveSharedStates(); } }); - window.addEventListener('unload', () => { + window.addEventListener('pagehide', () => { isUnloading = true; this._unloadSharedStates(); }); diff --git a/packages/ringcentral-integration/modules/AvailabilityMonitor/availabilityMonitorHelper.ts b/packages/ringcentral-integration/modules/AvailabilityMonitor/availabilityMonitorHelper.ts index 02589d7050..356d84a8ba 100644 --- a/packages/ringcentral-integration/modules/AvailabilityMonitor/availabilityMonitorHelper.ts +++ b/packages/ringcentral-integration/modules/AvailabilityMonitor/availabilityMonitorHelper.ts @@ -1,6 +1,5 @@ -import { pathOr } from 'ramda'; - import type { ApiError } from '@ringcentral/sdk'; +import { pathOr } from 'ramda'; import type { ErrorMessages } from './AvailabilityMonitor.interface'; import { availability } from './availabilityStatus'; diff --git a/packages/ringcentral-integration/modules/Brand/Brand.interface.ts b/packages/ringcentral-integration/modules/Brand/Brand.interface.ts index cc22059973..ddf7dc8741 100644 --- a/packages/ringcentral-integration/modules/Brand/Brand.interface.ts +++ b/packages/ringcentral-integration/modules/Brand/Brand.interface.ts @@ -1,4 +1,7 @@ +import type { RcThemeInput } from '@ringcentral/juno'; + import type { Locale } from '../Locale'; + import type { BrandConfig } from './BrandConfig.interface'; export interface BrandConfigOptions { @@ -15,3 +18,15 @@ export interface Deps { prefix?: string; brandConfigOptions?: BrandConfigOptions; } + +export type ThemeInfo = { + id: string; + theme: RcThemeInput; +}; + +export interface BrandThemeMap { + default: ThemeInfo[]; + light: ThemeInfo[]; + dark: ThemeInfo[]; + contrast: ThemeInfo[]; +} diff --git a/packages/ringcentral-integration/modules/Brand/Brand.ts b/packages/ringcentral-integration/modules/Brand/Brand.ts index 5b59540828..2b78bc42a1 100644 --- a/packages/ringcentral-integration/modules/Brand/Brand.ts +++ b/packages/ringcentral-integration/modules/Brand/Brand.ts @@ -3,14 +3,16 @@ import { computed, RcModuleV2, state, + watch, } from '@ringcentral-integration/core'; import { DEFAULT_LOCALE } from '@ringcentral-integration/i18n'; import { Module } from '../../lib/di'; import { processI18n } from '../../lib/processI18n'; -import type { Deps } from './Brand.interface'; + +import type { BrandThemeMap, Deps } from './Brand.interface'; import type { BrandConfig } from './BrandConfig.interface'; -import { processAssets } from './helpers'; +import { processAssets } from './processAssets'; @Module({ name: 'Brand', @@ -33,6 +35,8 @@ export class Brand< this._prefix = `${this._deps.brandConfig.code}-${ this._deps.brandConfig.application ?? '' }`; + + this.bindUpdateDocumentVariables(); } @state @@ -43,6 +47,46 @@ export class Brand< this._dynamicConfig = config; } + @state + themeMap: BrandThemeMap = { + default: [], + light: [], + dark: [], + contrast: [], + }; + + @action + setThemeMap(val: BrandThemeMap) { + this.themeMap = val; + } + + private bindUpdateDocumentVariables() { + if (!global.document) return; + + const updateDocumentVariables = () => { + const check = () => JSON.stringify(this.brandConfig.styleVariable); + let updateVal = check(); + this.updateDocumentVariables(); + + watch(this, check, (val) => { + if (val === updateVal) return; + updateVal = val; + this.updateDocumentVariables(); + }); + }; + + // * in old arch chrome extension content page, need to wait a tick, otherwise the watch event will not get any update + Promise.resolve().then(updateDocumentVariables); + } + + private updateDocumentVariables() { + // apply that style variable to global css variable + Object.entries(this.brandConfig.styleVariable ?? {}).forEach( + ([key, value]) => { + document.documentElement.style.setProperty(`--${key}`, `${value}`); + }, + ); + } /** * dynamic brand config with i18n processed with currentLocale */ diff --git a/packages/ringcentral-integration/modules/Brand/BrandConfig.interface.ts b/packages/ringcentral-integration/modules/Brand/BrandConfig.interface.ts index 1faef7dc63..7c7b39707f 100644 --- a/packages/ringcentral-integration/modules/Brand/BrandConfig.interface.ts +++ b/packages/ringcentral-integration/modules/Brand/BrandConfig.interface.ts @@ -10,7 +10,7 @@ export type I18nStrings = { }; }; -export type URL = `https://${string}` | `http://${string}`; +export type UrlString = `https://${string}` | `http://${string}`; export type Protocol = `${string}://`; @@ -74,7 +74,7 @@ export interface CallWithJupiterConfig { /** * Branded Jupiter call link */ - link: URL; + link: UrlString; /** * Branded Jupiter call protocol */ @@ -144,15 +144,23 @@ export interface BrandConfig { /** * Eula or Terms of Service link */ - eulaLink?: I18nStrings | URL; + eulaLink?: I18nStrings | UrlString; /** * Eula or Terms of Service link label */ eulaLabel?: I18nStrings | string; + /** + * Privacy Notice link + */ + privacyNotice?: UrlString; + /** + * Privacy Notice label + */ + privacyNoticeLabel?: I18nStrings | string; /** * Teleconference Url (for RCM) */ - teleconference?: URL; + teleconference?: UrlString; /** * Signup Url */ @@ -182,7 +190,7 @@ export interface BrandConfig { * Rcv E2EE support link */ - rcvE2EESupportUrl?: URL; + rcvE2EESupportUrl?: UrlString; /** * RCV default Meeting topic */ @@ -232,7 +240,7 @@ export interface BrandConfig { * rcv meeting teleconference Url * https://docs.google.com/spreadsheets/d/1fizbsFfVt0jur4BjJnYOD1bk1t_oH0XTquRNTfyv4Ws */ - rcvTeleconference: URL; + rcvTeleconference: UrlString; /** * allow region setting */ @@ -258,6 +266,10 @@ export interface BrandConfig { * assets path array */ assets?: Record; + /** + * assets meta data to apply to root element variable and others + */ + styleVariable?: Record; /** * */ @@ -265,7 +277,7 @@ export interface BrandConfig { /** * dialInNumbers link for Conference call */ - dialInNumbersLink: URL; + dialInNumbersLink: UrlString; /** * conference invite text */ @@ -279,10 +291,6 @@ export interface BrandConfig { * sub brands */ subBrands?: SubBrand[]; - /** - * build mode - */ - mode?: Mode; /** * disable call with RingCentral phone */ diff --git a/packages/ringcentral-integration/modules/Brand/index.ts b/packages/ringcentral-integration/modules/Brand/index.ts index 013e5cfa9c..474e6b2b76 100644 --- a/packages/ringcentral-integration/modules/Brand/index.ts +++ b/packages/ringcentral-integration/modules/Brand/index.ts @@ -3,3 +3,4 @@ export * from './Brand.interface'; export * from './BrandConfig.interface'; export * from './createBrandConfig'; export * from './defaultBrandConfig'; +export * from './processAssets'; diff --git a/packages/ringcentral-integration/modules/Brand/helpers.ts b/packages/ringcentral-integration/modules/Brand/processAssets.ts similarity index 68% rename from packages/ringcentral-integration/modules/Brand/helpers.ts rename to packages/ringcentral-integration/modules/Brand/processAssets.ts index bbaed4fa44..72e3cb4ed4 100644 --- a/packages/ringcentral-integration/modules/Brand/helpers.ts +++ b/packages/ringcentral-integration/modules/Brand/processAssets.ts @@ -8,28 +8,27 @@ export const processAssets = ( origin: string, ) => { // when that processed, return directly - // @ts-expect-error - if (assets[processedKey]) { + if (!origin || assets![processedKey]) { return assets; } + // Remove last slash from origin if any + origin = origin.replace(/\/+$/, ''); + const getUrl = (url: string) => { return `${origin}${url}`; }; - // @ts-expect-error - return Object.entries(assets).reduce( + return Object.entries(assets!).reduce( (acc, [key, url]) => { if (!url) { return acc; } if (Array.isArray(url)) { - // @ts-expect-error - acc[key] = url.map((x) => getUrl(x)); + acc![key] = url.map((x) => getUrl(x)); } else { - // @ts-expect-error - acc[key] = getUrl(url); + acc![key] = getUrl(url); } return acc; diff --git a/packages/ringcentral-integration/modules/BrowserLogger/BrowserLogger.interface.ts b/packages/ringcentral-integration/modules/BrowserLogger/BrowserLogger.interface.ts new file mode 100644 index 0000000000..7e6ff20ad2 --- /dev/null +++ b/packages/ringcentral-integration/modules/BrowserLogger/BrowserLogger.interface.ts @@ -0,0 +1,24 @@ +import type { Logger } from '@ringcentral/mfe-logger'; + +import type { Storage } from '../Storage'; + +export interface BrowserLoggerOptions { + /** + * logger name + */ + name?: string; + /** + * enabled logger initial + */ + enabled?: boolean; + /** + * logger + */ + logger?: Logger; +} + +export interface Deps { + storage: Storage; + prefix?: string; + browserLoggerOptions?: BrowserLoggerOptions; +} diff --git a/packages/ringcentral-integration/modules/BrowserLogger/BrowserLogger.ts b/packages/ringcentral-integration/modules/BrowserLogger/BrowserLogger.ts new file mode 100644 index 0000000000..b29289aad5 --- /dev/null +++ b/packages/ringcentral-integration/modules/BrowserLogger/BrowserLogger.ts @@ -0,0 +1,134 @@ +import { + action, + globalStorage, + RcModuleV2, + state, + watch, +} from '@ringcentral-integration/core'; +import { + loggerV2, + toggleLogger, +} from '@ringcentral-integration/core/lib/logger/loggerV2'; +import { StorageTransport } from '@ringcentral/mfe-logger'; + +import { Module } from '../../lib/di'; +import { proxify } from '../../lib/proxy/proxify'; + +import type { Deps } from './BrowserLogger.interface'; + +@Module({ + name: 'BrowserLogger', + deps: [ + 'GlobalStorage', + { dep: 'Prefix', optional: true }, + { dep: 'BrowserLoggerOptions', optional: true }, + ], +}) +export class BrowserLogger extends RcModuleV2 { + constructor(deps: Deps) { + super({ + deps, + storageKey: 'BrowserLogger', + enableGlobalCache: true, + }); + + watch( + this, + () => [this.enabled, this.ready], + () => { + if (!this.ready) return; + try { + if (this.enabled) { + this.logger.enable(); + this.logger.log('BrowserLogger enabled'); + } else { + this.logger.disable(); + } + } catch (e) { + console.error(e); + } + }, + { + multiple: true, + }, + ); + } + + @globalStorage + @state + enabled = this._deps.browserLoggerOptions?.enabled ?? false; + + @action + protected _enable() { + this.enabled = true; + } + + /** + * enable logger + */ + @proxify + async enable() { + await this.toggleLogger(true); + this._enable(); + } + + @action + protected _disable() { + this.enabled = false; + } + + /** + * disable logger + */ + @proxify + async disable() { + await this.toggleLogger(false); + this._disable(); + } + + @proxify + async toggleLogger(enabled: boolean) { + toggleLogger(enabled); + } + + @state + downloading = false; + + @action + protected _setDownloading(val: boolean) { + this.downloading = val; + } + + /** + * set downloading + */ + @proxify + async setDownloading(val: boolean) { + this._setDownloading(val); + } + + get logger() { + return this._deps.browserLoggerOptions?.logger ?? loggerV2; + } + + @proxify + async saveLog() { + await this.setDownloading(true); + try { + if (this.storageTransport) { + const name = this._deps.prefix; + await this.storageTransport.downloadLogs({ name }); + } else { + throw new Error('StorageTransport not found'); + } + } finally { + await this.setDownloading(false); + } + } + + protected get storageTransport() { + return this.logger.transports.find( + (transport) => transport instanceof StorageTransport, + ) as StorageTransport | void; + } +} diff --git a/packages/ringcentral-integration/modules/BrowserLogger/index.ts b/packages/ringcentral-integration/modules/BrowserLogger/index.ts new file mode 100644 index 0000000000..1249acd436 --- /dev/null +++ b/packages/ringcentral-integration/modules/BrowserLogger/index.ts @@ -0,0 +1,2 @@ +export * from './BrowserLogger'; +export * from './BrowserLogger.interface'; diff --git a/packages/ringcentral-integration/modules/Call/Call.interface.ts b/packages/ringcentral-integration/modules/Call/Call.interface.ts index 961976ad53..f6f7c8e748 100644 --- a/packages/ringcentral-integration/modules/Call/Call.interface.ts +++ b/packages/ringcentral-integration/modules/Call/Call.interface.ts @@ -18,7 +18,6 @@ export interface ToNumberMatched { } export interface CallOptions { - internationalCheck?: boolean; useCallControlToMakeCall?: boolean; } diff --git a/packages/ringcentral-integration/modules/Call/Call.ts b/packages/ringcentral-integration/modules/Call/Call.ts index bf19be56e7..05d0332060 100644 --- a/packages/ringcentral-integration/modules/Call/Call.ts +++ b/packages/ringcentral-integration/modules/Call/Call.ts @@ -7,13 +7,14 @@ import { } from '@ringcentral-integration/core'; import extractControls from '@ringcentral-integration/phone-number/lib/extractControls'; +import { trackEvents } from '../../enums/trackEvents'; import { Module } from '../../lib/di'; import { isBlank } from '../../lib/isBlank'; import { proxify } from '../../lib/proxy/proxify'; import { validateNumbers } from '../../lib/validateNumbers'; -import { trackEvents } from '../../enums/trackEvents'; import { callingModes } from '../CallingSettings'; import { ringoutErrors } from '../Ringout'; + import type { Deps, Recipient, ToNumberMatched } from './Call.interface'; import { callErrors } from './callErrors'; import { callStatus } from './callStatus'; @@ -59,8 +60,7 @@ export class Call< T extends Deps = Deps, K extends Recipient = Recipient, > extends RcModuleV2 { - _internationalCheck: boolean; - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'string'. _callSettingMode: string = null; _useCallControlToMakeCall: boolean; @@ -70,8 +70,6 @@ export class Call< enableCache: true, storageKey: 'callData', }); - this._internationalCheck = - this._deps.callOptions?.internationalCheck ?? true; this._useCallControlToMakeCall = this._deps.callOptions?.useCallControlToMakeCall ?? false; } @@ -145,9 +143,9 @@ export class Call< @action connect({ isConference, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'string'. phoneNumber = null, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'K'. recipient = null, callSettingMode, isValidNumber, @@ -201,7 +199,7 @@ export class Call< } async _initCallModule() { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'string | null' is not assignable to type 'st... Remove this comment to see the full error message this._callSettingMode = this._deps.callingSettings.callingMode; if ( this._callSettingMode === callingModes.webphone && @@ -212,7 +210,7 @@ export class Call< } _resetCallModule() { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'string | null' is not assignable to type 'st... Remove this comment to see the full error message this._callSettingMode = this._deps.callingSettings.callingMode; if ( this._callSettingMode === callingModes.webphone && @@ -228,7 +226,7 @@ export class Call< this._deps.callingSettings.callingMode !== oldCallSettingMode && this._deps.webphone ) { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'string | null' is not assignable to type 'st... Remove this comment to see the full error message this._callSettingMode = this._deps.callingSettings.callingMode; if (oldCallSettingMode === callingModes.webphone) { this._deps.webphone.disconnect(); @@ -269,18 +267,18 @@ export class Call< isConference, phoneNumber, recipient, - // @ts-expect-error + // @ts-expect-error TS(2339): Property 'type' does not exist on type 'NonNullabl... Remove this comment to see the full error message contactResourceType: recipient?.type || null, callSettingMode: this._callSettingMode, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'boolean | undefined' is not assignable to ty... Remove this comment to see the full error message isValidNumber, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'boolean | undefined' is not assignable to ty... Remove this comment to see the full error message clickDialerToCall, }); try { let validatedNumbers; if (fromNumber === 'undefined') { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'string'. fromNumber = null; } if (this._deps.appFeatures?.isEDPEnabled) { @@ -299,7 +297,7 @@ export class Call< if (validatedNumbers) { validatedNumbers.toNumber && this.setLastValidatedToNumber(validatedNumbers.toNumber); - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type '{ extendedControls: string[]; to... Remove this comment to see the full error message session = await this._makeCall({ ...validatedNumbers, extendedControls, @@ -506,19 +504,6 @@ export class Call< parsedToNumber = parsedNumbers[toNumberIndex]; parsedFromNumber = parsedNumbers[fromNumberIndex]; } - if (this._internationalCheck) { - if ( - parsedToNumber && - parsedToNumber.isInternational && - !this._deps.extensionFeatures.features?.InternationalCalling?.available - ) { - const error = { - phoneNumber: parsedToNumber.originalString, - type: 'noInternational', - }; - throw error; - } - } const parsedToNumberE164 = parsedToNumber?.parsedNumber; let parsedFromNumberE164 = parsedFromNumber?.parsedNumber; @@ -537,7 +522,7 @@ export class Call< async _makeCall({ toNumber, fromNumber, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'string | null' is not assignable to type 'st... Remove this comment to see the full error message callingMode = this._deps.callingSettings.callingMode, extendedControls = [], }: { @@ -561,8 +546,8 @@ export class Call< }); break; case callingModes.webphone: { - if (this._deps.activeCallControl && this._useCallControlToMakeCall) { - session = await this._deps.activeCallControl.makeCall({ + if (this.isActiveCallControlApplicable) { + session = await this._deps.activeCallControl!.makeCall({ fromNumber, toNumber, homeCountryId, @@ -586,6 +571,10 @@ export class Call< return session; } + get isActiveCallControlApplicable() { + return !!(this._deps.activeCallControl && this._useCallControlToMakeCall); + } + get isIdle() { return this.callStatus === callStatus.idle; } diff --git a/packages/ringcentral-integration/modules/Call/callErrors.ts b/packages/ringcentral-integration/modules/Call/callErrors.ts index 4f8740d2f6..4503ffb383 100644 --- a/packages/ringcentral-integration/modules/Call/callErrors.ts +++ b/packages/ringcentral-integration/modules/Call/callErrors.ts @@ -12,6 +12,7 @@ export const callErrors = ObjectMap.prefixKeys( 'noInternational', 'emergencyNumber', 'numberParseError', + 'fromAndToNumberIsSame', ], 'callErrors', ); diff --git a/packages/ringcentral-integration/modules/CallHistory/CallHistory.ts b/packages/ringcentral-integration/modules/CallHistory/CallHistory.ts index 01892044d7..48670cf2b6 100644 --- a/packages/ringcentral-integration/modules/CallHistory/CallHistory.ts +++ b/packages/ringcentral-integration/modules/CallHistory/CallHistory.ts @@ -1,5 +1,5 @@ -import { findIndex, forEach } from 'ramda'; - +import CallLogFromParty from '@rc-ex/core/lib/definitions/CallLogFromParty'; +import CallLogToParty from '@rc-ex/core/lib/definitions/CallLogToParty'; import { action, computed, @@ -9,12 +9,13 @@ import { track, watch, } from '@ringcentral-integration/core'; -import CallLogFromParty from '@rc-ex/core/lib/definitions/CallLogFromParty'; -import CallLogToParty from '@rc-ex/core/lib/definitions/CallLogToParty'; +import { findIndex, forEach } from 'ramda'; +import { trackEvents } from '../../enums/trackEvents'; import type { Call } from '../../interfaces/Call.interface'; import type { Entity } from '../../interfaces/Entity.interface'; import type { ActiveCall } from '../../interfaces/Presence.model'; +import type { PartialRequired } from '../../interfaces/utilities'; import { getPhoneNumberMatches, sortByStartTime, @@ -23,8 +24,8 @@ import debounce from '../../lib/debounce'; import { Module } from '../../lib/di'; import { normalizeNumber } from '../../lib/normalizeNumber'; import { proxify } from '../../lib/proxy/proxify'; -import { trackEvents } from '../../enums/trackEvents'; import { callingModes } from '../CallingSettings'; + import type { Deps, HistoryCall } from './CallHistory.interface'; import { addNumbersFromCall, @@ -69,7 +70,6 @@ export class CallHistory extends RcModuleV2 { }); } this._deps.activityMatcher?.addQuerySource({ - // @ts-expect-error getQueriesFn: () => this.sessionIds, readyCheckFn: () => (!this._deps.callMonitor || this._deps.callMonitor.ready) && @@ -103,7 +103,7 @@ export class CallHistory extends RcModuleV2 { forEach((call) => { const callWithDuration = { ...call, - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. duration: Math.floor((timestamp - call.startTime) / 1000), }; const idx = findIndex( @@ -129,7 +129,7 @@ export class CallHistory extends RcModuleV2 { telephonySessionId === call.telephonySessionId, ) || // clean current overdue ended call (default clean time: 1day). - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. Date.now() - call.startTime > DEFAULT_CLEAN_TIME ), ); @@ -167,10 +167,10 @@ export class CallHistory extends RcModuleV2 { if ( this.ready && (!this._deps.tabManager || this._deps.tabManager.active) && - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. this._deps.contactMatcher.ready ) { - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. this._deps.contactMatcher.triggerMatch(); } }, @@ -185,10 +185,10 @@ export class CallHistory extends RcModuleV2 { if ( this.ready && (!this._deps.tabManager || this._deps.tabManager.active) && - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. this._deps.activityMatcher.ready ) { - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. this._deps.activityMatcher.triggerMatch(); } }, @@ -232,12 +232,12 @@ export class CallHistory extends RcModuleV2 { ([currentCalls = [], ready]) => { if (!ready) return; const ids: Record = {}; - // @ts-expect-error - currentCalls.forEach((call) => { + // @ts-expect-error TS(2339): Property 'forEach' does not exist on type 'boolean... Remove this comment to see the full error message + currentCalls.forEach((call: any) => { ids[call.telephonySessionId] = true; }); const shouldRemovedCalls = this.endedCalls.filter( - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. (call) => ids[call.telephonySessionId], ); if (shouldRemovedCalls.length) { @@ -344,10 +344,10 @@ export class CallHistory extends RcModuleV2 { : pickPhoneOrExtensionNumber; const fromNumber = - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message call.from && pickNumber(call.from.phoneNumber, call.from.extensionNumber); const toNumber = - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message call.to && pickNumber(call.to.phoneNumber, call.to.extensionNumber); const fromMatches = (fromNumber && contactMapping[fromNumber]) || []; @@ -371,16 +371,16 @@ export class CallHistory extends RcModuleV2 { const callMatched = this._deps.callMonitor?.callMatched ?? {}; const telephonySessionIds: Record = {}; const calls = this.normalizedCalls.map((call) => { - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. telephonySessionIds[call.telephonySessionId] = true; - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. const fromName = call.from.name || call.from.phoneNumber; const toName = call.to.name || call.to.phoneNumber; - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'ActiveCall' is not assignable to... Remove this comment to see the full error message const { fromMatches, toMatches } = this.findMatches(contactMapping, call); - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. const activityMatches = activityMapping[call.sessionId] || []; - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. const matched = callMatched[call.sessionId]; return { ...call, @@ -393,7 +393,7 @@ export class CallHistory extends RcModuleV2 { }; }); const filteredEndedCalls = this.endedCalls - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. .filter((call) => !telephonySessionIds[call.telephonySessionId]) .map((call) => { const activityMatches = activityMapping[call.sessionId] || []; @@ -428,9 +428,9 @@ export class CallHistory extends RcModuleV2 { const effectSearchStr = searchInput.toLowerCase().trim(); const data = calls .filter((call) => { - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'HistoryCall' is not assignable t... Remove this comment to see the full error message const { phoneNumber, matches } = getPhoneNumberMatches(call); - // @ts-expect-error + // @ts-expect-error TS(2533): Object is possibly 'null' or 'undefined'. const matchesMatched = matches.some((entities) => { if (!entities || !entities.id) return false; if ( @@ -465,7 +465,7 @@ export class CallHistory extends RcModuleV2 { const newCalls = this.filterCalls.map((call) => ({ ...call, activityMatches: - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. this._deps.activityMatcher?.dataMapping[call.sessionId] || [], })); return newCalls; @@ -478,7 +478,7 @@ export class CallHistory extends RcModuleV2 { const output: string[] = []; const numberMap: Record = {}; this.normalizedCalls.forEach( - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type '(call: Call) => void' is not ass... Remove this comment to see the full error message addNumbersFromCall(output, numberMap, this.enableFullPhoneNumberMatch), ); this.endedCalls.forEach( @@ -490,9 +490,10 @@ export class CallHistory extends RcModuleV2 { @computed((that: CallHistory) => [that._deps.callLog.calls, that.endedCalls]) get sessionIds() { const sessionIds: Record = {}; - return this._deps.callLog.calls + return ( + this._deps.callLog.calls as PartialRequired[] + ) .map((call) => { - // @ts-expect-error sessionIds[call.sessionId] = true; return call.sessionId; }) diff --git a/packages/ringcentral-integration/modules/CallHistory/callHistoryHelper.ts b/packages/ringcentral-integration/modules/CallHistory/callHistoryHelper.ts index 2de2783fbd..2d2fc3e4df 100644 --- a/packages/ringcentral-integration/modules/CallHistory/callHistoryHelper.ts +++ b/packages/ringcentral-integration/modules/CallHistory/callHistoryHelper.ts @@ -23,7 +23,7 @@ export const addIfNotExist = ( // return phone number only. const pickPhoneNumber: PhoneNumberPicker = (phoneNumber: string) => phoneNumber; -// @ts-expect-error +// @ts-expect-error TS(2322): Type '(phoneNumber: string, extension: string) => ... Remove this comment to see the full error message export const pickPhoneOrExtensionNumber: PhoneNumberPicker = ( phoneNumber: string, extension: string, @@ -31,7 +31,7 @@ export const pickPhoneOrExtensionNumber: PhoneNumberPicker = ( const formatExt = (num: string) => `${RC_EXTENSION_DELIMITER}${num}`; -// @ts-expect-error +// @ts-expect-error TS(2322): Type '(phoneNumber: string, extensionNumber: strin... Remove this comment to see the full error message export const pickFullPhoneNumber: PhoneNumberPicker = ( phoneNumber: string, extensionNumber: string, diff --git a/packages/ringcentral-integration/modules/CallLog/CallLog.interface.ts b/packages/ringcentral-integration/modules/CallLog/CallLog.interface.ts index 1f1beac708..3f3a1184a3 100644 --- a/packages/ringcentral-integration/modules/CallLog/CallLog.interface.ts +++ b/packages/ringcentral-integration/modules/CallLog/CallLog.interface.ts @@ -1,6 +1,6 @@ -import type CallLogSyncResponse from '@rc-ex/core/lib/definitions/CallLogSyncResponse'; import type UserCallLogRecord from '@rc-ex/core/lib/definitions/CallLogRecord'; import type UserCallLogResponse from '@rc-ex/core/lib/definitions/CallLogResponse'; +import type CallLogSyncResponse from '@rc-ex/core/lib/definitions/CallLogSyncResponse'; import type { AppFeatures } from '../AppFeatures'; import type { Auth } from '../Auth'; diff --git a/packages/ringcentral-integration/modules/CallLog/CallLog.ts b/packages/ringcentral-integration/modules/CallLog/CallLog.ts index db8ebfd488..ffbd11e9d9 100644 --- a/packages/ringcentral-integration/modules/CallLog/CallLog.ts +++ b/packages/ringcentral-integration/modules/CallLog/CallLog.ts @@ -14,6 +14,7 @@ import { callResults } from '../../enums/callResults'; import { subscriptionFilters } from '../../enums/subscriptionFilters'; import type { SyncType } from '../../enums/syncTypes'; import { syncTypes } from '../../enums/syncTypes'; +import { ActiveCall } from '../../interfaces/Presence.model'; import { hasEndedCalls, isOutbound, @@ -25,6 +26,7 @@ import { Module } from '../../lib/di'; import fetchList from '../../lib/fetchList'; import getDateFrom from '../../lib/getDateFrom'; import { proxify } from '../../lib/proxy/proxify'; + import type { CallLogData, CallLogRecords, @@ -39,7 +41,6 @@ import { processData, processRecords, } from './helper'; -import { ActiveCall } from '../../interfaces/Presence.model'; const DEFAULT_TTL = 5 * 60 * 1000; // Lock fetching on app refresh if lst fetch happened less than this time span @@ -148,20 +149,20 @@ export class CallLog extends RcModuleV2 { }); processRecords(records, supplementRecords).forEach((call) => { if (call.startTime > cutOffTime) { - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. if (!this.data.map[call.id]) { - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message newState.push(call.id); } - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. this.data.map[call.id] = call; if (this._enableDeleted && call.deleted) { - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message const index = newState.indexOf(call.id); if (index > -1) { newState.splice(index, 1); } - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. delete this.data.map[call.id]; } } @@ -261,7 +262,7 @@ export class CallLog extends RcModuleV2 { if ( this.ready && this._deps.subscription.ready && - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message presenceRegExp.test(message.event) && message.body && message.body.activeCalls && @@ -313,7 +314,7 @@ export class CallLog extends RcModuleV2 { const calls = removeInboundRingOutLegs( removeDuplicateIntermediateCalls( // https://developers.ringcentral.com/api-reference/Call-Log/readUserCallLog - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'CallLogRecord[]' is not assignab... Remove this comment to see the full error message this.list.filter( (call) => // [RCINT-3472] calls with result === 'stopped' seems to be useless @@ -325,7 +326,7 @@ export class CallLog extends RcModuleV2 { call.result !== callResults.unknown && // Outgoing fax sending has failed // TODO: Types of Legacy, remove for checking type? - // @ts-ignore + // @ts-expect-error TS(2367): This condition will always return 'true' since the... Remove this comment to see the full error message call.result !== callResults.faxSendError && // Incoming fax has failed to be received call.result !== callResults.faxReceiptError && @@ -333,7 +334,7 @@ export class CallLog extends RcModuleV2 { call.result !== callResults.callFailed && // Error Internal error occurred when receiving fax // TODO: Types of Legacy, remove for checking type? - // @ts-ignore + // @ts-expect-error TS(2367): This condition will always return 'true' since the... Remove this comment to see the full error message call.result !== callResults.faxReceipt, ), ) as ActiveCall[], @@ -446,7 +447,7 @@ export class CallLog extends RcModuleV2 { showDeleted: this._enableDeleted, }); if (ownerId !== this._deps.auth.ownerId) throw Error('request aborted'); - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type '{ daySpan: number; records: Call... Remove this comment to see the full error message this.syncSuccess({ ...processData(data), daySpan: this._daySpan, @@ -481,7 +482,7 @@ export class CallLog extends RcModuleV2 { // reach the max record count supplementRecords = await this._fetch({ dateFrom, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'string | 0 | undefined' is not assignable to... Remove this comment to see the full error message dateTo: getISODateTo(records), }); } @@ -491,10 +492,10 @@ export class CallLog extends RcModuleV2 { } this.syncSuccess({ records, - // @ts-expect-error + // @ts-expect-error TS(2454): Variable 'supplementRecords' is used before being ... Remove this comment to see the full error message supplementRecords, timestamp, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'string | undefined' is not assignable to typ... Remove this comment to see the full error message syncToken, daySpan: this._daySpan, }); diff --git a/packages/ringcentral-integration/modules/CallLog/helper.ts b/packages/ringcentral-integration/modules/CallLog/helper.ts index f3a4633740..7ad5703038 100644 --- a/packages/ringcentral-integration/modules/CallLog/helper.ts +++ b/packages/ringcentral-integration/modules/CallLog/helper.ts @@ -1,6 +1,7 @@ import { callActions } from '../../enums/callActions'; import { normalizeStartTime } from '../../lib/callLogHelpers'; import getDateFrom from '../../lib/getDateFrom'; + import type { CallLogList, CallLogRecord, diff --git a/packages/ringcentral-integration/modules/CallLogger/CallLogger.ts b/packages/ringcentral-integration/modules/CallLogger/CallLogger.ts index af50bbc3c1..7e34d3ec8e 100644 --- a/packages/ringcentral-integration/modules/CallLogger/CallLogger.ts +++ b/packages/ringcentral-integration/modules/CallLogger/CallLogger.ts @@ -1,5 +1,3 @@ -import { reduce } from 'ramda'; - import { action, computed, @@ -7,21 +5,23 @@ import { storage, watch, } from '@ringcentral-integration/core'; +import { reduce } from 'ramda'; import type { CallLoggerTriggerType } from '../../enums/callLoggerTriggerTypes'; import { callLoggerTriggerTypes } from '../../enums/callLoggerTriggerTypes'; import type { Call } from '../../interfaces/Call.interface'; import type { ActiveCall } from '../../interfaces/Presence.model'; +import { LoggerBase } from '../../lib/LoggerBase'; import { isInbound, isRinging, removeDuplicateSelfCalls, } from '../../lib/callLogHelpers'; import { Module } from '../../lib/di'; -import { LoggerBase } from '../../lib/LoggerBase'; import proxify from '../../lib/proxy/proxify'; import type { HistoryCall } from '../CallHistory'; import type { CallLogRecord } from '../CallLog'; + import type { AutoLogCallOptions, Deps, @@ -51,6 +51,7 @@ const DEFAULT_OPACITY = 20; export class CallLogger extends LoggerBase { protected _customMatcherHooks: Hook[] = []; + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message protected _identityFunction = callIdentityFunction; _logFunction = this._deps.callLoggerOptions.logFunction; @@ -101,6 +102,7 @@ export class CallLogger extends LoggerBase { } @proxify + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message async log({ call, ...options }: LogOptions) { return super.log({ item: call, ...options }); } @@ -122,13 +124,15 @@ export class CallLogger extends LoggerBase { const inbound = isInbound(call); const fromEntity = (inbound && contact) || null; const toEntity = (!inbound && contact) || null; + // @ts-expect-error TS(2345): Argument of type 'Omit, "call" |... Remove this comment to see the full error message await this.log({ ...options, call: { ...call, duration: Object.prototype.hasOwnProperty.call(call, 'duration') ? (call as CallLogRecord).duration - : Math.round((Date.now() - call.startTime) / 1000), + : // @ts-expect-error TS(2532): Object is possibly 'undefined'. + Math.round((Date.now() - call.startTime) / 1000), result: (call as CallLogRecord).result || (call as Call).telephonyStatus, }, @@ -150,9 +154,11 @@ export class CallLogger extends LoggerBase { await this.log({ call: { ...call, + // @ts-expect-error TS(2322): Type 'number | undefined' is not assignable to typ... Remove this comment to see the full error message duration: Object.prototype.hasOwnProperty.call(call, 'duration') ? (call as CallLogRecord).duration - : Math.round((Date.now() - call.startTime) / 1000), + : // @ts-expect-error TS(2532): Object is possibly 'undefined'. + Math.round((Date.now() - call.startTime) / 1000), result: (call as CallLogRecord).result || (call as Call).telephonyStatus, }, @@ -164,7 +170,9 @@ export class CallLogger extends LoggerBase { _activityMatcherCheck(sessionId: string) { return ( + // @ts-expect-error TS(2532): Object is possibly 'undefined'. !this._deps.activityMatcher.dataMapping[sessionId] || + // @ts-expect-error TS(2532): Object is possibly 'undefined'. !this._deps.activityMatcher.dataMapping[sessionId].length ); } @@ -184,12 +192,14 @@ export class CallLogger extends LoggerBase { async _onNewCall(call: Call, triggerType: CallLoggerTriggerType) { if (await this._shouldLogNewCall(call)) { // RCINT-3857 check activity in case instance was reloaded when call is still active + // @ts-expect-error TS(2532): Object is possibly 'undefined'. await this._deps.activityMatcher.triggerMatch(); if ( this._activityMatcherCheck(call.sessionId) && this._customMatcherCheck(call.sessionId) ) { // is completely new, need entity information + // @ts-expect-error TS(2532): Object is possibly 'undefined'. await this._deps.contactMatcher.triggerMatch(); const toNumberEntity = call.toNumberEntity || ''; @@ -197,12 +207,14 @@ export class CallLogger extends LoggerBase { const fromMatches = (call.from && call.from.phoneNumber && + // @ts-expect-error TS(2532): Object is possibly 'undefined'. this._deps.contactMatcher.dataMapping[call.from.phoneNumber]) || []; const toMatches = (call.to && call.to.phoneNumber && + // @ts-expect-error TS(2532): Object is possibly 'undefined'. this._deps.contactMatcher.dataMapping[call.to.phoneNumber]) || []; @@ -219,7 +231,9 @@ export class CallLogger extends LoggerBase { await this._autoLogCall({ call, + // @ts-expect-error TS(2322): Type 'Entity | null' is not assignable to type 'En... Remove this comment to see the full error message fromEntity, + // @ts-expect-error TS(2322): Type 'Entity | null | undefined' is not assignable... Remove this comment to see the full error message toEntity, triggerType, }); @@ -236,10 +250,13 @@ export class CallLogger extends LoggerBase { @proxify async _shouldLogUpdatedCall(call: HistoryCall | ActiveCall) { const isActive = await this._ensureActive(); + // @ts-expect-error TS(2345): Argument of type 'ActiveCall | HistoryCall' is not... Remove this comment to see the full error message if (isActive && (this.logOnRinging || !isRinging(call))) { if (this.autoLog) return true; + // @ts-expect-error TS(2532): Object is possibly 'undefined'. await this._deps.activityMatcher.triggerMatch(); const activityMatches = + // @ts-expect-error TS(2532): Object is possibly 'undefined'. this._deps.activityMatcher.dataMapping[call.sessionId] || []; return activityMatches.length > 0; } @@ -266,11 +283,13 @@ export class CallLogger extends LoggerBase { (newCalls, oldCalls) => { if (this.ready) { oldCalls = oldCalls?.slice() || []; + // @ts-expect-error TS(2345): Argument of type 'Call[]' is not assignable to par... Remove this comment to see the full error message removeDuplicateSelfCalls(newCalls).forEach((call) => { const oldCallIndex = oldCalls.findIndex( (item) => item.sessionId === call.sessionId, ); if (oldCallIndex === -1) { + // @ts-expect-error TS(2345): Argument of type 'ActiveCall' is not assignable to... Remove this comment to see the full error message this._onNewCall(call, callLoggerTriggerTypes.presenceUpdate); } else { const oldCall = oldCalls[oldCallIndex]; @@ -280,11 +299,14 @@ export class CallLogger extends LoggerBase { { ...call, isTransferredCall: + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. !!this.transferredCallsMap[call.sessionId], transferredMiddleNumber: this.transferredCallsMap[ + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. call.sessionId ] - ? this.transferredCallsMap[call.sessionId] + ? // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. + this.transferredCallsMap[call.sessionId] .transferredMiddleNumber : null, }, @@ -302,6 +324,7 @@ export class CallLogger extends LoggerBase { (oldCall.from && oldCall.from.phoneNumber) ) { this._addTransferredCall( + // @ts-expect-error TS(2345): Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message call.sessionId, oldCall.from?.phoneNumber, ); @@ -309,6 +332,7 @@ export class CallLogger extends LoggerBase { { ...call, isTransferredCall: true, + // @ts-expect-error TS(2322): Type 'string | undefined' is not assignable to typ... Remove this comment to see the full error message transferredMiddleNumber: oldCall.from && oldCall.from.phoneNumber, phoneNumberUpdated: true, @@ -323,6 +347,7 @@ export class CallLogger extends LoggerBase { { ...call, isTransferredCall: !!this.transferredCallsMap[call.sessionId], + // @ts-expect-error TS(2322): Type 'string | null' is not assignable to type 'st... Remove this comment to see the full error message transferredMiddleNumber: this.transferredCallsMap[ call.sessionId ] @@ -358,7 +383,9 @@ export class CallLogger extends LoggerBase { { ...callInfo, isTransferredCall: + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. !!this.transferredCallsMap[callInfo.sessionId], + // @ts-expect-error TS(2322): Type 'string | null' is not assignable to type 'st... Remove this comment to see the full error message transferredMiddleNumber: this.transferredCallsMap[ call.sessionId ] diff --git a/packages/ringcentral-integration/modules/CallLogger/callLoggerHelper.ts b/packages/ringcentral-integration/modules/CallLogger/callLoggerHelper.ts index a5d2a5a188..3c1c2ba2af 100644 --- a/packages/ringcentral-integration/modules/CallLogger/callLoggerHelper.ts +++ b/packages/ringcentral-integration/modules/CallLogger/callLoggerHelper.ts @@ -14,9 +14,13 @@ export function hasRecording(call: HistoryCall) { // W6t1Xt8UVDFNQA&recordingId=1455472006&from=+18085820904&to=+18707762775&date=2021-10-09T14:35:32.748Z export function getRecordingInfo(call: HistoryCall) { return hasRecording(call) - ? `${call.id}&recordingId=${call.recording.id}&from=${encodeURIComponent( + ? // @ts-expect-error TS(2532): Object is possibly 'undefined'. + `${call.id}&recordingId=${call.recording.id}&from=${encodeURIComponent( + // @ts-expect-error TS(2532): Object is possibly 'undefined'. call.from.phoneNumber, + // @ts-expect-error TS(2532): Object is possibly 'undefined'. )}&to=${encodeURIComponent(call.to.phoneNumber)}&date=${new Date( + // @ts-expect-error TS(2769): No overload matches this call. call.startTime, ).toISOString()}` : ''; diff --git a/packages/ringcentral-integration/modules/CallMonitor/CallMonitor.interface.ts b/packages/ringcentral-integration/modules/CallMonitor/CallMonitor.interface.ts index 78e25f7d51..f97fbe3531 100644 --- a/packages/ringcentral-integration/modules/CallMonitor/CallMonitor.interface.ts +++ b/packages/ringcentral-integration/modules/CallMonitor/CallMonitor.interface.ts @@ -1,4 +1,3 @@ -import type { ExtensionInfo } from '../ExtensionInfo'; import type { Call as ICall } from '../../interfaces/Call.interface'; import type { AccountInfo } from '../AccountInfo'; import type { ActiveCallControl } from '../ActiveCallControl'; @@ -6,6 +5,7 @@ import type { ActivityMatcher } from '../ActivityMatcher'; import type { Call } from '../Call'; import type { ConferenceCall } from '../ConferenceCall'; import type { ContactMatcher } from '../ContactMatcher'; +import type { ExtensionInfo } from '../ExtensionInfo'; import type { Presence } from '../Presence'; import type { Storage } from '../Storage'; import type { TabManager } from '../TabManager'; diff --git a/packages/ringcentral-integration/modules/CallMonitor/CallMonitor.ts b/packages/ringcentral-integration/modules/CallMonitor/CallMonitor.ts index 8e833fac2e..2148faaed6 100644 --- a/packages/ringcentral-integration/modules/CallMonitor/CallMonitor.ts +++ b/packages/ringcentral-integration/modules/CallMonitor/CallMonitor.ts @@ -1,3 +1,12 @@ +import { + action, + computed, + RcModuleV2, + state, + storage, + track, + watch, +} from '@ringcentral-integration/core'; import { EventEmitter } from 'events'; import { difference, @@ -10,16 +19,7 @@ import { sort, } from 'ramda'; -import { - action, - computed, - RcModuleV2, - state, - storage, - track, - watch, -} from '@ringcentral-integration/core'; - +import { trackEvents } from '../../enums/trackEvents'; import type { Call, NormalizedCall, @@ -45,7 +45,6 @@ import { mapTelephonyStatus, isFaxSession, } from '../ActiveCallControl'; -import { trackEvents } from '../../enums/trackEvents'; import type { ToNumberMatched } from '../Call'; import { isConferenceSession, @@ -53,11 +52,12 @@ import { isRing, sortByLastActiveTimeDesc, } from '../Webphone/webphoneHelper'; -import { callEvents } from './callEvents'; + import type { CallEventCallback, Deps } from './CallMonitor.interface'; +import { callEvents } from './callEvents'; import { isCurrentDeviceEndCall, - matchWephoneSessionWithAcitveCall, + matchWebphoneSessionWithActiveCall, } from './callMonitorHelper'; @Module({ @@ -83,7 +83,7 @@ export class CallMonitor extends RcModuleV2 { protected _useTelephonySession = this._deps.callMonitorOptions?.useTelephonySession ?? false; - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'NormalizedC... Remove this comment to see the full error message protected _normalizedCalls: NormalizedCalls = null; private _enableContactMatchWhenNewCall: boolean = this._deps.callMonitorOptions?.enableContactMatchWhenNewCall ?? true; @@ -232,7 +232,7 @@ export class CallMonitor extends RcModuleV2 { ); if (oldCallIndex === -1) { this._eventEmitter.emit(callEvents.newCall, call); - // loop to execut the onRinging handlers + // loop to execute the onRinging handlers if (isRinging(call)) { this._eventEmitter.emit(callEvents.callRinging, call); } @@ -258,7 +258,7 @@ export class CallMonitor extends RcModuleV2 { const index = entities.indexOf(entity); const toEntity = entity && - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'Entity[] | undefined' is not ass... Remove this comment to see the full error message find((toMatch) => toMatch.id === entity.entityId, call.toMatches); if (toEntity !== undefined) { this._removeMatched(index, entities); @@ -276,9 +276,7 @@ export class CallMonitor extends RcModuleV2 { } _removeMatched(index: number, entities: ToNumberMatched[]) { - console.log('removeMatched:', index); entities.splice(index, 1); - console.log('entities after splice:', entities); return entities; } @@ -307,7 +305,7 @@ export class CallMonitor extends RcModuleV2 { mergeControlClickHangupTrack() {} @track((that: CallMonitor) => [ - // @ts-expect-error + // @ts-expect-error TS(2341): Property 'state' is private and only accessible wi... Remove this comment to see the full error message Object.values(that._deps.conferenceCall?.state.mergingPair ?? {}).length ? trackEvents.clickMergeCallControl : trackEvents.clickMergeMergeCallControl, @@ -367,8 +365,9 @@ export class CallMonitor extends RcModuleV2 { } @computed((that: CallMonitor) => [ - that.normalizedCallsFromPresence, - that.normalizedCallsFromTelephonySessions, + // Use "null" to avoid triggering get property unnecessarily that may cause issues + that.useTelephonySession ? null : that.normalizedCallsFromPresence, + that.useTelephonySession ? that.normalizedCallsFromTelephonySessions : null, that.useTelephonySession, ]) get normalizedCalls() { @@ -413,26 +412,26 @@ export class CallMonitor extends RcModuleV2 { // mapping and sort let theSessions = this._deps.webphone?.sessions ?? []; - // @ts-expect-error + // @ts-expect-error TS(2322): Type '({ from: { phoneNumber: string; }; to: { pho... Remove this comment to see the full error message this._normalizedCalls = sort( - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'NormalizedSession | undefined' i... Remove this comment to see the full error message (l, r) => sortByLastActiveTimeDesc(l.webphoneSession, r.webphoneSession), - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type '({ from: { phoneNumber: string; ... Remove this comment to see the full error message map((callItem) => { // use account countryCode to normalize number due to API issues [RCINT-3419] const fromNumber = normalizeNumber({ - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'string | undefined' is not assignable to typ... Remove this comment to see the full error message phoneNumber: callItem.from && callItem.from.phoneNumber, countryCode: this._deps.accountInfo.countryCode, maxExtensionLength: this._deps.accountInfo.maxExtensionNumberLength, }); const toNumber = normalizeNumber({ - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'string | undefined' is not assignable to typ... Remove this comment to see the full error message phoneNumber: callItem.to && callItem.to.phoneNumber, countryCode: this._deps.accountInfo.countryCode, maxExtensionLength: this._deps.accountInfo.maxExtensionNumberLength, }); - const webphoneSession = matchWephoneSessionWithAcitveCall( + const webphoneSession = matchWebphoneSessionWithActiveCall( theSessions, callItem, ); @@ -463,16 +462,29 @@ export class CallMonitor extends RcModuleV2 { that._deps.presence.calls, ]) get normalizedCallsFromTelephonySessions() { - // TODO: match cached calls when there are conference merging calls, refer to `normalizedCallsFromPresence` function - if (!this._deps.activeCallControl?.sessions) return []; - const combinedCalls = [...this._deps.activeCallControl?.sessions]; // clone + if (!this._deps.activeCallControl?.sessions) { + return []; + } + + // Match cached calls at the very beginning + let cachedCalls: NormalizedCalls = []; + if (this._normalizedCalls && this._deps.webphone?.cachedSessions?.length) { + cachedCalls = this._normalizedCalls.filter((x) => + this._deps.webphone?.cachedSessions.some( + (i) => i.partyData?.sessionId === x.telephonySessionId, + ), + ); + } + + const combinedCalls = [...this._deps.activeCallControl!.sessions]; // clone const { currentDeviceCallsMap, transferCallMapping } = this._deps.activeCallControl; + // mapping and sort - // @ts-expect-error + // @ts-ignore this._normalizedCalls = sort( - // @ts-expect-error - (l, r) => sortByLastActiveTimeDesc(l.webphoneSession, r.webphoneSession), + (l, r) => + sortByLastActiveTimeDesc(l!.webphoneSession, r!.webphoneSession), map((callItem) => { // sessionId arrives when telephony session event push and it's a required // reference https://github.com/ringcentral/ringcentral-call-js/blob/master/src/Session.ts @@ -502,7 +514,7 @@ export class CallMonitor extends RcModuleV2 { const presenceCall = this._deps.presence.calls.find( (presenceCall) => presenceCall.telephonySessionId === callItem.id, ); - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'string | undefined' is not assignable to typ... Remove this comment to see the full error message id = presenceCall?.id; } const fromNumber = normalizeNumber({ @@ -518,7 +530,7 @@ export class CallMonitor extends RcModuleV2 { const toName = to?.name; const fromName = from?.name; const partyId = party?.id; - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'PartyStatusCode | undefined' is ... Remove this comment to see the full error message const telephonyStatus = mapTelephonyStatus(party?.status?.code); // TODO: add sipData here @@ -548,6 +560,16 @@ export class CallMonitor extends RcModuleV2 { }; }, combinedCalls).filter((x) => !!x), ); + + // Keep the cached calls in the list + if (this._normalizedCalls) { + cachedCalls.forEach((cachedCall) => { + if (!this._normalizedCalls!.find((x) => x.id === cachedCall.id)) { + this._normalizedCalls!.push(cachedCall); + } + }); + } + return this._normalizedCalls; } @@ -557,9 +579,8 @@ export class CallMonitor extends RcModuleV2 { ]) get calls() { return filter((callItem) => { - // filtering out the conferece during merging + // filtering out the conference during merging if (this._deps.conferenceCall?.isMerging) { - // @ts-expect-error return !isConferenceSession(callItem.webphoneSession); } return true; @@ -568,13 +589,13 @@ export class CallMonitor extends RcModuleV2 { @computed((that: CallMonitor) => [that.calls, that.useTelephonySession]) get activeRingCalls() { - // @ts-expect-error + // @ts-expect-error TS(2769): No overload matches this call. return filter((callItem) => { if (this.useTelephonySession) { return ( callItem.webphoneSession && callItem.telephonySession && - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type '{ status: string; id: string; di... Remove this comment to see the full error message isProceeding(callItem.telephonySession) ); } @@ -587,17 +608,17 @@ export class CallMonitor extends RcModuleV2 { if (this.useTelephonySession) { return filter( (callItem) => - // @ts-expect-error + // @ts-expect-error TS(2769): No overload matches this call. callItem.webphoneSession && callItem.telephonySession && - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type '{ status: string; id: string; di... Remove this comment to see the full error message isHolding(callItem.telephonySession), this.calls, ); } return filter( (callItem) => - // @ts-expect-error + // @ts-expect-error TS(2769): No overload matches this call. callItem.webphoneSession && isOnHold(callItem.webphoneSession), this.calls, ); @@ -605,15 +626,15 @@ export class CallMonitor extends RcModuleV2 { @computed((that: CallMonitor) => [that.calls, that.useTelephonySession]) get _activeCurrentCalls() { - // @ts-expect-error + // @ts-expect-error TS(2769): No overload matches this call. return filter((callItem) => { if (this.useTelephonySession) { return ( callItem.webphoneSession && callItem.telephonySession && - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type '{ status: string; id: string; di... Remove this comment to see the full error message !isProceeding(callItem.telephonySession) && - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type '{ status: string; id: string; di... Remove this comment to see the full error message !isHolding(callItem.telephonySession) ); } @@ -669,13 +690,13 @@ export class CallMonitor extends RcModuleV2 { }; } // TODO: refactor - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'boolean | N... Remove this comment to see the full error message let endCall: boolean | NormalizedSession = null; if (this.useTelephonySession) { endCall = isCurrentDeviceEndCall(sessionsCache as string[], callItem); } else { - // @ts-expect-error - endCall = matchWephoneSessionWithAcitveCall( + // @ts-expect-error TS(2322): Type 'NormalizedSession | undefined' is not assign... Remove this comment to see the full error message + endCall = matchWebphoneSessionWithActiveCall( sessionsCache as NormalizedSession[], callItem, ); diff --git a/packages/ringcentral-integration/modules/CallMonitor/callEvents.ts b/packages/ringcentral-integration/modules/CallMonitor/callEvents.ts index 2d8299b365..e53e7ea8af 100644 --- a/packages/ringcentral-integration/modules/CallMonitor/callEvents.ts +++ b/packages/ringcentral-integration/modules/CallMonitor/callEvents.ts @@ -5,4 +5,4 @@ export const callEvents = { callUpdated: 'CallUpdated', } as const; -export type CallEvent = typeof callEvents[keyof typeof callEvents]; +export type CallEvent = (typeof callEvents)[keyof typeof callEvents]; diff --git a/packages/ringcentral-integration/modules/CallMonitor/callMonitorHelper.ts b/packages/ringcentral-integration/modules/CallMonitor/callMonitorHelper.ts index 00632b2f93..a4046c694d 100644 --- a/packages/ringcentral-integration/modules/CallMonitor/callMonitorHelper.ts +++ b/packages/ringcentral-integration/modules/CallMonitor/callMonitorHelper.ts @@ -1,8 +1,8 @@ import callDirections from '../../enums/callDirections'; +import type { ActiveCallControlSessionData } from '../../interfaces/ActiveSession.interface'; import type { Call } from '../../interfaces/Call.interface'; import type { ActiveCall } from '../../interfaces/Presence.model'; import type { NormalizedSession } from '../../interfaces/Webphone.interface'; -import type { ActiveCallControlSessionData } from '../../interfaces/ActiveSession.interface'; function getSessionStartTime(session: NormalizedSession) { let webphoneStartTime; @@ -14,7 +14,7 @@ function getSessionStartTime(session: NormalizedSession) { return webphoneStartTime; } -export function matchWephoneSessionWithAcitveCall( +export function matchWebphoneSessionWithActiveCall( sessions: NormalizedSession[], callItem: ActiveCall | Call, ) { @@ -59,22 +59,17 @@ export function matchWephoneSessionWithAcitveCall( * the `InviteClientContext`'s id will always begin with callItem's id. */ if (callItem.toName && callItem.toName.toLowerCase() === 'conference') { - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message return session.id.indexOf(callItem.id) === 0; } - if ( - // @ts-expect-error - !(callItem as ActiveCall).sipData.remoteUri || - // @ts-expect-error - (callItem as ActiveCall).sipData.remoteUri === '' - ) { + if (!(callItem as ActiveCall).sipData?.remoteUri) { return false; } if ( session.direction === callDirections.inbound && - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. (callItem as ActiveCall).sipData.remoteUri.indexOf(session.from) === -1 ) { return false; @@ -82,7 +77,7 @@ export function matchWephoneSessionWithAcitveCall( if ( session.direction === callDirections.outbound && - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. (callItem as ActiveCall).sipData.remoteUri.indexOf(session.to) === -1 ) { return false; @@ -91,7 +86,7 @@ export function matchWephoneSessionWithAcitveCall( // 16000 is from experience in test. // there is delay bettween active call created and webphone session created // for example, the time delay is decided by when webphone get invite info - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. if (Math.abs(callItem.startTime - getSessionStartTime(session)) > 16000) { return false; } @@ -101,9 +96,9 @@ export function matchWephoneSessionWithAcitveCall( if (matches.length > 1) { // order by the time gap asc matches.sort((x, y) => { - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. const gapX = Math.abs(callItem.startTime - getSessionStartTime(x)); - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. const gapY = Math.abs(callItem.startTime - getSessionStartTime(y)); return gapX === gapY ? 0 : gapX - gapY; }); @@ -113,7 +108,7 @@ export function matchWephoneSessionWithAcitveCall( } export function isCurrentDeviceEndCall(sessions: string[], callItem: Call) { - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message return sessions.indexOf(callItem.telephonySessionId) !== -1; } diff --git a/packages/ringcentral-integration/modules/CallerId/CallerId.ts b/packages/ringcentral-integration/modules/CallerId/CallerId.ts index 8e2f50e4ee..789bf3b842 100644 --- a/packages/ringcentral-integration/modules/CallerId/CallerId.ts +++ b/packages/ringcentral-integration/modules/CallerId/CallerId.ts @@ -1,9 +1,10 @@ -import { find } from 'ramda'; import type ExtensionCallerIdInfo from '@rc-ex/core/lib/definitions/ExtensionCallerIdInfo'; import { computed } from '@ringcentral-integration/core'; +import { find } from 'ramda'; import { Module } from '../../lib/di'; import { DataFetcherV2Consumer, DataSource } from '../DataFetcherV2'; + import type { Deps } from './CallerId.interface'; @Module({ diff --git a/packages/ringcentral-integration/modules/CallingSettings/CallingSettings.interface.ts b/packages/ringcentral-integration/modules/CallingSettings/CallingSettings.interface.ts index 5d083efd2c..cdf81ae553 100644 --- a/packages/ringcentral-integration/modules/CallingSettings/CallingSettings.interface.ts +++ b/packages/ringcentral-integration/modules/CallingSettings/CallingSettings.interface.ts @@ -16,7 +16,7 @@ export interface Deps { alert: Alert; brand: Brand; callerId?: CallerId; - extensionDevice?: ExtensionDevice; + extensionDevice: ExtensionDevice; appFeatures: AppFeatures; extensionInfo: ExtensionInfo; extensionPhoneNumber: ExtensionPhoneNumber; diff --git a/packages/ringcentral-integration/modules/CallingSettings/CallingSettings.ts b/packages/ringcentral-integration/modules/CallingSettings/CallingSettings.ts index 2467f5711b..37bdf740c6 100644 --- a/packages/ringcentral-integration/modules/CallingSettings/CallingSettings.ts +++ b/packages/ringcentral-integration/modules/CallingSettings/CallingSettings.ts @@ -8,10 +8,11 @@ import { import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; + +import type { Deps } from './CallingSettings.interface'; import { callingModes } from './callingModes'; import type { CallingOptionsType } from './callingOptions'; import { callingOptions } from './callingOptions'; -import type { Deps } from './CallingSettings.interface'; import { callingSettingsMessages } from './callingSettingsMessages'; import { deprecatedCallingOptions } from './deprecatedCallingOptions'; import { mapOptionToMode } from './mapOptionToMode'; @@ -270,7 +271,7 @@ class CallingSettings extends RcModuleV2 { } else if (this._hasPhoneNumberChanged()) { this.setDataAction({ callWith: callingOptions.ringout, - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. myLocation: this._myPhoneNumbers[0], timestamp: Date.now(), }); @@ -329,23 +330,18 @@ class CallingSettings extends RcModuleV2 { } _getLocationLabel(phoneNumber: string) { - const { devices } = this._deps.extensionDevice!; + const { devices } = this._deps.extensionDevice; const { flipNumbers } = this._deps.forwardingNumber; const { mainCompanyNumber } = this._deps.extensionPhoneNumber; const { extensionNumber } = this._deps.extensionInfo; - // @ts-expect-error - const mainPhoneNumber = `${mainCompanyNumber.phoneNumber}*${extensionNumber}`; let name = null; if (devices.length) { let registeredWithDevice = false; devices.forEach((device) => { const { phoneLines } = device; - // @ts-expect-error - if (phoneLines.length) { - // @ts-expect-error + if (phoneLines?.length) { registeredWithDevice = !!phoneLines.find((phoneLine) => { - // @ts-expect-error - return phoneLine.phoneInfo.phoneNumber === phoneNumber; + return phoneLine.phoneInfo?.phoneNumber === phoneNumber; }); if (registeredWithDevice) { name = device.name; @@ -363,6 +359,7 @@ class CallingSettings extends RcModuleV2 { } } + const mainPhoneNumber = `${mainCompanyNumber?.phoneNumber}*${extensionNumber}`; if (phoneNumber === mainPhoneNumber) { return 'Main'; } @@ -413,7 +410,6 @@ class CallingSettings extends RcModuleV2 { }, withPrompt: boolean, ) { - // TODO: validate myLocation this.setDataAction({ callWith, myLocation, diff --git a/packages/ringcentral-integration/modules/CallingSettings/callingOptions.ts b/packages/ringcentral-integration/modules/CallingSettings/callingOptions.ts index 1a190f22be..1cde4d0987 100644 --- a/packages/ringcentral-integration/modules/CallingSettings/callingOptions.ts +++ b/packages/ringcentral-integration/modules/CallingSettings/callingOptions.ts @@ -11,4 +11,4 @@ export const callingOptions = ObjectMap.prefixKeys( ); export type CallingOptionsKeys = keyof typeof callingOptions; -export type CallingOptionsType = typeof callingOptions[CallingOptionsKeys]; +export type CallingOptionsType = (typeof callingOptions)[CallingOptionsKeys]; diff --git a/packages/ringcentral-integration/modules/CompanyContacts/CompanyContacts.ts b/packages/ringcentral-integration/modules/CompanyContacts/CompanyContacts.ts index 53142dc2fc..d64b510eea 100644 --- a/packages/ringcentral-integration/modules/CompanyContacts/CompanyContacts.ts +++ b/packages/ringcentral-integration/modules/CompanyContacts/CompanyContacts.ts @@ -1,5 +1,3 @@ -import { filter, find, forEach, map, reduce, reject } from 'ramda'; -import type { Unsubscribe } from 'redux'; import type ContactResource from '@rc-ex/core/lib/definitions/ContactResource'; import { action, @@ -9,6 +7,8 @@ import { watch, } from '@ringcentral-integration/core'; import type { ObjectMapValue } from '@ringcentral-integration/core/lib/ObjectMap'; +import { filter, find, forEach, map, reduce, reject } from 'ramda'; +import type { Unsubscribe } from 'redux'; import { extensionStatusTypes } from '../../enums/extensionStatusTypes'; import { extensionTypes } from '../../enums/extensionTypes'; @@ -17,6 +17,7 @@ import { subscriptionFilters } from '../../enums/subscriptionFilters'; import { Module } from '../../lib/di'; import fetchList from '../../lib/fetchList'; import { DataFetcherV2Consumer, DataSource } from '../DataFetcherV2'; + import type { Deps } from './CompanyContacts.interface'; /** @@ -65,7 +66,7 @@ export class CompanyContacts extends DataFetcherV2Consumer< Deps, ContactResource[] > { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Unsubscribe... Remove this comment to see the full error message protected _stopWatching: Unsubscribe = null; constructor(deps: Deps) { @@ -112,7 +113,7 @@ export class CompanyContacts extends DataFetcherV2Consumer< protected async fetchDataCore() { const data = await fetchList((params) => this.fetchContacts(params)); - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'unknown[]' is not assignable to ... Remove this comment to see the full error message this.setCompanyContactsData(data); } @@ -154,7 +155,7 @@ export class CompanyContacts extends DataFetcherV2Consumer< override onReset() { this._stopWatching?.(); - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Unsubscribe... Remove this comment to see the full error message this._stopWatching = null; } @@ -170,12 +171,12 @@ export class CompanyContacts extends DataFetcherV2Consumer< @storage @state - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'boolean'. _showDisabled: boolean = null; @storage @state - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'boolean'. _showNotActivated: boolean = null; @storage @@ -199,7 +200,7 @@ export class CompanyContacts extends DataFetcherV2Consumer< @storage @state - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type '("Announcem... Remove this comment to see the full error message _selectedTypes: ObjectMapValue[] = null; @action @@ -264,7 +265,7 @@ export class CompanyContacts extends DataFetcherV2Consumer< item.status === extensionStatusTypes.disabled) || (!this.showNotActivated && item.status === extensionStatusTypes.notActivated) || - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. !typeFilter[item.type] ), ); diff --git a/packages/ringcentral-integration/modules/ComposeText/ComposeText.ts b/packages/ringcentral-integration/modules/ComposeText/ComposeText.ts index e786bb2e5f..148f36ab0d 100644 --- a/packages/ringcentral-integration/modules/ComposeText/ComposeText.ts +++ b/packages/ringcentral-integration/modules/ComposeText/ComposeText.ts @@ -15,6 +15,7 @@ import { ATTACHMENT_SIZE_LIMITATION, messageSenderMessages, } from '../MessageSender'; + import type { Deps, ToNumber } from './ComposeText.interface'; /** @@ -79,7 +80,7 @@ export class ComposeText extends RcModuleV2 { @action _setTypingToNumber(number?: string) { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'string | undefined' is not assignable to typ... Remove this comment to see the full error message this.typingToNumber = number; } @@ -217,14 +218,14 @@ export class ComposeText extends RcModuleV2 { _handleRecipient() { const dummy = this.toNumbers.find((toNumber) => !toNumber.entityType); if (dummy) { - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. const recipient = this._deps.contactSearch.searchResult.find( (item: any) => item.id === dummy.id, ); if (recipient) { this.addToNumber(recipient); this._lastContactSearchResult = - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. this._deps.contactSearch.searchResult.slice(); } } @@ -272,6 +273,10 @@ export class ComposeText extends RcModuleV2 { } async _validateIsOnlyPager(phoneNumber: string) { + const validate = this._deps.numberValidate.validate([phoneNumber]); + if (!validate.result) { + return false; + } const [{ isAnExtension }] = (await this._deps.numberValidate.parseNumbers([ phoneNumber, ])) || [{}]; @@ -318,7 +323,7 @@ export class ComposeText extends RcModuleV2 { if (this._deps.routerInteraction?.currentPath === '/composeText') { this.alertMessageSending(); } - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Timeout'. timeoutID = null; }, 10000); @@ -332,7 +337,7 @@ export class ComposeText extends RcModuleV2 { if (timeoutID) { clearTimeout(timeoutID); - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Timeout'. timeoutID = null; } this.dismissMessageSending(); @@ -340,7 +345,7 @@ export class ComposeText extends RcModuleV2 { } catch (err) { if (timeoutID) { clearTimeout(timeoutID); - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Timeout'. timeoutID = null; } throw err; diff --git a/packages/ringcentral-integration/modules/ConferenceCall/ConferenceCall.interface.ts b/packages/ringcentral-integration/modules/ConferenceCall/ConferenceCall.interface.ts index 1e68548b3a..ccf59f988d 100644 --- a/packages/ringcentral-integration/modules/ConferenceCall/ConferenceCall.interface.ts +++ b/packages/ringcentral-integration/modules/ConferenceCall/ConferenceCall.interface.ts @@ -8,8 +8,8 @@ import type { Alert } from '../Alert'; import type { AppFeatures } from '../AppFeatures'; import type { Auth } from '../Auth'; import type { AvailabilityMonitor } from '../AvailabilityMonitor'; -import type { CallingSettings } from '../CallingSettings'; import type { Call } from '../Call'; +import type { CallingSettings } from '../CallingSettings'; import type { ConnectivityMonitor } from '../ConnectivityMonitor'; import type { ContactMatcher } from '../ContactMatcher'; import type { sessionStatus, Webphone } from '../Webphone'; diff --git a/packages/ringcentral-integration/modules/ConferenceCall/ConferenceCall.ts b/packages/ringcentral-integration/modules/ConferenceCall/ConferenceCall.ts index acd9380da0..e2ed3dfb5e 100644 --- a/packages/ringcentral-integration/modules/ConferenceCall/ConferenceCall.ts +++ b/packages/ringcentral-integration/modules/ConferenceCall/ConferenceCall.ts @@ -1,6 +1,3 @@ -import { EventEmitter } from 'events'; -import { filter, find, is, map, values } from 'ramda'; - import { action, computed, @@ -8,20 +5,23 @@ import { state, track, } from '@ringcentral-integration/core'; +import { EventEmitter } from 'events'; +import { filter, find, is, map, values } from 'ramda'; import callDirections from '../../enums/callDirections'; import calleeTypes from '../../enums/calleeTypes'; import { permissionsMessages } from '../../enums/permissionsMessages'; +import { trackEvents } from '../../enums/trackEvents'; import type { NormalizedSession, WebphoneSession, } from '../../interfaces/Webphone.interface'; import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; -import { trackEvents } from '../../enums/trackEvents'; import { callingModes } from '../CallingSettings'; import sessionStatusEnum from '../Webphone/sessionStatus'; import { isConferenceSession, isRecording } from '../Webphone/webphoneHelper'; + import type { Conference, ConferencesState, @@ -61,12 +61,12 @@ import { ], }) export class ConferenceCall extends RcModuleV2 { - private _eventEmitter = new EventEmitter(); + _eventEmitter = new EventEmitter(); private _timers: { [key: string]: number; } = {}; - // @ts-expect-error + // @ts-expect-error TS(2564): Property '_fromSessionId' has no initializer and i... Remove this comment to see the full error message private _fromSessionId: string; private _ttl: number = DEFAULT_TTL; private _timeout: number = @@ -75,7 +75,7 @@ export class ConferenceCall extends RcModuleV2 { this._deps.conferenceCallOptions?.capacity ?? MAXIMUM_CAPACITY; protected _pulling: boolean = this._deps.conferenceCallOptions?.pulling ?? true; - // @ts-expect-error + // @ts-expect-error TS(2564): Property '_lastCallInfo' has no initializer and is... Remove this comment to see the full error message private _lastCallInfo: { calleeType: string; extraNum: number; @@ -96,7 +96,7 @@ export class ConferenceCall extends RcModuleV2 { mergingPair: MergingPair = {}; @state - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'string'. currentConferenceId: string = null; @state @@ -182,10 +182,10 @@ export class ConferenceCall extends RcModuleV2 { if (this.isMerging && !res) { const session = find( (session) => session.id === sessionId, - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. this._deps.webphone.sessions, ); - // @ts-expect-error + res = isConferenceSession(session); } @@ -275,7 +275,6 @@ export class ConferenceCall extends RcModuleV2 { return null; } const { sessionId } = conferenceState; - let { conference } = conferenceState; this.setConferenceCallStatus(conferenceCallStatus.requesting); try { @@ -287,14 +286,14 @@ export class ConferenceCall extends RcModuleV2 { webphoneSession.partyData, ); const newConference = await this.updateConferenceStatus(id); - conference = newConference.conference; + const conference = newConference.conference; if (partyProfile) { const conferenceState = this.conferences[id]; const newParties = ascendSortParties( conferenceState.conference.parties, ); - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'string | undefined' is not assignable to typ... Remove this comment to see the full error message (partyProfile as PartyState).id = newParties[newParties.length - 1].id; this.bringInParty(conference, sessionId, partyProfile as PartyState); } @@ -357,7 +356,7 @@ export class ConferenceCall extends RcModuleV2 { return null; } - if (!(this._deps.callingSettings.callingMode === callingModes.webphone)) { + if (this._deps.callingSettings.callingMode !== callingModes.webphone) { if (!propagate) { this._deps.alert.danger({ message: conferenceCallErrors.modeError, @@ -390,102 +389,66 @@ export class ConferenceCall extends RcModuleV2 { }); return; } + this.setIsMerging(true); - let sipInstances; - let conferenceId = null; - - if (this._deps.webphone) { - /** - * Because the concurrency behaviour of the server, - * we cannot sure the merging process is over when - * the function's procedure has finshed. - */ - sipInstances = map( - (webphoneSession) => - // @ts-expect-error - this._deps.webphone._sessions.get(webphoneSession.id), - webphoneSessions, - ); - /** - * HACK: we need to preserve the merging session in prevent the glitch of - * the call control page. - */ - const sessionIds = map((x) => x.id, webphoneSessions); - this._deps.webphone.setSessionCaching(sessionIds); - - const pSips = map((instance) => { - const p = new Promise((resolve) => { - // @ts-expect-error - instance.on('terminated', () => { - resolve(null); - }); - }); - return p; - }, sipInstances); - - await Promise.all([ - this._mergeToConference(webphoneSessions), - ...pSips, - ]).then( - () => { - this.setIsMerging(false); - this.setMergingPair({}); - const conferenceState = Object.values(this.conferences)[0]; - - this._eventEmitter.emit(mergeEvents.mergeSucceeded, conferenceState); - }, - (e) => { - console.error(e); - const conferenceState = Object.values(this.conferences)[0]; - - /** - * if create conference successfully but failed to bring-in, - * then terminate the conference. - */ - if (conferenceState && conferenceState.profiles.length < 1) { - this.terminateConference(conferenceState.conference.id); - } - this._deps.alert.warning({ - message: conferenceCallErrors.bringInFailed, - }); - this.setIsMerging(false); - }, - ); - this._deps.webphone.clearSessionCaching(); - } else { - try { - conferenceId = await this._mergeToConference(webphoneSessions); + /** + * Because the concurrency behavior of the server, + * we cannot sure the merging process is over when + * the function's procedure has finished. + */ + const sipInstances = map( + (webphoneSession) => + this._deps.webphone?._sessions.get(webphoneSession.id), + webphoneSessions, + ).filter((x) => !!x); + + /** + * HACK: we need to preserve the merging session in prevent the glitch of + * the call control page. + */ + const sessionIds = map((x) => x.id, webphoneSessions); + this._deps.webphone?.setSessionCaching(sessionIds); + + const pSips = map((instance) => { + const p = new Promise((resolve) => { + instance!.on('terminated', () => { + resolve(null); + }); + }); + return p; + }, sipInstances); + + await Promise.all([ + this._mergeToConference(webphoneSessions), + ...pSips, + ]).then( + () => { this.setIsMerging(false); this.setMergingPair({}); - this._eventEmitter.emit(mergeEvents.mergeSucceeded); - } catch (e: any /** TODO: confirm with instanceof */) { const conferenceState = Object.values(this.conferences)[0]; + + this._eventEmitter.emit(mergeEvents.mergeSucceeded, conferenceState); + }, + (e) => { + console.error(e); + const conferenceState = Object.values(this.conferences)[0]; + /** * if create conference successfully but failed to bring-in, * then terminate the conference. */ - if ( - conferenceState && - conferenceState?.conference?.parties?.length < 1 - ) { + if (conferenceState && conferenceState.profiles.length < 1) { this.terminateConference(conferenceState.conference.id); } - - if ( - !this._deps.availabilityMonitor || - !(await this._deps.availabilityMonitor.checkIfHAError(e)) - ) { - this._deps.alert.warning({ - message: conferenceCallErrors.bringInFailed, - }); - } - } - - if (!sipInstances || conferenceId === null) { + this._deps.alert.warning({ + message: conferenceCallErrors.bringInFailed, + }); this.setIsMerging(false); - } - } + }, + ); + + this._deps.webphone?.clearSessionCaching(); } @proxify @@ -518,7 +481,7 @@ export class ConferenceCall extends RcModuleV2 { const conferenceData = this.conferences[id]; if (!conferenceData) { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type '(Party & Pa... Remove this comment to see the full error message return null; } @@ -528,13 +491,13 @@ export class ConferenceCall extends RcModuleV2 { party?.status?.code.toLowerCase() !== partyStatusCode.disconnected ) { // 0 position is the host - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'number' is not assignable to type 'never'. accum.push({ idx, party }); } return accum; }, []) .map(({ idx, party }) => ({ - // @ts-expect-error + // @ts-expect-error TS(2698): Spread types may only be created from object types... Remove this comment to see the full error message ...party, ...conferenceData.profiles[idx], })) @@ -554,12 +517,12 @@ export class ConferenceCall extends RcModuleV2 { countOnlineParties(id: string) { const res = this.getOnlineParties(id); - // @ts-expect-error + // @ts-expect-error TS(2531): Object is possibly 'null'. return is(Array, res) ? res.length : null; } isOverload(id: string) { - // @ts-expect-error + // @ts-expect-error TS(2531): Object is possibly 'null'. return this.countOnlineParties(id) >= this._capacity; } @@ -584,39 +547,7 @@ export class ConferenceCall extends RcModuleV2 { delete this._timers[id]; } - openPulling() { - this._pulling = true; - } - - closePulling() { - this._pulling = false; - } - - togglePulling() { - this._pulling = !this._pulling; - } - - setCapacity(capacity = MAXIMUM_CAPACITY) { - if (typeof capacity !== 'number') { - throw new Error('The capcity must be a number'); - } - this._capacity = capacity; - return capacity; - } - - setTimeout(timeout: number = DEFAULT_TIMEOUT) { - if (typeof timeout !== 'number') { - throw new Error('The timeout must be a number'); - } - this._timeout = timeout; - return timeout; - } - - onMergeSuccess(func: (...args: any[]) => void, isOnce?: boolean) { - if (isOnce) { - this._eventEmitter.once(mergeEvents.mergeSucceeded, func); - return; - } + onMergeSuccess(func: (...args: any[]) => void) { this._eventEmitter.on(mergeEvents.mergeSucceeded, func); } @@ -685,29 +616,29 @@ export class ConferenceCall extends RcModuleV2 { this.startPollingConferenceStatus(conferenceId); return conferenceId; } - // @ts-expect-error + // @ts-expect-error TS(2339): Property 'id' does not exist on type 'Conference |... Remove this comment to see the full error message const { id } = await this.makeConference(true); let conferenceAccepted = false; await Promise.race([ new Promise((resolve, reject) => { - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. const sipSession = this._deps.webphone._sessions.get( this.conferences[id].sessionId, ); - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. sipSession.on('accepted', () => { conferenceAccepted = true; resolve(null); }); - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. sipSession.on('cancel', () => reject(new Error('conferencing cancel'))); - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. sipSession.on('failed', () => reject(new Error('conferencing failed'))); - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. sipSession.on('rejected', () => reject(new Error('conferencing rejected')), ); - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. sipSession.on('terminated', () => reject(new Error('conferencing terminated')), ); @@ -775,7 +706,7 @@ export class ConferenceCall extends RcModuleV2 { private _getProfile(sessionId: string) { const session = find( (session) => session.id === sessionId, - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. this._deps.webphone.sessions, ); @@ -783,17 +714,17 @@ export class ConferenceCall extends RcModuleV2 { let avatarUrl; let calleeType = calleeTypes.unknown; let partyName = - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. session.direction === callDirections.outbound - ? // @ts-expect-error + ? // @ts-expect-error TS(2532): Object is possibly 'undefined'. session.toUserName - : // @ts-expect-error + : // @ts-expect-error TS(2532): Object is possibly 'undefined'. session.fromUserName; const partyNumber = - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. session.direction === callDirections.outbound ? session.to : session.from; - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. let matchedContact = session.contactMatch; if (!matchedContact && this._deps.contactMatcher) { const nameMatches = this._deps.contactMatcher.dataMapping[partyNumber]; @@ -828,13 +759,13 @@ export class ConferenceCall extends RcModuleV2 { }) { const session = find( (x) => x.id === sessionId, - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. this._deps.webphone.sessions, ); const sessionToMergeWith = find( (x) => x.id === (sessionIdToMergeWith || this.mergingPair.fromSessionId), - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. this._deps.webphone.sessions, ); @@ -843,7 +774,7 @@ export class ConferenceCall extends RcModuleV2 { : [session]; for (const session of webphoneSessions) { - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'NormalizedSession | undefined' i... Remove this comment to see the full error message if (!this.validateCallRecording(session)) { return null; } @@ -853,10 +784,10 @@ export class ConferenceCall extends RcModuleV2 { if (conferenceState) { const conferenceSession = find( (x) => x.id === conferenceState.sessionId, - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. this._deps.webphone.sessions, ); - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'NormalizedSession | undefined' i... Remove this comment to see the full error message if (!this.validateCallRecording(conferenceSession)) { return null; } @@ -886,20 +817,20 @@ export class ConferenceCall extends RcModuleV2 { const conferenceData = Object.values(this.conferences)[0]; if (!conferenceData) { - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. await this._deps.webphone.resume(session.id); return null; } const currentConferenceSession = find( (x) => x.id === conferenceData.sessionId, - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. this._deps.webphone.sessions, ); - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. const isCurrentConferenceOnHold = currentConferenceSession.isOnHold; if (isCurrentConferenceOnHold) { - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. this._deps.webphone.resume(conferenceData.sessionId); } @@ -920,7 +851,7 @@ export class ConferenceCall extends RcModuleV2 { resetSuccess() { this.setIsMerging(false); this.setMergingPair({}); - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'null' is not assignable to param... Remove this comment to see the full error message this.setCurrentConferenceId(null); this.conferenceCallStatus = conferenceCallStatus.idle as any; this.conferences = {}; @@ -930,13 +861,19 @@ export class ConferenceCall extends RcModuleV2 { * User action track dispatchs * */ @track(trackEvents.clickHangupParticipantList) - participantListClickHangupTrack() {} + participantListClickHangupTrack() { + // + } @track(trackEvents.cancelRemoveRemoveParticipantsModal) - removeParticipantClickCancelTrack() {} + removeParticipantClickCancelTrack() { + // + } @track(trackEvents.clickRemoveRemoveParticipantsModal) - removeParticipantClickRemoveTrack() {} + removeParticipantClickRemoveTrack() { + // + } override _shouldInit() { return this._deps.auth.loggedIn && super._shouldInit(); @@ -947,20 +884,20 @@ export class ConferenceCall extends RcModuleV2 { } @computed((that: ConferenceCall) => [ - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. that._deps.webphone.sessions, that.mergingPair.fromSessionId, that.partyProfiles, ]) get lastCallInfo(): LastCallInfo { - // @ts-expect-error + // @ts-expect-error TS(2339): Property 'sessions' does not exist on type 'Webpho... Remove this comment to see the full error message const { sessions } = this._deps.webphone; const { partyProfiles, mergingPair: { fromSessionId }, } = this; if (!fromSessionId) { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type '{ calleeTyp... Remove this comment to see the full error message this._lastCallInfo = null; return this._lastCallInfo; } @@ -970,8 +907,7 @@ export class ConferenceCall extends RcModuleV2 { let sessionStatus; let matchedContact; const fromSession = sessions.find( - // @ts-expect-error - (session) => session.id === fromSessionId, + (session: any) => session.id === fromSessionId, ); if (fromSession) { sessionName = @@ -1028,13 +964,13 @@ export class ConferenceCall extends RcModuleV2 { case calleeTypes.conference: this._lastCallInfo = { calleeType: calleeTypes.conference, - // @ts-expect-error + // @ts-expect-error TS(2531): Object is possibly 'null'. avatarUrl: partiesAvatarUrls[0], - // @ts-expect-error + // @ts-expect-error TS(2531): Object is possibly 'null'. extraNum: partiesAvatarUrls.length - 1, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'string | un... Remove this comment to see the full error message name: null, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'string | un... Remove this comment to see the full error message phoneNumber: null, status: sessionStatus, lastCallContact: null, @@ -1054,7 +990,7 @@ export class ConferenceCall extends RcModuleV2 { default: this._lastCallInfo = { calleeType: calleeTypes.unknown, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'string | un... Remove this comment to see the full error message avatarUrl: null, name: sessionName, status: sessionStatus, diff --git a/packages/ringcentral-integration/modules/ConferenceCall/lib/constants.ts b/packages/ringcentral-integration/modules/ConferenceCall/lib/constants.ts index 0dcca9a12b..1fd4d186c0 100644 --- a/packages/ringcentral-integration/modules/ConferenceCall/lib/constants.ts +++ b/packages/ringcentral-integration/modules/ConferenceCall/lib/constants.ts @@ -22,4 +22,5 @@ export const partyStatusCode = ObjectMap.fromKeys( 'VoiceMailScreening', ].map((i) => i.toLowerCase()), ); + export const mergeEvents = ObjectMap.fromKeys(['mergeSucceeded']); diff --git a/packages/ringcentral-integration/modules/ConferenceCall/lib/helpers.ts b/packages/ringcentral-integration/modules/ConferenceCall/lib/helpers.ts index 0fd65e5736..8f7cfcf3fd 100644 --- a/packages/ringcentral-integration/modules/ConferenceCall/lib/helpers.ts +++ b/packages/ringcentral-integration/modules/ConferenceCall/lib/helpers.ts @@ -1,13 +1,13 @@ import { filter, find, map, sort } from 'ramda'; import type { Party } from '../ConferenceCall.interface'; + import { conferenceRole } from './constants'; export function ascendSortParties(parties: Party[]): Party[] { return sort( (last: Party, next: Party) => - // @ts-expect-error - +last.id.split('-')[1] - +next.id.split('-')[1], + +last.id!.split('-')[1] - +next.id!.split('-')[1], filter( (party) => party.conferenceRole.toLowerCase() !== conferenceRole.host, parties, diff --git a/packages/ringcentral-integration/modules/ConnectivityMonitor/ConnectivityMonitor.ts b/packages/ringcentral-integration/modules/ConnectivityMonitor/ConnectivityMonitor.ts index ed697d08ff..8a55858069 100644 --- a/packages/ringcentral-integration/modules/ConnectivityMonitor/ConnectivityMonitor.ts +++ b/packages/ringcentral-integration/modules/ConnectivityMonitor/ConnectivityMonitor.ts @@ -1,5 +1,3 @@ -import 'isomorphic-fetch'; - import { action, RcModuleV2, state } from '@ringcentral-integration/core'; import type { ApiError } from '@ringcentral/sdk'; @@ -7,6 +5,7 @@ import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; import { errorMessages } from '../AvailabilityMonitor'; import { errorMessages as rateLimiterErrorMessage } from '../RateLimiter'; + import type { Deps } from './ConnectivityMonitor.interface'; export const DEFAULT_TIME_TO_RETRY = 5 * 1000; @@ -37,7 +36,7 @@ export class ConnectivityMonitor extends RcModuleV2 { this._deps.connectivityMonitorOptions?.heartBeatInterval ?? DEFAULT_HEART_BEAT_INTERVAL; - protected _checkConnectionFunc = async () => { + _checkConnectionFunc = async () => { try { const checkConnectionFunc = this._deps.connectivityMonitorOptions?.checkConnectionFunc ?? @@ -53,16 +52,16 @@ export class ConnectivityMonitor extends RcModuleV2 { protected _lastEnvironmentCounter = 0; - private _unbindHandlers?: (() => void) | null = null; + _unbindHandlers?: (() => void) | null = null; - protected _requestSuccessHandler = () => { + _requestSuccessHandler = () => { if (!this.connectivity) { this.setConnectSuccess(); } this._retry(); }; - protected _requestErrorHandler = (error: ApiError) => { + _requestErrorHandler = (error: ApiError) => { if (error.message && errorMessageTypes.includes(error.message)) return; if (!error.response && this.connectivity) { diff --git a/packages/ringcentral-integration/modules/ContactMatcher/ContactMatcher.ts b/packages/ringcentral-integration/modules/ContactMatcher/ContactMatcher.ts index 8c5091ef6d..ce688cef0f 100644 --- a/packages/ringcentral-integration/modules/ContactMatcher/ContactMatcher.ts +++ b/packages/ringcentral-integration/modules/ContactMatcher/ContactMatcher.ts @@ -2,6 +2,7 @@ import type { Entity } from '../../interfaces/Entity.interface'; import { DataMatcher } from '../../lib/DataMatcherV2'; import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; + import type { Deps, ForceMatchBatchNumbersOptions, @@ -21,7 +22,7 @@ class ContactMatcher extends DataMatcher< super(deps, 'ContactMatcher', deps.contactMatcherOptions?.disableCache); } - // @ts-expect-error + // @ts-expect-error TS(2416): Property 'dataMatcherOptions' in type 'ContactMatc... Remove this comment to see the full error message get dataMatcherOptions() { return this._deps.contactMatcherOptions; } diff --git a/packages/ringcentral-integration/modules/ContactSearch/ContactSearch.ts b/packages/ringcentral-integration/modules/ContactSearch/ContactSearch.ts index 2abf772a14..0c87659011 100644 --- a/packages/ringcentral-integration/modules/ContactSearch/ContactSearch.ts +++ b/packages/ringcentral-integration/modules/ContactSearch/ContactSearch.ts @@ -1,6 +1,3 @@ -import { identity, sortBy } from 'ramda'; -import * as uuid from 'uuid'; - import { action, computed, @@ -8,10 +5,13 @@ import { state, storage, } from '@ringcentral-integration/core'; +import { identity, sortBy } from 'ramda'; +import * as uuid from 'uuid'; import { debounce } from '../../lib/debounce-throttle'; import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; + import type { ContactSearchState, Deps, @@ -68,7 +68,7 @@ export class ContactSearch extends RcModuleV2 { protected _debouncedSearchFn = debounce({ fn: this.search, threshold: 800 }); - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Timeout'. protected _timeoutId: NodeJS.Timeout = null; constructor(deps: Deps) { @@ -94,6 +94,15 @@ export class ContactSearch extends RcModuleV2 { this.searchStatus = searchStatus; } + @action + clearAndReset() { + this.cleanUp(); + this.searchStatus = contactSearchStatus.idle; + if (this._debouncedSearchFn) { + this._debouncedSearchFn.cancel(); + } + } + @action setPrepareSearch() { this.searchStatus = contactSearchStatus.prepareSearching; @@ -252,7 +261,7 @@ export class ContactSearch extends RcModuleV2 { this._clearTimeout(); this._timeoutId = setTimeout(async () => { const searching = { ...this.searching }; - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'undefined' is not assignable to type 'string... Remove this comment to see the full error message await this.search({ searchString: undefined }); await this.search(searching); }, this._ttl); @@ -285,9 +294,9 @@ export class ContactSearch extends RcModuleV2 { this.setSearchStatus(contactSearchStatus.searching); try { // search cache - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Entities'. let entities: Entities = null; - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'Entities | null' is not assignable to type '... Remove this comment to see the full error message entities = this._searchFromCache({ sourceName, searchString }); if (entities) { this._loadSearching({ searchOnSources, searchString, entities }); @@ -295,11 +304,11 @@ export class ContactSearch extends RcModuleV2 { } // search source const searchFn = this._searchSources.get(sourceName); - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'Entities | null' is not assignable to type '... Remove this comment to see the full error message entities = await searchFn({ searchString }); // format result const formatFn = this._searchSourcesFormat.get(sourceName); - // @ts-expect-error + // @ts-expect-error TS(2722): Cannot invoke an object which is possibly 'undefin... Remove this comment to see the full error message entities = formatFn(entities); // save result this._saveSearching({ sourceName, searchString, entities }); @@ -323,7 +332,7 @@ export class ContactSearch extends RcModuleV2 { _readyCheck() { for (const sourceName of this._searchSourcesCheck.keys()) { - // @ts-expect-error + // @ts-expect-error TS(2722): Cannot invoke an object which is possibly 'undefin... Remove this comment to see the full error message if (!this._searchSourcesCheck.get(sourceName)()) { return false; } diff --git a/packages/ringcentral-integration/modules/Contacts/Contacts.ts b/packages/ringcentral-integration/modules/Contacts/Contacts.ts index a66641b65a..fb324409b4 100644 --- a/packages/ringcentral-integration/modules/Contacts/Contacts.ts +++ b/packages/ringcentral-integration/modules/Contacts/Contacts.ts @@ -8,6 +8,7 @@ import type { } from '../../interfaces/Contact.model'; import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; + import type { Deps } from './Contacts.interface'; @Module({ diff --git a/packages/ringcentral-integration/modules/ConversationLogger/ConversationLogger.ts b/packages/ringcentral-integration/modules/ConversationLogger/ConversationLogger.ts index 7acdbeedfe..09fb610cc2 100644 --- a/packages/ringcentral-integration/modules/ConversationLogger/ConversationLogger.ts +++ b/packages/ringcentral-integration/modules/ConversationLogger/ConversationLogger.ts @@ -9,11 +9,12 @@ import { sleep } from '@ringcentral-integration/utils'; import { messageTypes } from '../../enums/messageTypes'; import type { Entity } from '../../interfaces/Entity.interface'; import type { Message } from '../../interfaces/MessageStore.model'; -import { Module } from '../../lib/di'; import { LoggerBase } from '../../lib/LoggerBase'; +import { Module } from '../../lib/di'; import type { Correspondent } from '../../lib/messageHelper'; import { getNumbersFromMessage, sortByDate } from '../../lib/messageHelper'; import { proxify } from '../../lib/proxy/proxify'; + import type { ConversationLogItem, ConversationLogMap, @@ -224,7 +225,10 @@ export class ConversationLogger extends LoggerBase { numberMap[number] = true; } } - addIfNotExist(conversation.self!); + + const self = conversation.self; + if (self) addIfNotExist(self); + conversation.correspondents!.forEach(addIfNotExist); await this._deps.contactMatcher.match({ queries: numbers }); const selfNumber = @@ -447,7 +451,7 @@ export class ConversationLogger extends LoggerBase { get uniqueNumbers() { const output: string[] = []; const numberMap: Record = {}; - function addIfNotExist(contact: Correspondent = {}) { + function addIfNotExist(contact: Correspondent) { const number = contact.phoneNumber || contact.extensionNumber; if (number && !numberMap[number]) { output.push(number); @@ -457,7 +461,10 @@ export class ConversationLogger extends LoggerBase { Object.keys(this.conversationLogMap).forEach((conversationId) => { Object.keys(this.conversationLogMap[conversationId]).forEach((date) => { const conversation = this.conversationLogMap[conversationId][date]; - addIfNotExist(conversation.self); + + const self = conversation.self; + if (self) addIfNotExist(self); + conversation.correspondents!.forEach(addIfNotExist); }); }); diff --git a/packages/ringcentral-integration/modules/ConversationMatcher/ConversationMatcher.ts b/packages/ringcentral-integration/modules/ConversationMatcher/ConversationMatcher.ts index e9ccd6c928..393811430c 100644 --- a/packages/ringcentral-integration/modules/ConversationMatcher/ConversationMatcher.ts +++ b/packages/ringcentral-integration/modules/ConversationMatcher/ConversationMatcher.ts @@ -2,6 +2,7 @@ import type { Entity } from '../../interfaces/Entity.interface'; import type { DataMatcherOptions } from '../../lib/DataMatcherV2'; import { DataMatcher } from '../../lib/DataMatcherV2'; import { Module } from '../../lib/di'; + import type { Deps } from './ConversationMatcher.interface'; @Module({ @@ -14,6 +15,7 @@ class ConversationMatcher< > extends DataMatcher { constructor(deps: Deps) { super( + // @ts-expect-error TS(2345): Argument of type 'Deps' is not assignable to param... Remove this comment to see the full error message deps, 'ConversationMatcher', deps.conversationMatcherOptions?.disableCache, @@ -21,6 +23,7 @@ class ConversationMatcher< } get dataMatcherOptions(): DataMatcherOptions { + // @ts-expect-error TS(2322): Type 'ConversationMatcherOptions | undefined' is n... Remove this comment to see the full error message return this._deps.conversationMatcherOptions; } } diff --git a/packages/ringcentral-integration/modules/Conversations/Conversations.interface.ts b/packages/ringcentral-integration/modules/Conversations/Conversations.interface.ts index 795674b109..39cc4e4459 100644 --- a/packages/ringcentral-integration/modules/Conversations/Conversations.interface.ts +++ b/packages/ringcentral-integration/modules/Conversations/Conversations.interface.ts @@ -1,12 +1,12 @@ import type MessageAttachmentInfo from '@rc-ex/core/lib/definitions/MessageAttachmentInfo'; import type { Message } from '../../interfaces/MessageStore.model'; +import type { RingCentralClient } from '../../lib/RingCentralClient'; import type { Correspondent, FaxAttachment, VoicemailAttachment, } from '../../lib/messageHelper'; -import type { RingCentralClient } from '../../lib/RingCentralClient'; import type { Alert } from '../Alert'; import type { AppFeatures } from '../AppFeatures'; import type { Auth } from '../Auth'; diff --git a/packages/ringcentral-integration/modules/Conversations/Conversations.ts b/packages/ringcentral-integration/modules/Conversations/Conversations.ts index 05b1da25f7..4d8bf26590 100644 --- a/packages/ringcentral-integration/modules/Conversations/Conversations.ts +++ b/packages/ringcentral-integration/modules/Conversations/Conversations.ts @@ -38,6 +38,7 @@ import { ATTACHMENT_SIZE_LIMITATION, messageSenderMessages, } from '../MessageSender'; + import type { CorrespondentMatch, CorrespondentResponse, @@ -54,10 +55,12 @@ function mergeMessages(messages: Message[], oldMessages: Message[]): Message[] { const currentMessages: Message[] = []; messages.forEach((element) => { currentMessages.push(element); + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. tmp[element.id] = 1; }); oldMessages.forEach((element) => { + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. if (!tmp[element.id]) { currentMessages.push(element); } @@ -68,6 +71,7 @@ function mergeMessages(messages: Message[], oldMessages: Message[]): Message[] { function getEarliestTime(messages: Message[]) { let newTime = Date.now(); messages.forEach((message) => { + // @ts-expect-error TS(2769): No overload matches this call. const creationTime = new Date(message.creationTime).getTime(); if (creationTime < newTime) { newTime = creationTime; @@ -89,6 +93,7 @@ export function getUniqueNumbers(conversations: Message[]): string[] { if (message.from && message.direction === messageDirection.inbound) { const fromNumber = message.from.phoneNumber || message.from.extensionNumber; + // @ts-expect-error TS(2345): Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message addIfNotExist(fromNumber); } if ( @@ -101,6 +106,7 @@ export function getUniqueNumbers(conversations: Message[]): string[] { return; } const toPhoneNumber = toNumber.phoneNumber || toNumber.extensionNumber; + // @ts-expect-error TS(2345): Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message addIfNotExist(toPhoneNumber); }); } @@ -185,6 +191,7 @@ export class Conversations extends RcModuleV2 { conversationsStatus.idle; @state + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'string | un... Remove this comment to see the full error message currentConversationId?: string = null; @state @@ -317,6 +324,7 @@ export class Conversations extends RcModuleV2 { (content) => content.conversationId === conversationId, ); if (existedContent) { + // @ts-expect-error TS(2532): Object is possibly 'undefined'. existedContent.attachments = existedContent.attachments.filter( (f) => f.name !== attachment.name, ); @@ -382,6 +390,7 @@ export class Conversations extends RcModuleV2 { this.oldConversations = []; this.currentPage = 1; this.fetchConversationsStatus = conversationsStatus.idle; + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'string | un... Remove this comment to see the full error message this.currentConversationId = null; this.oldMessages = []; this.fetchMessagesStatus = conversationsStatus.idle; @@ -515,11 +524,14 @@ export class Conversations extends RcModuleV2 { params.messageType = [typeFilter]; } try { + // @ts-expect-error TS(2322): Type 'import("/Users/declan.zou/Projects/rc/integr... Remove this comment to see the full error message const { records }: GetMessageList = await this._deps.client .account() .extension() .messageStore() + // @ts-expect-error TS(2345): Argument of type 'ListMessagesParameters' is not a... Remove this comment to see the full error message .list(params); + // @ts-expect-error TS(2532): Object is possibly 'undefined'. const recordsLength = records.length; this._olderDataExisted = recordsLength === this._perPage; if (typeFilter === this.typeFilter && currentPage === this.currentPage) { @@ -527,6 +539,7 @@ export class Conversations extends RcModuleV2 { recordsLength && this._perPage * this.currentPage < recordsLength + this.filteredConversations.length; + // @ts-expect-error TS(2345): Argument of type 'GetMessageInfoResponse[] | undef... Remove this comment to see the full error message this._fetchOldConversationsSuccess(records, isIncreaseCurrentPage); } } catch (e: any /** TODO: confirm with instanceof */) { @@ -567,6 +580,7 @@ export class Conversations extends RcModuleV2 { @proxify async unloadConversation() { + // @ts-expect-error TS(2345): Argument of type 'null' is not assignable to param... Remove this comment to see the full error message this._updateCurrentConversationId(null); this._olderMessagesExisted = true; } @@ -604,13 +618,17 @@ export class Conversations extends RcModuleV2 { dateTo: dateTo.toISOString(), }; try { + // @ts-expect-error TS(2322): Type 'import("/Users/declan.zou/Projects/rc/integr... Remove this comment to see the full error message const { records }: GetMessageList = await this._deps.client .account() .extension() .messageStore() + // @ts-expect-error TS(2345): Argument of type 'ListMessagesParameters' is not a... Remove this comment to see the full error message .list(params); + // @ts-expect-error TS(2532): Object is possibly 'undefined'. this._olderMessagesExisted = records.length === perPage; if (conversationId === this.currentConversationId) { + // @ts-expect-error TS(2345): Argument of type 'GetMessageInfoResponse[] | undef... Remove this comment to see the full error message this._fetchOldMessagesSuccess(records); } } catch (e: any /** TODO: confirm with instanceof */) { @@ -638,16 +656,19 @@ export class Conversations extends RcModuleV2 { if (text.length > 1000) { return this._alertWarning(messageSenderMessages.textTooLong); } + // @ts-expect-error TS(2345): Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message this._updateMessageText(this.currentConversationId, text); } @proxify async addAttachment(attachment: Attachment) { const attachments = this.attachments; + // @ts-expect-error TS(2532): Object is possibly 'undefined'. if (attachments.length >= 10) { this._alertWarning(messageSenderMessages.attachmentCountLimitation); return; } + // @ts-expect-error TS(2532): Object is possibly 'undefined'. const size = attachments.reduce((prev, curr) => { return prev + curr.size; }, 0); @@ -655,11 +676,13 @@ export class Conversations extends RcModuleV2 { this._alertWarning(messageSenderMessages.attachmentSizeLimitation); return; } + // @ts-expect-error TS(2345): Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message this._addAttachment(this.currentConversationId, attachment); } @proxify async removeAttachment(attachment: Attachment) { + // @ts-expect-error TS(2345): Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message this._removeAttachment(this.currentConversationId, attachment); } @@ -668,15 +691,20 @@ export class Conversations extends RcModuleV2 { this._updateConversationStatus(conversationsStatus.pushing); try { const responses = await this._deps.messageSender.send({ + // @ts-expect-error TS(2322): Type 'string | null | undefined' is not assignable... Remove this comment to see the full error message fromNumber: this._getFromNumber(), + // @ts-expect-error TS(2322): Type '(string | undefined)[]' is not assignable to... Remove this comment to see the full error message toNumbers: this._getToNumbers(), text, attachments, + // @ts-expect-error TS(2322): Type 'number | null' is not assignable to type 'nu... Remove this comment to see the full error message replyOnMessageId: this._getReplyOnMessageId(), }); if (responses && responses[0]) { + // @ts-expect-error TS(2345): Argument of type 'import("/Users/declan.zou/Projec... Remove this comment to see the full error message this._deps.messageStore.pushMessage(responses[0]); this._updateConversationStatus(conversationsStatus.idle); + // @ts-expect-error TS(2345): Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message this._removeInputContent(this.currentConversationId); return responses[0]; } @@ -761,10 +789,12 @@ export class Conversations extends RcModuleV2 { const pushConversation = (c: Message) => { // use conversationId when available, use id for VoiceMail/Fax/etc.. const cid = c.conversationId || c.id; + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. if (conversationMap[cid]) { return; } newConversations.push(c); + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. conversationMap[cid] = 1; }; conversations.forEach(pushConversation); @@ -840,13 +870,16 @@ export class Conversations extends RcModuleV2 { this._deps.conversationLogger.dataMapping) || {}; const accessToken = this._deps.auth.accessToken; + // @ts-expect-error TS(2322): Type '{ unreadCounts: number; self: any; selfMatch... Remove this comment to see the full error message return conversations.map((message) => { const { self, correspondents } = getNumbersFromMessage({ + // @ts-expect-error TS(2322): Type 'string | undefined' is not assignable to typ... Remove this comment to see the full error message extensionNumber, message, }); const selfNumber = self && (self.phoneNumber || self.extensionNumber); const selfMatches = (selfNumber && contactMapping[selfNumber]) || []; + // @ts-expect-error TS(2532): Object is possibly 'undefined'. const correspondentMatches: CorrespondentMatch[] = correspondents.reduce( (matches: CorrespondentMatch[], contact: Correspondent) => { const number = @@ -864,13 +897,16 @@ export class Conversations extends RcModuleV2 { : null; const isLogging = !!(conversationLogId && loggingMap[conversationLogId]); const conversationMatches = + // @ts-expect-error TS(2538): Type 'null' cannot be used as an index type. conversationLogMapping[conversationLogId] || []; let voicemailAttachment = null; if (messageIsVoicemail(message)) { + // @ts-expect-error TS(2345): Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message voicemailAttachment = getVoicemailAttachment(message, accessToken); } let faxAttachment = null; if (messageIsFax(message)) { + // @ts-expect-error TS(2345): Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message faxAttachment = getFaxAttachment(message, accessToken); } let unreadCounts = message.unreadCounts; @@ -879,6 +915,7 @@ export class Conversations extends RcModuleV2 { } let mmsAttachments: MessageAttachmentInfo[] = []; if (messageIsTextMessage(message) && this._showMMSAttachment) { + // @ts-expect-error TS(2345): Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message mmsAttachments = getMMSAttachments(message, accessToken); } return { @@ -897,6 +934,7 @@ export class Conversations extends RcModuleV2 { lastMatchedCorrespondentEntity: (this._deps.conversationLogger && this._deps.conversationLogger.getLastMatchedCorrespondentEntity( + // @ts-expect-error TS(2345): Argument of type 'Message' is not assignable to pa... Remove this comment to see the full error message message, )) || null, @@ -974,7 +1012,9 @@ export class Conversations extends RcModuleV2 { return; } const messageList: Message[] = + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. this._deps.messageStore.conversationStore[message.conversationId] || []; + // @ts-expect-error TS(2322): Type 'Message | undefined' is not assignable to ty... Remove this comment to see the full error message const matchedMessage: Message = messageList.find( (item) => (item.subject || '').toLowerCase().indexOf(searchString) > -1, ); @@ -1039,6 +1079,7 @@ export class Conversations extends RcModuleV2 { (c) => c.conversationId === conversationId, ); const messages: Message[] = [].concat( + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. conversationStore[conversationId] || [], ); const currentConversation = { @@ -1048,6 +1089,7 @@ export class Conversations extends RcModuleV2 { if (!this._showMMSAttachment) { return m; } + // @ts-expect-error TS(2345): Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message const mmsAttachments = getMMSAttachments(m, accessToken); return { ...m, @@ -1055,7 +1097,9 @@ export class Conversations extends RcModuleV2 { }; }); const { correspondents = [] } = getNumbersFromMessage({ + // @ts-expect-error TS(2322): Type 'string | undefined' is not assignable to typ... Remove this comment to see the full error message extensionNumber, + // @ts-expect-error TS(2322): Type 'Message | undefined' is not assignable to ty... Remove this comment to see the full error message message: conversation, }); const correspondentMatches: CorrespondentMatch[] = correspondents.reduce( @@ -1069,29 +1113,38 @@ export class Conversations extends RcModuleV2 { [] as CorrespondentMatch[], ); const conversationLogId = this._deps.conversationLogger - ? this._deps.conversationLogger.getConversationLogId(conversation) + ? // @ts-expect-error TS(2345): Argument of type 'Message | undefined' is not assi... Remove this comment to see the full error message + this._deps.conversationLogger.getConversationLogId(conversation) : null; + // @ts-expect-error TS(2538): Type 'null' cannot be used as an index type. const conversationMatches = conversationLogMapping[conversationLogId] || []; + // @ts-expect-error TS(2322): Type 'string | null | undefined' is not assignable... Remove this comment to see the full error message currentConversation.conversationLogId = conversationLogId; currentConversation.correspondents = correspondents; currentConversation.correspondentMatches = correspondentMatches; currentConversation.conversationMatches = conversationMatches; currentConversation.messages = allMessages.reverse(); + // @ts-expect-error TS(2322): Type 'MessageStoreCallerInfoResponseFrom | null | ... Remove this comment to see the full error message currentConversation.senderNumber = getMyNumberFromMessage({ + // @ts-expect-error TS(2322): Type 'Message | undefined' is not assignable to ty... Remove this comment to see the full error message message: conversation, + // @ts-expect-error TS(2322): Type 'string | undefined' is not assignable to typ... Remove this comment to see the full error message myExtensionNumber: this._deps.extensionInfo.extensionNumber, }); currentConversation.recipients = getRecipientNumbersFromMessage({ + // @ts-expect-error TS(2322): Type 'Message | undefined' is not assignable to ty... Remove this comment to see the full error message message: conversation, myNumber: currentConversation.senderNumber, }); currentConversation.isLogging = !!( conversationLogId && loggingMap[conversationLogId] ); + // @ts-expect-error TS(2322): Type 'Entity | null' is not assignable to type 'La... Remove this comment to see the full error message currentConversation.lastMatchedCorrespondentEntity = (this._deps.conversationLogger && conversation && this._deps.conversationLogger.getLastMatchedCorrespondentEntity( + // @ts-expect-error TS(2345): Argument of type 'Message' is not assignable to pa... Remove this comment to see the full error message conversation, )) || null; @@ -1166,6 +1219,7 @@ export class Conversations extends RcModuleV2 { const { countryCode, areaCode } = this._deps.regionSettings; const formattedCorrespondentMatch = this.correspondentMatch.map((item) => { const formatted = normalizeNumber({ + // @ts-expect-error TS(2322): Type 'string | undefined' is not assignable to typ... Remove this comment to see the full error message phoneNumber: item.phoneNumber, countryCode, areaCode, @@ -1179,7 +1233,9 @@ export class Conversations extends RcModuleV2 { formattedCorrespondentMatch.forEach((item) => { const { phoneNumber } = item; const conversationId = this.correspondentResponse[phoneNumber]; + // @ts-expect-error TS(2532): Object is possibly 'undefined'. if (this._deps.conversationLogger.autoLog) { + // @ts-expect-error TS(2532): Object is possibly 'undefined'. this._deps.conversationLogger.logConversation({ entity: item, conversationId, diff --git a/packages/ringcentral-integration/modules/DataFetcherV2/DataFetcherV2.ts b/packages/ringcentral-integration/modules/DataFetcherV2/DataFetcherV2.ts index 5e9f9e4e03..3d640c1b23 100644 --- a/packages/ringcentral-integration/modules/DataFetcherV2/DataFetcherV2.ts +++ b/packages/ringcentral-integration/modules/DataFetcherV2/DataFetcherV2.ts @@ -1,14 +1,14 @@ -import { forEach } from 'ramda'; - import { action, RcModuleV2, state, storage, } from '@ringcentral-integration/core'; +import { forEach } from 'ramda'; import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; + import type { Deps } from './DataFetcherV2.interface'; import type { DataSource } from './DataSource'; import { sourceStatus } from './sourceStatus'; @@ -35,9 +35,6 @@ export class DataFetcherV2 extends RcModuleV2 { enableCache: true, deps, }); - this._deps.sleepDetector.on(this._deps.sleepDetector.events.detected, () => - this._handleSleepDetected(), - ); } override _shouldInit() { @@ -147,7 +144,7 @@ export class DataFetcherV2 extends RcModuleV2 { protected _startPolling( source: DataSource, - // @ts-expect-error + // @ts-expect-error TS(2531): Object is possibly 'null'. t = this.getTimestamp(source) + source.pollingInterval + 10 - Date.now(), ) { this._clearTimeout(source); @@ -162,7 +159,7 @@ export class DataFetcherV2 extends RcModuleV2 { source.permissionCheckFunction() ) { if (this._expired(source)) { - this.fetchData(source); + this.tryFetchData(source, '_startPolling'); } else { this._startPolling(source); } @@ -187,7 +184,7 @@ export class DataFetcherV2 extends RcModuleV2 { source.readyCheckFunction() && source.permissionCheckFunction() ) { - this.fetchData(source); + this.tryFetchData(source, '_retry'); } else { this._retry(source); } @@ -197,7 +194,20 @@ export class DataFetcherV2 extends RcModuleV2 { } @proxify - async fetchData(source: DataSource): Promise { + async tryFetchData(source: DataSource, callerName: string) { + try { + await this.fetchData(source); + } catch (ex) { + console.error( + `[DataFetcherV2] > ${callerName} > fetchData`, + `source "${source.key}"`, + ex, + ); + } + } + + @proxify + async fetchData(source: DataSource): Promise { if (!this._promises.get(source.key)) { this._promises.set(source.key, this._fetchData(source)); } @@ -216,7 +226,7 @@ export class DataFetcherV2 extends RcModuleV2 { } protected _expired(source: DataSource) { - // @ts-expect-error + // @ts-expect-error TS(2531): Object is possibly 'null'. return Date.now() - this.getTimestamp(source) > source.ttl; } @@ -307,13 +317,13 @@ export class DataFetcherV2 extends RcModuleV2 { ) { // if the data set to null due to permission before // but now there is permission, then fetch data - this.fetchData(source); + this.tryFetchData(source, '_processSources'); } } } else if (status === sourceStatus.ready) { this._setSourceStatus(source.key, sourceStatus.pending); if (source.cleanOnReset) { - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'null' is not assignable to param... Remove this comment to see the full error message this._setData(source.key, source.disableCache, null, null); } } @@ -321,13 +331,13 @@ export class DataFetcherV2 extends RcModuleV2 { } } - protected _handleSleepDetected() { + protected _handleSleepDetected = () => { forEach((source) => { if (this.ready && this._shouldFetch(source)) { - this.fetchData(source); + this.tryFetchData(source, '_handleSleepDetected'); } }, Array.from(this._sources)); - } + }; protected _getRegisteredKeys() { const keys = new Set(); @@ -372,11 +382,19 @@ export class DataFetcherV2 extends RcModuleV2 { } override onInit() { + this._deps.sleepDetector.on( + this._deps.sleepDetector.events.detected, + this._handleSleepDetected, + ); // clean up cached sources that are no longer exist this._cleanCache(); } override onReset() { + this._deps.sleepDetector.off( + this._deps.sleepDetector.events.detected, + this._handleSleepDetected, + ); forEach((source) => { // clear all pollings or retries this._clearTimeout(source); @@ -392,7 +410,7 @@ export class DataFetcherV2 extends RcModuleV2 { this.getData(source) !== null && this.getTimestamp(source) !== null ) { - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'null' is not assignable to param... Remove this comment to see the full error message this._setData(source.key, source.disableCache, null, null); } }, Array.from(this._sources)); @@ -413,7 +431,7 @@ export class DataFetcherV2 extends RcModuleV2 { } return this.cachedData[source.key] || null; } - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'T'. return null; } } diff --git a/packages/ringcentral-integration/modules/DataFetcherV2/DataFetcherV2Consumer.ts b/packages/ringcentral-integration/modules/DataFetcherV2/DataFetcherV2Consumer.ts index af32bbb34b..ec34e79780 100644 --- a/packages/ringcentral-integration/modules/DataFetcherV2/DataFetcherV2Consumer.ts +++ b/packages/ringcentral-integration/modules/DataFetcherV2/DataFetcherV2Consumer.ts @@ -1,7 +1,6 @@ -import { EventEmitter } from 'events'; - import { RcModuleV2, watch } from '@ringcentral-integration/core'; import { ObjectMap } from '@ringcentral-integration/core/lib/ObjectMap'; +import { EventEmitter } from 'events'; import type { DataFetcherV2ConsumerBaseDeps } from './DataFetcherV2Consumer.interface'; import type { DataSource } from './DataSource'; @@ -13,7 +12,7 @@ export abstract class DataFetcherV2Consumer< D extends DataFetcherV2ConsumerBaseDeps, T, > extends RcModuleV2 { - // @ts-expect-error + // @ts-expect-error TS(2564): Property '_source' has no initializer and is not d... Remove this comment to see the full error message protected _source: DataSource; protected _emitter = new EventEmitter(); diff --git a/packages/ringcentral-integration/modules/DataTransportManager/DataTransportManager.ts b/packages/ringcentral-integration/modules/DataTransportManager/DataTransportManager.ts index 074e0e3a5c..7f1085cf7e 100644 --- a/packages/ringcentral-integration/modules/DataTransportManager/DataTransportManager.ts +++ b/packages/ringcentral-integration/modules/DataTransportManager/DataTransportManager.ts @@ -1,6 +1,8 @@ import { RcModuleV2 } from '@ringcentral-integration/core'; -import { Module } from '../../lib/di'; + import { createTransport } from '../../lib/dataTransport'; +import { Module } from '../../lib/di'; + import type { CustomKeyNameMap, Deps, @@ -9,6 +11,7 @@ import type { TransportMapParams, Transports, } from './DataTransportManager.interface'; + @Module({ name: 'DataTransportManager', }) diff --git a/packages/ringcentral-integration/modules/DateTimeFormat/DateTimeFormat.ts b/packages/ringcentral-integration/modules/DateTimeFormat/DateTimeFormat.ts index ac0aabb232..7344ba069d 100644 --- a/packages/ringcentral-integration/modules/DateTimeFormat/DateTimeFormat.ts +++ b/packages/ringcentral-integration/modules/DateTimeFormat/DateTimeFormat.ts @@ -3,6 +3,7 @@ import { RcModuleV2 } from '@ringcentral-integration/core'; import { Module } from '../../lib/di'; import type { DateTimeFormatter } from '../../lib/getIntlDateTimeFormatter'; import getIntlDateTimeFormatter from '../../lib/getIntlDateTimeFormatter'; + import type { AddFormatterOptions, Deps, @@ -34,6 +35,7 @@ export class DateTimeFormat extends RcModuleV2 { this.setDefaultFormatter(); } + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message initializeProxy() { this.store.subscribe(() => { this.setDefaultFormatter(); @@ -72,6 +74,7 @@ export class DateTimeFormat extends RcModuleV2 { type, }); } + // @ts-expect-error TS(2722): Cannot invoke an object which is possibly 'undefin... Remove this comment to see the full error message return this._defaultFormatter({ utcTimestamp, locale, diff --git a/packages/ringcentral-integration/modules/DialingPlan/DialingPlan.ts b/packages/ringcentral-integration/modules/DialingPlan/DialingPlan.ts index 2d65a56098..68aa2f888c 100644 --- a/packages/ringcentral-integration/modules/DialingPlan/DialingPlan.ts +++ b/packages/ringcentral-integration/modules/DialingPlan/DialingPlan.ts @@ -1,9 +1,11 @@ import type CountryInfoShortModel from '@rc-ex/core/lib/definitions/CountryInfoShortModel'; import { computed } from '@ringcentral-integration/core'; + import { renameTurkeyCountries } from '../../helpers/renameTurkey'; import { Module } from '../../lib/di'; import fetchList from '../../lib/fetchList'; import { DataFetcherV2Consumer, DataSource } from '../DataFetcherV2'; + import type { Deps } from './DialingPlan.interface'; @Module({ @@ -24,7 +26,7 @@ export class DialingPlan extends DataFetcherV2Consumer< deps, }); const { polling = true } = deps.dialingPlanOptions ?? {}; - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'DataSource' is not assignable to ... Remove this comment to see the full error message this._source = new DataSource({ ...deps.dialingPlanOptions, key: 'dialingPlan', diff --git a/packages/ringcentral-integration/modules/Environment/Environment.interface.ts b/packages/ringcentral-integration/modules/Environment/Environment.interface.ts index f3123a2bf3..38ab326f33 100644 --- a/packages/ringcentral-integration/modules/Environment/Environment.interface.ts +++ b/packages/ringcentral-integration/modules/Environment/Environment.interface.ts @@ -1,9 +1,10 @@ -import type { SDKConfig } from '../../lib/createSdkConfig'; import type { RingCentralClient } from '../../lib/RingCentralClient'; +import type { SDKConfig } from '../../lib/createSdkConfig'; import type { GlobalStorage } from '../GlobalStorage'; export interface EnvironmentOptions { defaultRecordingHost?: string; + useDataTrackingSetting?: boolean; } export interface Deps { @@ -18,4 +19,5 @@ export interface SetDataOptions { recordingHost: string; enabled: boolean; environmentChanged?: boolean; + allowDataTracking?: boolean; } diff --git a/packages/ringcentral-integration/modules/Environment/Environment.ts b/packages/ringcentral-integration/modules/Environment/Environment.ts index 95e80d1f49..fde382c753 100644 --- a/packages/ringcentral-integration/modules/Environment/Environment.ts +++ b/packages/ringcentral-integration/modules/Environment/Environment.ts @@ -9,10 +9,13 @@ import { SDK } from '@ringcentral/sdk'; import type { SDKConfig } from '../../lib/createSdkConfig'; import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; + import type { Deps, SetDataOptions } from './Environment.interface'; +import { localStorageDataTrackingTimestamp } from './enabledDataTrackingTimestamp'; const DEFAULT_RECORDING_HOST = 'https://apps.ringcentral.com/integrations/recording/v3.0/rc/index.html'; +const TWO_HOURS_IN_MILLISECONDS = 2 * 60 * 60 * 1000; @Module({ name: 'Environment', @@ -47,6 +50,10 @@ export class Environment extends RcModuleV2 { @state recordingHostState: string | null = null; + get enabledDataTrackingTimestamp() { + return localStorageDataTrackingTimestamp.get(); + } + @globalStorage @state enabled = false; @@ -55,10 +62,18 @@ export class Environment extends RcModuleV2 { changeCounter = 0; @action - setEnvData({ server, recordingHost, enabled }: SetDataOptions) { + setEnvData({ + server, + recordingHost, + enabled, + allowDataTracking, + }: SetDataOptions) { this.server = server; this.recordingHostState = recordingHost; this.enabled = enabled; + localStorageDataTrackingTimestamp.set( + allowDataTracking ? Date.now() : null, + ); } @action @@ -66,9 +81,21 @@ export class Environment extends RcModuleV2 { this.changeCounter++; } - changeEnvironment() { + protected async changeEnvironment() { const sdkConfig = this.getSdkConfig(); + if (sdkConfig.enableDiscovery) { + // Clear discovery data before switching to new env + const discovery = this._deps.client.service.platform().discovery(); + if (discovery) { + await discovery.removeExternalData?.(); + await discovery.removeInitialData?.(); + } + } this._deps.client.service = new SDK(sdkConfig); + if (sdkConfig.enableDiscovery && sdkConfig.discoveryAutoInit === false) { + // make sure to init discovery API if discoveryAutoInit is deliberately set to false + this._deps.client.service.platform().initDiscovery(); + } } getSdkConfig() { @@ -87,6 +114,7 @@ export class Environment extends RcModuleV2 { server, recordingHost, enabled, + allowDataTracking = false, environmentChanged = false, }: SetDataOptions) { // `recordingHost` change no need to set to SDK @@ -99,11 +127,12 @@ export class Environment extends RcModuleV2 { server, recordingHost, enabled, + allowDataTracking, }); if (isEnvChanged) { // apply changes - this.changeEnvironment(); + await this.changeEnvironment(); // notify change at last this.updateChangeCounter(); } @@ -113,6 +142,26 @@ export class Environment extends RcModuleV2 { return this.enabled ? this.recordingHostState : this._defaultRecordingHost; } + get allowDataTracking() { + if (!this.useDataTrackingSetting) return true; + + const timestamp = this.enabledDataTrackingTimestamp; + if (!timestamp) return false; + + const isWithinTwoHours = Date.now() - timestamp < TWO_HOURS_IN_MILLISECONDS; + + if (!isWithinTwoHours) { + // clear data tracking setting if it's expired for prevent get Date.now() anymore + localStorageDataTrackingTimestamp.set(null); + } + + return isWithinTwoHours; + } + + get useDataTrackingSetting() { + return this._deps.environmentOptions?.useDataTrackingSetting; + } + protected get _defaultRecordingHost() { return ( this._deps.environmentOptions?.defaultRecordingHost ?? diff --git a/packages/ringcentral-integration/modules/Environment/enabledDataTrackingTimestamp.ts b/packages/ringcentral-integration/modules/Environment/enabledDataTrackingTimestamp.ts new file mode 100644 index 0000000000..fb2c9b1e98 --- /dev/null +++ b/packages/ringcentral-integration/modules/Environment/enabledDataTrackingTimestamp.ts @@ -0,0 +1,29 @@ +const DATA_TRACKING_TIMESTAMP_STORAGE_KEY = + 'environment.enabledDataTrackingTimestamp'; + +/** + * Local storage for data tracking timestamp + * + * use local storage to get state be `synchronously` + */ +export const localStorageDataTrackingTimestamp = { + get: () => { + if (typeof localStorage === 'undefined') return null; + + const value = localStorage.getItem(DATA_TRACKING_TIMESTAMP_STORAGE_KEY); + return value ? +value : null; + }, + set: (timestamp: number | null) => { + if (typeof localStorage === 'undefined') return null; + + if (timestamp === null) { + localStorage.removeItem(DATA_TRACKING_TIMESTAMP_STORAGE_KEY); + return; + } + + localStorage.setItem( + DATA_TRACKING_TIMESTAMP_STORAGE_KEY, + timestamp.toString(), + ); + }, +}; diff --git a/packages/ringcentral-integration/modules/ErrorLogger/ErrorLogger.interface.ts b/packages/ringcentral-integration/modules/ErrorLogger/ErrorLogger.interface.ts index 7ca9506c6e..5132ad98a8 100644 --- a/packages/ringcentral-integration/modules/ErrorLogger/ErrorLogger.interface.ts +++ b/packages/ringcentral-integration/modules/ErrorLogger/ErrorLogger.interface.ts @@ -1,3 +1,4 @@ +import type { AccountInfo } from '../AccountInfo'; import type { Auth } from '../Auth'; import type { BrandConfig } from '../Brand'; @@ -11,10 +12,15 @@ export interface ErrorLoggerOptions { appRelease?: string; environment?: string; sentryConfig?: SentryConfig; + /** + * The brands by brandId that should be intercepted by the error logger. + */ + interceptedBrands?: string[]; } export interface Deps { brandConfig: BrandConfig; auth?: Auth; + accountInfo?: AccountInfo; errorLoggerOptions?: ErrorLoggerOptions; } diff --git a/packages/ringcentral-integration/modules/ErrorLogger/ErrorLogger.ts b/packages/ringcentral-integration/modules/ErrorLogger/ErrorLogger.ts index 4f81d548c1..8e83704c01 100644 --- a/packages/ringcentral-integration/modules/ErrorLogger/ErrorLogger.ts +++ b/packages/ringcentral-integration/modules/ErrorLogger/ErrorLogger.ts @@ -1,17 +1,20 @@ import { RcModuleV2, watch } from '@ringcentral-integration/core'; - import * as Sentry from '@sentry/browser'; -import type { SeverityLevel, User } from '@sentry/types'; import { BrowserTracing } from '@sentry/tracing'; +import type { SeverityLevel, User } from '@sentry/types'; import { Module } from '../../lib/di'; + import type { Deps, ErrorLoggerOptions } from './ErrorLogger.interface'; +const DEFAULT_INTERCEPTED_BRANDS = ['3000.Brightspeed']; + @Module({ name: 'ErrorLogger', deps: [ 'BrandConfig', { dep: 'Auth', optional: true }, + { dep: 'AccountInfo', optional: true }, { dep: 'ErrorLoggerOptions', optional: true }, ], }) @@ -25,6 +28,21 @@ export class ErrorLogger extends RcModuleV2 { if (deps.errorLoggerOptions) { this._bootstrap(deps.errorLoggerOptions); + Promise.resolve().then(() => { + if (this._sentryInitialized && this._deps.accountInfo) { + watch( + this, + () => this._deps.accountInfo?.userBrandId, + (userBrandId) => { + if (userBrandId && this.interceptedBrands.includes(userBrandId)) { + this.toggle({ intercepted: true }); + } else { + this.toggle({ intercepted: false }); + } + }, + ); + } + }); } } @@ -62,7 +80,19 @@ export class ErrorLogger extends RcModuleV2 { 'INVALID_STATE_ERROR: Invalid status: 11', 'INVALID_STATE_ERROR: Invalid status: 1', 'rateLimiterErrorMessages-rateLimitReached', + // chrome error + '[executeScript] Cannot access contents of the page. Extension manifest must request permission to access the respective host.', + '[executeScript] The extensions gallery cannot be scripted.', + '[executeScript] Cannot access contents of url', + '[executeScript] This page cannot be scripted due to an ExtensionsSettings policy.', + '[executeScript] Cannot access a chrome', ], + beforeSend: (event) => { + if (this.intercepted) { + return null; + } + return event; + }, }); this._sentryInitialized = true; } @@ -84,6 +114,19 @@ export class ErrorLogger extends RcModuleV2 { } } + get interceptedBrands() { + return ( + this._deps.errorLoggerOptions?.interceptedBrands ?? + DEFAULT_INTERCEPTED_BRANDS + ); + } + + private intercepted?: boolean; + + toggle({ intercepted }: { intercepted: boolean }) { + this.intercepted = intercepted; + } + setUser(user: User | null) { Sentry.configureScope((scope) => { scope.setUser(user); diff --git a/packages/ringcentral-integration/modules/ExtensionDevice/ExtensionDevice.ts b/packages/ringcentral-integration/modules/ExtensionDevice/ExtensionDevice.ts index 9219f0c763..bac48950b2 100644 --- a/packages/ringcentral-integration/modules/ExtensionDevice/ExtensionDevice.ts +++ b/packages/ringcentral-integration/modules/ExtensionDevice/ExtensionDevice.ts @@ -1,11 +1,12 @@ -import { reduce } from 'ramda'; import type DeviceResource from '@rc-ex/core/lib/definitions/DeviceResource'; import type PhoneLinesInfo from '@rc-ex/core/lib/definitions/PhoneLinesInfo'; import { computed } from '@ringcentral-integration/core'; +import { reduce } from 'ramda'; import { Module } from '../../lib/di'; import fetchList from '../../lib/fetchList'; import { DataFetcherV2Consumer, DataSource } from '../DataFetcherV2'; + import type { Deps } from './ExtensionDevice.interface'; @Module({ diff --git a/packages/ringcentral-integration/modules/ExtensionFeatures/ExtensionFeatures.ts b/packages/ringcentral-integration/modules/ExtensionFeatures/ExtensionFeatures.ts index 84017554d3..65977b4289 100644 --- a/packages/ringcentral-integration/modules/ExtensionFeatures/ExtensionFeatures.ts +++ b/packages/ringcentral-integration/modules/ExtensionFeatures/ExtensionFeatures.ts @@ -1,9 +1,9 @@ -import { reduce } from 'ramda'; -import type { Unsubscribe } from 'redux'; import type ExtensionInfoEvent from '@rc-ex/core/lib/definitions/ExtensionInfoEvent'; import type FeatureInfo from '@rc-ex/core/lib/definitions/FeatureInfo'; import type FeatureList from '@rc-ex/core/lib/definitions/FeatureList'; import { computed, watch } from '@ringcentral-integration/core'; +import { reduce } from 'ramda'; +import type { Unsubscribe } from 'redux'; import { permissionsMessages } from '../../enums/permissionsMessages'; import { subscriptionFilters } from '../../enums/subscriptionFilters'; @@ -11,6 +11,7 @@ import { subscriptionHints } from '../../enums/subscriptionHints'; import { Module } from '../../lib/di'; import { loginStatus } from '../Auth'; import { DataFetcherV2Consumer, DataSource } from '../DataFetcherV2'; + import type { Deps } from './ExtensionFeatures.interface'; @Module({ @@ -29,7 +30,7 @@ export class ExtensionFeatures extends DataFetcherV2Consumer< Deps, FeatureList > { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Unsubscribe... Remove this comment to see the full error message protected _stopWatchingSubscription: Unsubscribe = null; constructor(deps: Deps) { super({ @@ -118,7 +119,7 @@ export class ExtensionFeatures extends DataFetcherV2Consumer< override onReset() { this._stopWatchingSubscription?.(); - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Unsubscribe... Remove this comment to see the full error message this._stopWatchingSubscription = null; } diff --git a/packages/ringcentral-integration/modules/ExtensionInfo/ExtensionInfo.ts b/packages/ringcentral-integration/modules/ExtensionInfo/ExtensionInfo.ts index 6ec6464490..6349237979 100644 --- a/packages/ringcentral-integration/modules/ExtensionInfo/ExtensionInfo.ts +++ b/packages/ringcentral-integration/modules/ExtensionInfo/ExtensionInfo.ts @@ -1,13 +1,15 @@ -import type { Unsubscribe } from 'redux'; import type ExtensionInfoEvent from '@rc-ex/core/lib/definitions/ExtensionInfoEvent'; import type GetExtensionInfoResponse from '@rc-ex/core/lib/definitions/GetExtensionInfoResponse'; import { computed, watch } from '@ringcentral-integration/core'; -import { renameTurkeyCountry } from '../../helpers/renameTurkey'; +import type { Unsubscribe } from 'redux'; + import { permissionsMessages } from '../../enums/permissionsMessages'; import { subscriptionFilters } from '../../enums/subscriptionFilters'; import { subscriptionHints } from '../../enums/subscriptionHints'; +import { renameTurkeyCountry } from '../../helpers/renameTurkey'; import { Module } from '../../lib/di'; import { DataFetcherV2Consumer, DataSource } from '../DataFetcherV2'; + import type { Deps } from './ExtensionInfo.interface'; const extensionRegExp = /.*\/extension\/\d+$/; @@ -34,7 +36,7 @@ export class ExtensionInfo extends DataFetcherV2Consumer< Deps, GetExtensionInfoResponse > { - // @ts-expect-error + // @ts-expect-error TS(2564): Property '_stopWatching' has no initializer and is... Remove this comment to see the full error message protected _stopWatching: Unsubscribe; constructor(deps: Deps) { super({ @@ -75,7 +77,7 @@ export class ExtensionInfo extends DataFetcherV2Consumer< if ( this.ready && (this._source.disableCache || (this._deps.tabManager?.active ?? true)) && - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message extensionRegExp.test(message?.event) && !( message.body?.hints?.includes(subscriptionHints.companyNumbers) || @@ -102,7 +104,7 @@ export class ExtensionInfo extends DataFetcherV2Consumer< override onReset() { this._stopWatching?.(); - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Unsubscribe... Remove this comment to see the full error message this._stopWatching = null; } diff --git a/packages/ringcentral-integration/modules/ExtensionNumberAreaCode/ExtensionNumberAreaCode.ts b/packages/ringcentral-integration/modules/ExtensionNumberAreaCode/ExtensionNumberAreaCode.ts index 656fba8a22..e14e9cb65f 100644 --- a/packages/ringcentral-integration/modules/ExtensionNumberAreaCode/ExtensionNumberAreaCode.ts +++ b/packages/ringcentral-integration/modules/ExtensionNumberAreaCode/ExtensionNumberAreaCode.ts @@ -1,10 +1,10 @@ -import type { Unsubscribe } from 'redux'; - import { computed, watch } from '@ringcentral-integration/core'; +import type { Unsubscribe } from 'redux'; import type { NumberParserAPIResponse } from '../../interfaces/NumberParserResponse.interface'; import { Module } from '../../lib/di'; import { DataFetcherV2Consumer, DataSource } from '../DataFetcherV2'; + import type { Deps } from './ExtensionNumberAreaCode.interface'; @Module({ diff --git a/packages/ringcentral-integration/modules/ExtensionPhoneNumber/ExtensionPhoneNumber.ts b/packages/ringcentral-integration/modules/ExtensionPhoneNumber/ExtensionPhoneNumber.ts index 53291f13a0..e4403a170a 100644 --- a/packages/ringcentral-integration/modules/ExtensionPhoneNumber/ExtensionPhoneNumber.ts +++ b/packages/ringcentral-integration/modules/ExtensionPhoneNumber/ExtensionPhoneNumber.ts @@ -1,8 +1,8 @@ -import { filter, find } from 'ramda'; -import type { Unsubscribe } from 'redux'; import type ExtensionInfoEvent from '@rc-ex/core/lib/definitions/ExtensionInfoEvent'; import type UserPhoneNumberInfo from '@rc-ex/core/lib/definitions/UserPhoneNumberInfo'; import { computed, watch } from '@ringcentral-integration/core'; +import { filter, find } from 'ramda'; +import type { Unsubscribe } from 'redux'; import { subscriptionFilters } from '../../enums/subscriptionFilters'; import { subscriptionHints } from '../../enums/subscriptionHints'; @@ -10,6 +10,7 @@ import { usageTypes } from '../../enums/usageTypes'; import { Module } from '../../lib/di'; import fetchList from '../../lib/fetchList'; import { DataFetcherV2Consumer, DataSource } from '../DataFetcherV2'; + import type { Deps } from './ExtensionPhoneNumber.interface'; @Module({ @@ -27,7 +28,7 @@ export class ExtensionPhoneNumber extends DataFetcherV2Consumer< Deps, UserPhoneNumberInfo[] > { - // @ts-expect-error + // @ts-expect-error TS(2564): Property '_stopWatching' has no initializer and is... Remove this comment to see the full error message protected _stopWatching: Unsubscribe; constructor(deps: Deps) { @@ -71,7 +72,7 @@ export class ExtensionPhoneNumber extends DataFetcherV2Consumer< override onReset() { this._stopWatching?.(); - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Unsubscribe... Remove this comment to see the full error message this._stopWatching = null; } @@ -107,7 +108,7 @@ export class ExtensionPhoneNumber extends DataFetcherV2Consumer< @computed(({ numbers }: ExtensionPhoneNumber) => [numbers]) get callerIdNumbers() { return filter( - (phoneNumber) => phoneNumber.features?.indexOf('CallerId') !== -1, + (phoneNumber) => !!phoneNumber.features?.includes('CallerId'), this.numbers, ); } @@ -120,7 +121,7 @@ export class ExtensionPhoneNumber extends DataFetcherV2Consumer< @computed(({ numbers }: ExtensionPhoneNumber) => [numbers]) get smsSenderNumbers() { return filter( - (phoneNumber) => phoneNumber.features?.indexOf('SmsSender') !== -1, + (phoneNumber) => !!phoneNumber.features?.includes('SmsSender'), this.numbers, ); } diff --git a/packages/ringcentral-integration/modules/FCMSubscription/FCMSubscription.interface.ts b/packages/ringcentral-integration/modules/FCMSubscription/FCMSubscription.interface.ts index ae7365381c..3c222d73c6 100644 --- a/packages/ringcentral-integration/modules/FCMSubscription/FCMSubscription.interface.ts +++ b/packages/ringcentral-integration/modules/FCMSubscription/FCMSubscription.interface.ts @@ -1,6 +1,8 @@ import { FirebaseOptions } from 'firebase/app'; + import { RingCentralClient } from '../../lib/RingCentralClient'; import { Auth } from '../Auth'; + export interface IFirebaseConfig { firebaseConfig: FirebaseOptions; vapidKey: string; diff --git a/packages/ringcentral-integration/modules/FCMSubscription/FCMSubscription.ts b/packages/ringcentral-integration/modules/FCMSubscription/FCMSubscription.ts index 1fbfa889ae..9d19294b3a 100644 --- a/packages/ringcentral-integration/modules/FCMSubscription/FCMSubscription.ts +++ b/packages/ringcentral-integration/modules/FCMSubscription/FCMSubscription.ts @@ -1,19 +1,22 @@ -import { initializeApp } from 'firebase/app'; -import { getMessaging, getToken } from 'firebase/messaging'; import { action, RcModuleV2, state, storage, } from '@ringcentral-integration/core'; +import { initializeApp } from 'firebase/app'; +import { getMessaging, getToken } from 'firebase/messaging'; + import { subscriptionFilters } from '../../enums/subscriptionFilters'; import { Module } from '../../lib/di'; import { waitUntilTo } from '../../utils'; + import { Deps, TFCMSubscription, TRegistrationToken, } from './FCMSubscription.interface'; + @Module({ name: 'FCMSubscription', deps: ['Auth', 'Storage', 'Client', { dep: 'FCMSubscriptionOptions' }], diff --git a/packages/ringcentral-integration/modules/Feedback/Feedback.ts b/packages/ringcentral-integration/modules/Feedback/Feedback.ts index a560628055..ac705347f3 100644 --- a/packages/ringcentral-integration/modules/Feedback/Feedback.ts +++ b/packages/ringcentral-integration/modules/Feedback/Feedback.ts @@ -7,6 +7,7 @@ import { import { Module } from '../../lib/di'; import proxify from '../../lib/proxy/proxify'; + import type { Deps } from './Feedback.interface'; @Module({ diff --git a/packages/ringcentral-integration/modules/ForwardingNumber/ForwardingNumber.ts b/packages/ringcentral-integration/modules/ForwardingNumber/ForwardingNumber.ts index b865643caf..a6f4c3a7e6 100644 --- a/packages/ringcentral-integration/modules/ForwardingNumber/ForwardingNumber.ts +++ b/packages/ringcentral-integration/modules/ForwardingNumber/ForwardingNumber.ts @@ -1,10 +1,11 @@ -import { filter } from 'ramda'; import type ForwardingNumberInfo from '@rc-ex/core/lib/definitions/ForwardingNumberInfo'; import { computed } from '@ringcentral-integration/core'; +import { filter } from 'ramda'; import { Module } from '../../lib/di'; import fetchList from '../../lib/fetchList'; import { DataFetcherV2Consumer, DataSource } from '../DataFetcherV2'; + import type { Deps } from './ForwardingNumber.interface'; @Module({ @@ -37,7 +38,7 @@ export class ForwardingNumber extends DataFetcherV2Consumer< .forwardingNumber() .list(params), ); - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'unknown[]' is not assignable to type 'Forwar... Remove this comment to see the full error message return forwardingNumbers; } catch (error: any /** TODO: confirm with instanceof */) { if (error.response?.status === 403) { @@ -64,8 +65,7 @@ export class ForwardingNumber extends DataFetcherV2Consumer< return filter( (phoneNumber) => !!( - phoneNumber.features?.indexOf('CallFlip') !== -1 && - phoneNumber.phoneNumber + phoneNumber.features?.includes('CallFlip') && phoneNumber.phoneNumber ), this.numbers, ); @@ -76,7 +76,7 @@ export class ForwardingNumber extends DataFetcherV2Consumer< return filter( (phoneNumber) => !!( - phoneNumber.features?.indexOf('CallForwarding') !== -1 && + phoneNumber.features?.includes('CallForwarding') && phoneNumber.phoneNumber ), this.numbers, diff --git a/packages/ringcentral-integration/modules/GenericMeeting/GenericMeeting.ts b/packages/ringcentral-integration/modules/GenericMeeting/GenericMeeting.ts index 930a5a72d2..5c9b84d20c 100644 --- a/packages/ringcentral-integration/modules/GenericMeeting/GenericMeeting.ts +++ b/packages/ringcentral-integration/modules/GenericMeeting/GenericMeeting.ts @@ -1,6 +1,5 @@ -import { EventEmitter } from 'events'; - import { action, RcModuleV2, state } from '@ringcentral-integration/core'; +import { EventEmitter } from 'events'; import type { RcVideoAPI, RcVMeetingModel } from '../../interfaces/Rcv.model'; import background from '../../lib/background'; @@ -13,6 +12,7 @@ import type { } from '../Meeting'; import type { RcVideo, RcVideoResponse } from '../RcVideo'; import { generateRandomPassword } from '../RcVideo'; + import type { Deps, ScheduledCallback, @@ -122,6 +122,7 @@ export class GenericMeeting extends RcModuleV2 { scheduleOriginalInfo: ScheduleModel; }; if (this.isRCM) { + // @ts-expect-error TS(2322): Type 'ScheduleMeetingResponse' is not assignable t... Remove this comment to see the full error message result = await this._deps.meeting.schedule( meeting as RcMMeetingModel, config, @@ -129,7 +130,9 @@ export class GenericMeeting extends RcModuleV2 { } else if (this.isRCV) { const rcvMeetingInfo = meeting as RcVMeetingModel; if (rcvMeetingInfo.usePersonalMeetingId) { + // @ts-expect-error TS(2322): Type 'RcVideoResponse | null' is not assignable to... Remove this comment to see the full error message result = await this._deps.rcVideo.updateMeeting( + // @ts-expect-error TS(2345): Argument of type 'string | null | undefined' is no... Remove this comment to see the full error message this._deps.rcVideo.personalMeeting?.id, rcvMeetingInfo, config, @@ -366,6 +369,7 @@ export class GenericMeeting extends RcModuleV2 { } get personalMeetingId(): string { + // @ts-expect-error TS(2322): Type 'string | undefined' is not assignable to typ... Remove this comment to see the full error message return (this.personalMeeting as Partial)?.shortId; } diff --git a/packages/ringcentral-integration/modules/GlipCompany/GlipCompany.ts b/packages/ringcentral-integration/modules/GlipCompany/GlipCompany.ts index 93dea7e9a5..fd8e9b5329 100644 --- a/packages/ringcentral-integration/modules/GlipCompany/GlipCompany.ts +++ b/packages/ringcentral-integration/modules/GlipCompany/GlipCompany.ts @@ -1,8 +1,9 @@ -import type GlipCompanyType from 'ringcentral-client/build/definitions/GlipCompany'; import { computed } from '@ringcentral-integration/core'; +import type GlipCompanyType from 'ringcentral-client/build/definitions/GlipCompany'; import { Module } from '../../lib/di'; import { DataFetcherV2Consumer, DataSource } from '../DataFetcherV2'; + import type { Deps } from './GlipCompany.interface'; @Module({ diff --git a/packages/ringcentral-integration/modules/GlipGroups/getReducer.ts b/packages/ringcentral-integration/modules/GlipGroups/getReducer.ts index 4d63ce3188..23df1ea94b 100644 --- a/packages/ringcentral-integration/modules/GlipGroups/getReducer.ts +++ b/packages/ringcentral-integration/modules/GlipGroups/getReducer.ts @@ -2,14 +2,16 @@ import { combineReducers } from 'redux'; import getModuleStatusReducer from '../../lib/getModuleStatusReducer'; -export function getDataReducer(types) { - return (state = [], { type, data, group }) => { +export function getDataReducer(types: any) { + return (state = [], { type, data, group }: any) => { switch (type) { case types.fetchSuccess: return data && data.records; case types.updateGroup: + // @ts-expect-error TS(2339): Property 'id' does not exist on type 'never'. return [group].concat(state.filter((g) => g.id !== group.id)); case types.removeGroup: + // @ts-expect-error TS(2339): Property 'id' does not exist on type 'never'. return state.filter((g) => g.id !== group.id); case types.resetSuccess: return []; @@ -19,8 +21,8 @@ export function getDataReducer(types) { }; } -export function getSearchFilterReducer(types) { - return (state = '', { type, searchFilter }) => { +export function getSearchFilterReducer(types: any) { + return (state = '', { type, searchFilter }: any) => { switch (type) { case types.updateFilter: if (searchFilter !== null && searchFilter !== undefined) { @@ -33,8 +35,8 @@ export function getSearchFilterReducer(types) { }; } -export function getCurrentGroupIdReducer(types) { - return (state = null, { type, groupId }) => { +export function getCurrentGroupIdReducer(types: any) { + return (state = null, { type, groupId }: any) => { switch (type) { case types.updateCurrentGroupId: return groupId; @@ -44,8 +46,8 @@ export function getCurrentGroupIdReducer(types) { }; } -export function getTimestampReducer(types) { - return (state = null, { type, timestamp }) => { +export function getTimestampReducer(types: any) { + return (state = null, { type, timestamp }: any) => { switch (type) { case types.fetchSuccess: return timestamp; @@ -57,7 +59,7 @@ export function getTimestampReducer(types) { }; } -export default function getReducer(types, reducers = {}) { +export default function getReducer(types: any, reducers = {}) { return combineReducers({ ...reducers, status: getModuleStatusReducer(types), diff --git a/packages/ringcentral-integration/modules/GlipGroups/index.ts b/packages/ringcentral-integration/modules/GlipGroups/index.ts index 06a8c41374..67df70a814 100644 --- a/packages/ringcentral-integration/modules/GlipGroups/index.ts +++ b/packages/ringcentral-integration/modules/GlipGroups/index.ts @@ -1,12 +1,13 @@ import { sleep } from '@ringcentral-integration/utils'; import moduleStatuses from '../../enums/moduleStatuses'; +import Pollable from '../../lib/Pollable'; import { Module } from '../../lib/di'; import ensureExist from '../../lib/ensureExist'; import { isBlank } from '../../lib/isBlank'; -import Pollable from '../../lib/Pollable'; import proxify from '../../lib/proxy/proxify'; import { selector } from '../../lib/selector'; + import { actionTypes } from './actionTypes'; import getReducer, { getDataReducer, getTimestampReducer } from './getReducer'; @@ -19,13 +20,13 @@ const DEFAULT_RETRY = 62 * 1000; const DEFAULT_RECORD_COUNT_PER_REQ = 250; const DEFAULT_PRELOAD_POSTS_DELAY_TTL = 800; -function formatGroup(group, personsMap, postsMap = {}, ownerId) { +function formatGroup(group: any, personsMap: any, postsMap = {}, ownerId: any) { if (!group || !group.id) { return {}; } - const detailMembers = []; + const detailMembers: any = []; if (group.members) { - group.members.forEach((memberId) => { + group.members.forEach((memberId: any) => { if (personsMap[memberId]) { detailMembers.push({ ...personsMap[memberId], @@ -39,6 +40,7 @@ function formatGroup(group, personsMap, postsMap = {}, ownerId) { detailMembers, updatedTime: new Date(group.lastModifiedTime).getTime(), }; + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message const latestPost = postsMap[group.id] && postsMap[group.id][0]; if (latestPost) { newGroup.latestPost = { @@ -53,14 +55,16 @@ function formatGroup(group, personsMap, postsMap = {}, ownerId) { return newGroup; } -function getUniqueMemberIds(groups) { - const memberIds = []; +function getUniqueMemberIds(groups: any) { + const memberIds: any = []; const memberIdsMap = {}; - groups.forEach((group) => { - group.members.forEach((memberId) => { + groups.forEach((group: any) => { + group.members.forEach((memberId: any) => { + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message if (memberIdsMap[memberId]) { return; } + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message memberIdsMap[memberId] = true; memberIds.push(memberId); }); @@ -68,7 +72,7 @@ function getUniqueMemberIds(groups) { return memberIds; } -function searchPosts(searchFilter, posts) { +function searchPosts(searchFilter: any, posts: any) { let result = false; for (const post of posts) { if (post.text && post.text.toLowerCase().indexOf(searchFilter) > -1) { @@ -77,7 +81,7 @@ function searchPosts(searchFilter, posts) { } if (post.mentions && post.mentions.length > 0) { const mentionNames = post.mentions - .map((m) => m.name) + .map((m: any) => m.name) .join(' ') .toLowerCase(); if (mentionNames.indexOf(searchFilter) > -1) { @@ -107,7 +111,31 @@ function searchPosts(searchFilter, posts) { { dep: 'GLipGroupsOptions', optional: true }, ], }) +// @ts-expect-error TS(2415): Class 'GlipGroups' incorrectly extends base class ... Remove this comment to see the full error message export default class GlipGroups extends Pollable { + _appFeatures: any; + _auth: any; + _client: any; + _connectivity: any; + _connectivityMonitor: any; + _dataStorageKey: any; + _glipPersons: any; + _glipPosts: any; + _lastMessage: any; + _perPage: any; + _polling: any; + _preloadPosts: any; + _preloadPostsDelayTtl: any; + _preloadedPosts: any; + _promise: any; + _readyCheckFn: any; + _recordCountPerReq: any; + _storage: any; + _subscription: any; + _subscriptionFilters: any; + _timeToRetry: any; + _timestampStorageKey: any; + _ttl: any; constructor({ auth, subscription, @@ -127,13 +155,16 @@ export default class GlipGroups extends Pollable { preloadPosts = true, preloadPostsDelayTtl = DEFAULT_PRELOAD_POSTS_DELAY_TTL, ...options - }) { + }: any) { super({ ...options, actionTypes, }); + // @ts-expect-error TS(2345): Argument of type 'string' is not assignable to par... Remove this comment to see the full error message this._auth = ensureExist.call(this, auth, 'auth'); + // @ts-expect-error TS(2345): Argument of type 'string' is not assignable to par... Remove this comment to see the full error message this._client = ensureExist.call(this, client, 'client'); + // @ts-expect-error TS(2345): Argument of type 'string' is not assignable to par... Remove this comment to see the full error message this._subscription = ensureExist.call(this, subscription, 'subscription'); this._appFeatures = appFeatures; this._connectivityMonitor = connectivityMonitor; @@ -180,14 +211,16 @@ export default class GlipGroups extends Pollable { } if (this._glipPosts) { - this._glipPosts.addNewPostListener((post) => this.onNewPost(post)); + this._glipPosts.addNewPostListener((post: any) => this.onNewPost(post)); } } + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message initialize() { this.store.subscribe(() => this._onStateChange()); } + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message async _onStateChange() { if (this._shouldInit()) { this.store.dispatch({ @@ -225,6 +258,7 @@ export default class GlipGroups extends Pollable { } } + // @ts-expect-error TS(4113): This member cannot have an 'override' modifier bec... Remove this comment to see the full error message override _shouldInit() { return !!( this._auth.loggedIn && @@ -240,6 +274,7 @@ export default class GlipGroups extends Pollable { ); } + // @ts-expect-error TS(4113): This member cannot have an 'override' modifier bec... Remove this comment to see the full error message override _shouldReset() { return !!( (!this._auth.loggedIn || @@ -271,11 +306,12 @@ export default class GlipGroups extends Pollable { } if (this._preloadPosts) { this._preloadedPosts = {}; + // @ts-expect-error TS(2554): Expected 1 arguments, but got 0. this._preloadGroupPosts(); } } - async _subscriptionHandleFn(message) { + async _subscriptionHandleFn(message: any) { if ( message && glipGroupRegExp.test(message.event) && @@ -343,22 +379,29 @@ export default class GlipGroups extends Pollable { this._subscriptionHandleFn(this._lastMessage); } - async _preloadGroupPosts(force) { + async _preloadGroupPosts(force: any) { const groups = this.groups.slice(0, 20); for (const group of groups) { if (!this._glipPosts) { return; } + // @ts-expect-error TS(2339): Property 'id' does not exist on type '(filteredGro... Remove this comment to see the full error message if (!this._preloadedPosts[group.id]) { + // @ts-expect-error TS(2339): Property 'id' does not exist on type '(filteredGro... Remove this comment to see the full error message this._preloadedPosts[group.id] = true; + // @ts-expect-error TS(2339): Property 'id' does not exist on type '(filteredGro... Remove this comment to see the full error message if (!this._glipPosts.postsMap[group.id] || force) { await sleep(this._preloadPostsDelayTtl); + // @ts-expect-error TS(2339): Property 'id' does not exist on type '(filteredGro... Remove this comment to see the full error message if (!this._glipPosts.postsMap[group.id] || force) { + // @ts-expect-error TS(2339): Property 'id' does not exist on type '(filteredGro... Remove this comment to see the full error message await this._glipPosts.fetchPosts(group.id); } } + // @ts-expect-error TS(2339): Property 'id' does not exist on type '(filteredGro... Remove this comment to see the full error message if (!this._glipPosts.readTimeMap[group.id]) { this._glipPosts.updateReadTime( + // @ts-expect-error TS(2339): Property 'id' does not exist on type '(filteredGro... Remove this comment to see the full error message group.id, Date.now() - 1000 * 3600 * 2, ); @@ -367,20 +410,23 @@ export default class GlipGroups extends Pollable { } } + // @ts-expect-error TS(2345): Argument of type 'TypedPropertyDescriptor<({ searc... Remove this comment to see the full error message @proxify - updateFilter({ searchFilter, pageNumber }) { + updateFilter({ searchFilter, pageNumber }: any) { this.store.dispatch({ type: this.actionTypes.updateFilter, searchFilter, pageNumber, }); if (this._preloadPosts && this.groups.length <= this._perPage * 2) { + // @ts-expect-error TS(2554): Expected 1 arguments, but got 0. this._preloadGroupPosts(); } } + // @ts-expect-error TS(2345): Argument of type 'TypedPropertyDescriptor<(groupId... Remove this comment to see the full error message @proxify - updateCurrentGroupId(groupId) { + updateCurrentGroupId(groupId: any) { if (!groupId) { return; } @@ -392,6 +438,7 @@ export default class GlipGroups extends Pollable { }); if (this._glipPersons) { this._glipPersons.loadPersons( + // @ts-expect-error TS(2339): Property 'members' does not exist on type '((allGr... Remove this comment to see the full error message this.currentGroup && this.currentGroup.members, ); } @@ -448,6 +495,7 @@ export default class GlipGroups extends Pollable { } @proxify + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message async fetchData() { if (!this._promise) { this._promise = this._fetchData(); @@ -456,7 +504,7 @@ export default class GlipGroups extends Pollable { } @proxify - async startChat(personId) { + async startChat(personId: any) { try { const group = await this._client .glip() @@ -481,13 +529,13 @@ export default class GlipGroups extends Pollable { return null; } - onNewPost(post) { + onNewPost(post: any) { if (post.groupId === this.currentGroupId && this._glipPosts) { this._glipPosts.updateReadTime(post.groupId); } } - async createTeam(name, members, type = 'Team') { + async createTeam(name: any, members: any, type = 'Team') { const group = await this._client.glip().groups().post({ type, name, @@ -498,53 +546,57 @@ export default class GlipGroups extends Pollable { return group.id; } + // @ts-expect-error TS(1240): Unable to resolve signature of property decorator ... Remove this comment to see the full error message @selector allGroups = [ () => this.data, () => this._glipPersons && this._glipPersons.personsMap, () => this._glipPosts && this._glipPosts.postsMap, () => this._auth.ownerId, - (data, personsMap = {}, postsMap = {}, ownerId) => - (data || []).map((group) => + (data: any, personsMap = {}, postsMap = {}, ownerId: any) => + (data || []).map((group: any) => formatGroup(group, personsMap, postsMap, ownerId), ), ]; + // @ts-expect-error TS(1240): Unable to resolve signature of property decorator ... Remove this comment to see the full error message @selector filteredGroups = [ () => this.allGroups, () => this.searchFilter, () => this._glipPosts && this._glipPosts.postsMap, - (allGroups, searchFilter, postsMap = {}) => { + (allGroups: any, searchFilter: any, postsMap = {}) => { if (isBlank(searchFilter)) { return allGroups; } const filterString = searchFilter.toLowerCase(); - return allGroups.filter((group) => { + return allGroups.filter((group: any) => { const name = group.name && group.name.toLowerCase(); if (name && name.indexOf(filterString) > -1) { return true; } if (!name) { const groupUsernames = group.detailMembers - .map((m) => `${m.firstName} ${m.lastName}`) + .map((m: any) => `${m.firstName} ${m.lastName}`) .join(' ') .toLowerCase(); if (groupUsernames && groupUsernames.indexOf(filterString) > -1) { return true; } } + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message const result = searchPosts(filterString, postsMap[group.id] || []); return result; }); }, ]; + // @ts-expect-error TS(1240): Unable to resolve signature of property decorator ... Remove this comment to see the full error message @selector groups = [ () => this.filteredGroups, - (filteredGroups) => { - const sortedGroups = filteredGroups.sort((a, b) => { + (filteredGroups: any) => { + const sortedGroups = filteredGroups.sort((a: any, b: any) => { if (a.updatedTime === b.updatedTime) return 0; return a.updatedTime > b.updatedTime ? -1 : 1; }); @@ -552,29 +604,33 @@ export default class GlipGroups extends Pollable { }, ]; + // @ts-expect-error TS(1240): Unable to resolve signature of property decorator ... Remove this comment to see the full error message @selector uniqueMemberIds = [() => this.allGroups, getUniqueMemberIds]; + // @ts-expect-error TS(1240): Unable to resolve signature of property decorator ... Remove this comment to see the full error message @selector groupMemberIds = [ () => this.allGroups, - (groups) => { - const noTeamGroups = groups.filter((g) => g.type !== 'Team'); + (groups: any) => { + const noTeamGroups = groups.filter((g: any) => g.type !== 'Team'); return getUniqueMemberIds(noTeamGroups); }, ]; + // @ts-expect-error TS(1240): Unable to resolve signature of property decorator ... Remove this comment to see the full error message @selector currentGroup = [ () => this.allGroups, () => this.currentGroupId, () => (this._glipPersons && this._glipPersons.personsMap) || {}, - (allGroups, currentGroupId, personsMap) => { - const group = allGroups.find((g) => g.id === currentGroupId) || {}; + (allGroups: any, currentGroupId: any, personsMap: any) => { + const group = allGroups.find((g: any) => g.id === currentGroupId) || {}; return formatGroup(group, personsMap, undefined, this._auth.ownerId); }, ]; + // @ts-expect-error TS(1240): Unable to resolve signature of property decorator ... Remove this comment to see the full error message @selector currentGroupPosts = [ () => { @@ -582,10 +638,10 @@ export default class GlipGroups extends Pollable { return postsMap[this.currentGroupId]; }, () => (this._glipPersons && this._glipPersons.personsMap) || {}, - (posts, personsMap) => { + (posts: any, personsMap: any) => { // const posts = postsMap[currentGroupId] || []; const reversePosts = (posts || []).slice(0).reverse(); - return reversePosts.map((post) => { + return reversePosts.map((post: any) => { const creator = personsMap[post.creatorId]; return { ...post, @@ -596,19 +652,20 @@ export default class GlipGroups extends Pollable { }, ]; + // @ts-expect-error TS(1240): Unable to resolve signature of property decorator ... Remove this comment to see the full error message @selector groupsWithUnread = [ () => this.groups, () => (this._glipPosts && this._glipPosts.postsMap) || {}, () => (this._glipPosts && this._glipPosts.readTimeMap) || {}, - (groups, postsMap, readTimeMap) => - groups.map((group) => { + (groups: any, postsMap: any, readTimeMap: any) => + groups.map((group: any) => { const posts = postsMap[group.id] || []; const readTime = readTimeMap[group.id] || Date.now(); return { ...group, unread: posts.filter( - (post) => + (post: any) => new Date(post.creationTime).getTime() > readTime && post.creatorId !== this._auth.ownerId, ).length, @@ -616,10 +673,11 @@ export default class GlipGroups extends Pollable { }), ]; + // @ts-expect-error TS(1240): Unable to resolve signature of property decorator ... Remove this comment to see the full error message @selector unreadCounts = [ () => this.groupsWithUnread, - (groups) => groups.reduce((a, b) => a + b.unread, 0), + (groups: any) => groups.reduce((a: any, b: any) => a + b.unread, 0), ]; get searchFilter() { @@ -632,6 +690,7 @@ export default class GlipGroups extends Pollable { : this.state.data; } + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message get timestamp() { return this._storage ? this._storage.getItem(this._timestampStorageKey) @@ -642,22 +701,27 @@ export default class GlipGroups extends Pollable { return this.state.currentGroupId; } + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message get status() { return this.state.status; } + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message get ready() { return this.status === moduleStatuses.ready; } + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message get pending() { return this.status === moduleStatuses.pending; } + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message get ttl() { return this._ttl; } + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message get timeToRetry() { return this._timeToRetry; } diff --git a/packages/ringcentral-integration/modules/GlipPersons/getReducer.ts b/packages/ringcentral-integration/modules/GlipPersons/getReducer.ts index 05b8f60275..bdea535934 100644 --- a/packages/ringcentral-integration/modules/GlipPersons/getReducer.ts +++ b/packages/ringcentral-integration/modules/GlipPersons/getReducer.ts @@ -1,10 +1,11 @@ import { combineReducers } from 'redux'; import getModuleStatusReducer from '../../lib/getModuleStatusReducer'; + import status from './status'; -export function getGlipPersonsStatusReducer(types) { - return (state = status.idle, { type }) => { +export function getGlipPersonsStatusReducer(types: any) { + return (state = status.idle, { type }: any) => { switch (type) { case types.fetch: return status.fetching; @@ -18,21 +19,22 @@ export function getGlipPersonsStatusReducer(types) { }; } -export function getGlipPersonStoreReducer(types) { - return (state = {}, { type, person, persons }) => { +export function getGlipPersonStoreReducer(types: any) { + return (state = {}, { type, person, persons }: any) => { let newState; switch (type) { case types.fetchSuccess: newState = { ...state, }; + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message newState[person.id] = person; return newState; case types.batchFetchSuccess: newState = { ...state, }; - persons.forEach((p) => { + persons.forEach((p: any) => { if (p.id) { newState[p.id] = p; } @@ -47,7 +49,7 @@ export function getGlipPersonStoreReducer(types) { }; } -export default function getGlipPostsReducer(types, reducers = {}) { +export default function getGlipPostsReducer(types: any, reducers = {}) { return combineReducers({ ...reducers, status: getModuleStatusReducer(types), diff --git a/packages/ringcentral-integration/modules/GlipPersons/index.ts b/packages/ringcentral-integration/modules/GlipPersons/index.ts index 599bdbff47..b6428b1a75 100644 --- a/packages/ringcentral-integration/modules/GlipPersons/index.ts +++ b/packages/ringcentral-integration/modules/GlipPersons/index.ts @@ -1,11 +1,12 @@ import { sleep } from '@ringcentral-integration/utils'; import moduleStatuses from '../../enums/moduleStatuses'; +import RcModule from '../../lib/RcModule'; import { batchGetApi } from '../../lib/batchApiHelper'; import { Module } from '../../lib/di'; import ensureExist from '../../lib/ensureExist'; import proxify from '../../lib/proxy/proxify'; -import RcModule from '../../lib/RcModule'; + import { actionTypes } from './actionTypes'; import getReducer, { getGlipPersonStoreReducer } from './getReducer'; @@ -22,7 +23,16 @@ const DEFAULT_BATCH_FETCH_DELAY = 500; { dep: 'GlipPersonsOptions', optional: true }, ], }) +// @ts-expect-error TS(2415): Class 'GlipPersons' incorrectly extends base class... Remove this comment to see the full error message export default class GlipPersons extends RcModule { + _appFeatures: any; + _auth: any; + _batchFetchDelay: any; + _client: any; + _dataStorageKey: any; + _fetchingIds: any; + _storage: any; + _tabManager: any; constructor({ client, auth, @@ -31,14 +41,16 @@ export default class GlipPersons extends RcModule { appFeatures, batchFetchDelay = DEFAULT_BATCH_FETCH_DELAY, ...options - }) { + }: any) { super({ ...options, actionTypes, }); this._appFeatures = appFeatures; + // @ts-expect-error TS(2345): Argument of type 'string' is not assignable to par... Remove this comment to see the full error message this._client = ensureExist.call(this, client, 'client'); + // @ts-expect-error TS(2345): Argument of type 'string' is not assignable to par... Remove this comment to see the full error message this._auth = ensureExist.call(this, auth, 'auth'); this._tabManager = tabManager; this._storage = storage; @@ -60,10 +72,12 @@ export default class GlipPersons extends RcModule { } } + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message initialize() { this.store.subscribe(() => this._onStateChange()); } + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message async _onStateChange() { if (this._shouldInit()) { this.store.dispatch({ @@ -86,6 +100,7 @@ export default class GlipPersons extends RcModule { } } + // @ts-expect-error TS(4113): This member cannot have an 'override' modifier bec... Remove this comment to see the full error message override _shouldInit() { return ( this._auth.loggedIn && @@ -96,6 +111,7 @@ export default class GlipPersons extends RcModule { ); } + // @ts-expect-error TS(4113): This member cannot have an 'override' modifier bec... Remove this comment to see the full error message override _shouldReset() { return ( ((this._storage && !this._storage.ready) || @@ -112,7 +128,7 @@ export default class GlipPersons extends RcModule { } @proxify - async loadPerson(id) { + async loadPerson(id: any) { try { this.store.dispatch({ type: this.actionTypes.fetch, @@ -130,7 +146,7 @@ export default class GlipPersons extends RcModule { } @proxify - async loadPersons(personIds) { + async loadPersons(personIds: any) { if (!this._auth.loggedIn) { return; } @@ -138,8 +154,8 @@ export default class GlipPersons extends RcModule { return; } const { ownerId } = this._auth; - const newPersonIds = []; - personIds.forEach((id) => { + const newPersonIds: any = []; + personIds.forEach((id: any) => { if (!this.personsMap[id] && !this._fetchingIds[id]) { newPersonIds.push(id); } @@ -148,6 +164,7 @@ export default class GlipPersons extends RcModule { return; } const ids = newPersonIds.slice(0, MaximumBatchGetPersons); + // @ts-expect-error TS(7006): Parameter 'id' implicitly has an 'any' type. ids.forEach((id) => { this._fetchingIds[id] = 1; }); @@ -160,6 +177,7 @@ export default class GlipPersons extends RcModule { type: this.actionTypes.batchFetchSuccess, persons, }); + // @ts-expect-error TS(7006): Parameter 'id' implicitly has an 'any' type. ids.forEach((id) => { delete this._fetchingIds[id]; }); @@ -167,6 +185,7 @@ export default class GlipPersons extends RcModule { this.store.dispatch({ type: this.actionTypes.fetchError, }); + // @ts-expect-error TS(7006): Parameter 'id' implicitly has an 'any' type. ids.forEach((id) => { delete this._fetchingIds[id]; }); @@ -181,7 +200,7 @@ export default class GlipPersons extends RcModule { } } - async _batchGetPersons(personIds) { + async _batchGetPersons(personIds: any) { if (!personIds || personIds.length === 0) { return []; } @@ -195,11 +214,12 @@ export default class GlipPersons extends RcModule { url: `/restapi/v1.0/glip/persons/${ids}`, }); const responses = await Promise.all( - multipartResponse.filter((r) => r.ok).map((x) => x.json()), + multipartResponse.filter((r: any) => r.ok).map((x: any) => x.json()), ); return responses; } + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message get _actionTypes() { return actionTypes; } @@ -211,10 +231,12 @@ export default class GlipPersons extends RcModule { return this.state.glipPersonStore; } + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message get status() { return this.state.status; } + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message get ready() { return this.status === moduleStatuses.ready; } diff --git a/packages/ringcentral-integration/modules/GlipPosts/getReducer.ts b/packages/ringcentral-integration/modules/GlipPosts/getReducer.ts index ae983b2685..faf5022934 100644 --- a/packages/ringcentral-integration/modules/GlipPosts/getReducer.ts +++ b/packages/ringcentral-integration/modules/GlipPosts/getReducer.ts @@ -1,10 +1,11 @@ import { combineReducers } from 'redux'; import getModuleStatusReducer from '../../lib/getModuleStatusReducer'; + import status from './status'; -export function getGlipPostsStatusReducer(types) { - return (state = status.idle, { type }) => { +export function getGlipPostsStatusReducer(types: any) { + return (state = status.idle, { type }: any) => { switch (type) { case types.fetch: return status.fetching; @@ -17,8 +18,8 @@ export function getGlipPostsStatusReducer(types) { }; } -export function getGlipPostsCreateStatusReducer(types) { - return (state = status.idle, { type }) => { +export function getGlipPostsCreateStatusReducer(types: any) { + return (state = status.idle, { type }: any) => { switch (type) { case types.create: return status.creating; @@ -31,10 +32,18 @@ export function getGlipPostsCreateStatusReducer(types) { }; } -export function getGlipPostsStoreReducer(types) { +export function getGlipPostsStoreReducer(types: any) { return ( state = {}, - { type, groupId, records, record, oldRecordId, isSendByMe, lastPageToken }, + { + type, + groupId, + records, + record, + oldRecordId, + isSendByMe, + lastPageToken, + }: any, ) => { let newState; let newPosts; @@ -45,9 +54,12 @@ export function getGlipPostsStoreReducer(types) { ...state, }; if (!lastPageToken) { + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message newState[groupId] = records; } else { + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message const preRecords = newState[groupId]; + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message newState[groupId] = [].concat(preRecords).concat(records); } return newState; @@ -57,30 +69,34 @@ export function getGlipPostsStoreReducer(types) { newState = { ...state, }; + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message newPosts = (newState[groupId] && [...newState[groupId]]) || []; if (oldRecordId) { - oldPostIndex = newPosts.findIndex((p) => p.id === oldRecordId); + oldPostIndex = newPosts.findIndex((p: any) => p.id === oldRecordId); } else { - oldPostIndex = newPosts.findIndex((p) => p.id === record.id); + oldPostIndex = newPosts.findIndex((p: any) => p.id === record.id); } if (oldPostIndex > -1) { newPosts.splice(oldPostIndex, 1, record); + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message newState[groupId] = newPosts; } else if (isSendByMe) { oldPostIndex = newPosts.findIndex( - (p) => + (p: any) => p.creatorId === record.creatorId && p.text === record.text && p.sendStatus === status.creating, ); if (oldPostIndex === -1) { + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message newState[groupId] = [record].concat( - newPosts.filter((p) => p.id !== record.id), + newPosts.filter((p: any) => p.id !== record.id), ); } } else { + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message newState[groupId] = [record].concat( - newPosts.filter((p) => p.id !== record.id), + newPosts.filter((p: any) => p.id !== record.id), ); } return newState; @@ -92,14 +108,15 @@ export function getGlipPostsStoreReducer(types) { }; } -export function getGlipPostsInputsReducer(types) { - return (state = {}, { type, groupId, textValue, mentions }) => { +export function getGlipPostsInputsReducer(types: any) { + return (state = {}, { type, groupId, textValue, mentions }: any) => { let newState; switch (type) { case types.updatePostInput: newState = { ...state, }; + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message newState[groupId] = { text: textValue, mentions, @@ -111,14 +128,15 @@ export function getGlipPostsInputsReducer(types) { }; } -export function getGlipPostsReadTimeReducer(types) { - return (state = {}, { type, groupId, time = Date.now() }) => { +export function getGlipPostsReadTimeReducer(types: any) { + return (state = {}, { type, groupId, time = Date.now() }: any) => { let newState; switch (type) { case types.updateReadTime: newState = { ...state, }; + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message newState[groupId] = time; return newState; case types.resetSuccess: @@ -129,14 +147,15 @@ export function getGlipPostsReadTimeReducer(types) { }; } -export function getGlipPostsPageInfoReducer(types) { - return (state = {}, { type, groupId, navigation }) => { +export function getGlipPostsPageInfoReducer(types: any) { + return (state = {}, { type, groupId, navigation }: any) => { let newState; switch (type) { case types.fetchSuccess: newState = { ...state, }; + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message newState[groupId] = navigation; return newState; case types.resetSuccess: @@ -147,14 +166,15 @@ export function getGlipPostsPageInfoReducer(types) { }; } -export function getGlipPostsFetchTimeReducer(types) { - return (state = {}, { type, groupId }) => { +export function getGlipPostsFetchTimeReducer(types: any) { + return (state = {}, { type, groupId }: any) => { let newState; switch (type) { case types.fetchSuccess: newState = { ...state, }; + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message newState[groupId] = Date.now(); return newState; case types.resetSuccess: @@ -165,7 +185,7 @@ export function getGlipPostsFetchTimeReducer(types) { }; } -export default function getGlipPostsReducer(types, reducers = {}) { +export default function getGlipPostsReducer(types: any, reducers = {}) { return combineReducers({ ...reducers, status: getModuleStatusReducer(types), diff --git a/packages/ringcentral-integration/modules/GlipPosts/index.ts b/packages/ringcentral-integration/modules/GlipPosts/index.ts index 4538aba7ef..0a61992157 100644 --- a/packages/ringcentral-integration/modules/GlipPosts/index.ts +++ b/packages/ringcentral-integration/modules/GlipPosts/index.ts @@ -1,9 +1,10 @@ import moduleStatuses from '../../enums/moduleStatuses'; +import RcModule from '../../lib/RcModule'; import { Module } from '../../lib/di'; import ensureExist from '../../lib/ensureExist'; import { isBlank } from '../../lib/isBlank'; import proxify from '../../lib/proxy/proxify'; -import RcModule from '../../lib/RcModule'; + import { actionTypes } from './actionTypes'; import getReducer, { getGlipPostsReadTimeReducer } from './getReducer'; import { status } from './status'; @@ -25,7 +26,18 @@ const DEFAULT_LOAD_TTL = 30 * 60 * 1000; { dep: 'GlipPostsOptions', optional: true }, ], }) +// @ts-expect-error TS(2415): Class 'GlipPosts' incorrectly extends base class '... Remove this comment to see the full error message export default class GlipPosts extends RcModule { + _auth: any; + _client: any; + _extensionFeatures: any; + _fetchPromises: any; + _lastMessage: any; + _loadTtl: any; + _newPostListeners: any; + _readTimeStorageKey: any; + _storage: any; + _subscription: any; constructor({ client, auth, @@ -34,16 +46,19 @@ export default class GlipPosts extends RcModule { extensionFeatures, loadTtl = DEFAULT_LOAD_TTL, ...options - }) { + }: any) { super({ ...options, actionTypes, }); this._reducer = getReducer(this.actionTypes); + // @ts-expect-error TS(2345): Argument of type 'string' is not assignable to par... Remove this comment to see the full error message this._client = ensureExist.call(this, client, 'client'); + // @ts-expect-error TS(2345): Argument of type 'string' is not assignable to par... Remove this comment to see the full error message this._auth = ensureExist.call(this, auth, 'auth'); this._extensionFeatures = extensionFeatures; + // @ts-expect-error TS(2345): Argument of type 'string' is not assignable to par... Remove this comment to see the full error message this._subscription = ensureExist.call(this, subscription, 'subscription'); this._fetchPromises = {}; this._lastMessage = null; @@ -58,16 +73,18 @@ export default class GlipPosts extends RcModule { this._newPostListeners = []; } - addNewPostListener(listen) { + addNewPostListener(listen: any) { if (typeof listen === 'function') { this._newPostListeners.push(listen); } } + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message initialize() { this.store.subscribe(() => this._onStateChange()); } + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message async _onStateChange() { if (this._shouldInit()) { this.store.dispatch({ @@ -88,6 +105,7 @@ export default class GlipPosts extends RcModule { } } + // @ts-expect-error TS(4113): This member cannot have an 'override' modifier bec... Remove this comment to see the full error message override _shouldInit() { return ( this._auth.loggedIn && @@ -97,6 +115,7 @@ export default class GlipPosts extends RcModule { ); } + // @ts-expect-error TS(4113): This member cannot have an 'override' modifier bec... Remove this comment to see the full error message override _shouldReset() { return ( (!this._auth.loggedIn || @@ -141,7 +160,7 @@ export default class GlipPosts extends RcModule { post.creatorId === this._auth.ownerId && eventType === 'PostAdded', }); if (eventType === 'PostAdded' && post.creatorId !== this._auth.ownerId) { - this._newPostListeners.forEach((listen) => { + this._newPostListeners.forEach((listen: any) => { listen(post); }); } @@ -149,17 +168,18 @@ export default class GlipPosts extends RcModule { } @proxify - async loadPosts(groupId, recordCount = 20) { + async loadPosts(groupId: any, recordCount = 20) { const lastPosts = this.postsMap[groupId]; const fetchTime = this.fetchTimeMap[groupId]; if (lastPosts && fetchTime && Date.now() - fetchTime < this._loadTtl) { return; } + // @ts-expect-error TS(2554): Expected 3 arguments, but got 2. await this.fetchPosts(groupId, recordCount); } @proxify - async fetchPosts(groupId, recordCount = 20, pageToken) { + async fetchPosts(groupId: any, recordCount = 20, pageToken: any) { if (!groupId) { return; } @@ -171,6 +191,7 @@ export default class GlipPosts extends RcModule { }); const params = { recordCount }; if (pageToken) { + // @ts-expect-error TS(2339): Property 'pageToken' does not exist on type '{ rec... Remove this comment to see the full error message params.pageToken = pageToken; } const response = await this._client @@ -198,7 +219,7 @@ export default class GlipPosts extends RcModule { } @proxify - async loadNextPage(groupId, recordCount) { + async loadNextPage(groupId: any, recordCount: any) { const pageInfo = this.pageInfos[groupId]; const pageToken = pageInfo && pageInfo.prevPageToken; if (!pageToken) { @@ -208,7 +229,7 @@ export default class GlipPosts extends RcModule { } @proxify - async create({ groupId }) { + async create({ groupId }: any) { let text = this.postInputs[groupId] && this.postInputs[groupId].text; const mentions = this.postInputs[groupId] && this.postInputs[groupId].mentions; @@ -216,7 +237,7 @@ export default class GlipPosts extends RcModule { return; } if (mentions && mentions.length > 0) { - mentions.forEach((mention) => { + mentions.forEach((mention: any) => { if (!mention.matcherId) { return; } @@ -253,6 +274,7 @@ export default class GlipPosts extends RcModule { oldRecordId: fakeId, }); } catch (e: any /** TODO: confirm with instanceof */) { + // @ts-expect-error TS(2339): Property 'createError' does not exist on type 'Obj... Remove this comment to see the full error message fakeRecord.sendStatus = status.createError; this.store.dispatch({ type: this.actionTypes.createError, @@ -265,7 +287,7 @@ export default class GlipPosts extends RcModule { } @proxify - async sendFile({ fileName, groupId, rawFile }) { + async sendFile({ fileName, groupId, rawFile }: any) { try { const platform = this._client.service.platform(); const body = rawFile; @@ -286,8 +308,9 @@ export default class GlipPosts extends RcModule { return null; } + // @ts-expect-error TS(2345): Argument of type 'TypedPropertyDescriptor<(groupId... Remove this comment to see the full error message @proxify - updateReadTime(groupId, time) { + updateReadTime(groupId: any, time: any) { this.store.dispatch({ type: this.actionTypes.updateReadTime, groupId, @@ -295,8 +318,9 @@ export default class GlipPosts extends RcModule { }); } + // @ts-expect-error TS(2345): Argument of type 'TypedPropertyDescriptor<({ text,... Remove this comment to see the full error message @proxify - updatePostInput({ text, groupId, mentions }) { + updatePostInput({ text, groupId, mentions }: any) { this.store.dispatch({ type: this.actionTypes.updatePostInput, groupId, @@ -309,10 +333,12 @@ export default class GlipPosts extends RcModule { return this.state.glipPostsStore; } + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message get status() { return this.state.status; } + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message get ready() { return this.status === moduleStatuses.ready; } diff --git a/packages/ringcentral-integration/modules/GlobalStorage/GlobalStorage.ts b/packages/ringcentral-integration/modules/GlobalStorage/GlobalStorage.ts index 045876599c..ccb23b1da4 100644 --- a/packages/ringcentral-integration/modules/GlobalStorage/GlobalStorage.ts +++ b/packages/ringcentral-integration/modules/GlobalStorage/GlobalStorage.ts @@ -1,7 +1,8 @@ // @ts-nocheck -import { Module } from '../../lib/di'; import type { IStorage } from '../../lib/StorageBase'; import { StorageBase } from '../../lib/StorageBase'; +import { Module } from '../../lib/di'; + import type { Deps } from './GlobalStorage.interface'; @Module({ diff --git a/packages/ringcentral-integration/modules/Locale/Locale.ts b/packages/ringcentral-integration/modules/Locale/Locale.ts index 03fd919861..6d0692e362 100644 --- a/packages/ringcentral-integration/modules/Locale/Locale.ts +++ b/packages/ringcentral-integration/modules/Locale/Locale.ts @@ -8,11 +8,13 @@ import I18n, { DEFAULT_LOCALE, PSEUDO_LOCALE, } from '@ringcentral-integration/i18n'; +import { setDayjsLocale } from '@ringcentral-integration/i18n-dayjs'; import formatLocale from '@ringcentral-integration/i18n/lib/formatLocale'; import detectBrowserLocale from '../../lib/detectBrowserLocale'; import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; + import type { Deps } from './Locale.interface'; @Module({ @@ -129,7 +131,9 @@ export class Locale extends RcModuleV2 { } protected async _setLocale(locale: string | null) { - await I18n.setLocale(locale); + const toLocale = locale || this.defaultLocale; + await I18n.setLocale(toLocale); + setDayjsLocale(toLocale); } normalizeLocale(inputLocale: string) { diff --git a/packages/ringcentral-integration/modules/LocaleSettings/LocaleSettings.ts b/packages/ringcentral-integration/modules/LocaleSettings/LocaleSettings.ts index b7c189fa80..9829b208cf 100644 --- a/packages/ringcentral-integration/modules/LocaleSettings/LocaleSettings.ts +++ b/packages/ringcentral-integration/modules/LocaleSettings/LocaleSettings.ts @@ -8,6 +8,7 @@ import { DEFAULT_LOCALE } from '@ringcentral-integration/i18n'; import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; + import type { Deps } from './LocaleSettings.interface'; @Module({ @@ -35,6 +36,7 @@ export class LocaleSettings extends RcModuleV2 { @globalStorage @state + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'string'. savedLocale: string = null; @action diff --git a/packages/ringcentral-integration/modules/Meeting/Meeting.interface.ts b/packages/ringcentral-integration/modules/Meeting/Meeting.interface.ts index 2d990fc770..dfe7a5ec0b 100644 --- a/packages/ringcentral-integration/modules/Meeting/Meeting.interface.ts +++ b/packages/ringcentral-integration/modules/Meeting/Meeting.interface.ts @@ -1,5 +1,5 @@ -import type Client from 'ringcentral-client'; import type TimezoneInfo from '@rc-ex/core/lib/definitions/TimezoneInfo'; +import type Client from 'ringcentral-client'; import type { MeetingTypeV } from '../../helpers/meetingHelper.interface'; import type { Alert } from '../Alert'; diff --git a/packages/ringcentral-integration/modules/Meeting/Meeting.ts b/packages/ringcentral-integration/modules/Meeting/Meeting.ts index 1e83b64185..7b454c5d59 100644 --- a/packages/ringcentral-integration/modules/Meeting/Meeting.ts +++ b/packages/ringcentral-integration/modules/Meeting/Meeting.ts @@ -1,7 +1,3 @@ -import dayjs from 'dayjs'; -import utc from 'dayjs/plugin/utc'; -import { filter, find, isEmpty, pick } from 'ramda'; - import type MeetingServiceInfoResource from '@rc-ex/core/lib/definitions/MeetingServiceInfoResource'; import { action, @@ -12,6 +8,9 @@ import { track, } from '@ringcentral-integration/core'; import { DEFAULT_LOCALE } from '@ringcentral-integration/i18n'; +import dayjs from 'dayjs'; +import utc from 'dayjs/plugin/utc'; +import { filter, find, isEmpty, pick } from 'ramda'; import { trackEvents } from '../../enums/trackEvents'; import { @@ -32,21 +31,7 @@ import background from '../../lib/background'; import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; import type { Analytics } from '../AnalyticsV2'; -import { - ASSISTED_USERS_MYSELF, - COMMON_SETTINGS, - DEFAULT_LOCK_SETTINGS, - LAST_MEETING_SETTINGS, - PMIRequirePassword, - RCM_PASSWORD_REGEX, - SAVED_DEFAULT_MEETING_SETTINGS, -} from './constants'; -import { - getExtensionName, - getHostId, - getRcmUriRegExp, - getRcvUriRegExp, -} from './helper'; + import type { CommonPersonalMeetingSettings, DefaultScheduleLockedSettings, @@ -71,6 +56,21 @@ import type { UserSettings, UserTelephonySettingResponse, } from './Meeting.interface'; +import { + ASSISTED_USERS_MYSELF, + COMMON_SETTINGS, + DEFAULT_LOCK_SETTINGS, + LAST_MEETING_SETTINGS, + PMIRequirePassword, + RCM_PASSWORD_REGEX, + SAVED_DEFAULT_MEETING_SETTINGS, +} from './constants'; +import { + getExtensionName, + getHostId, + getRcmUriRegExp, + getRcvUriRegExp, +} from './helper'; import { MeetingErrors } from './meetingErrors'; import { meetingStatus } from './meetingStatus'; @@ -94,15 +94,15 @@ export class Meeting extends RcModuleV2 implements IMeeting { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Timeout'. protected _fetchDelegatorsTimeout: NodeJS.Timeout = null; - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Timeout'. private _fetchPersonMeetingTimeout: NodeJS.Timeout = null; - // @ts-expect-error + // @ts-expect-error TS(2564): Property '_createMeetingPromise' has no initialize... Remove this comment to see the full error message private _createMeetingPromise: Promise; constructor(deps: Deps) { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'Deps' is not assignable to type 'T & { stora... Remove this comment to see the full error message super({ deps, enableCache: true, storageKey: 'Meeting' }); } @@ -144,7 +144,7 @@ export class Meeting isPreferencesChanged = false; get extensionName(): string { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'string | undefined' is not assignable to typ... Remove this comment to see the full error message return this._deps.extensionInfo.info?.name; } @@ -172,7 +172,7 @@ export class Meeting that.scheduleUserSettings.usePmiForScheduledMeetings, ]) get usePmiDefaultFromSW(): boolean { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'boolean | undefined' is not assignable to ty... Remove this comment to see the full error message return ( this.enablePersonalMeeting && this.enableServiceWebSettings && @@ -234,7 +234,7 @@ export class Meeting get pmiDefaultSettings(): RcMMeetingModel { if (!this.enableServiceWebSettings) { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'RcMMeetingModel | null' is not assignable to... Remove this comment to see the full error message return this.personalMeeting; } @@ -304,17 +304,17 @@ export class Meeting getInitialMeetingSetting() { const meetingName = getExtensionName({ extensionInfo: this.extensionInfo, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'boolean | undefined' is not assignable to ty... Remove this comment to see the full error message enableScheduleOnBehalf: this.enableScheduleOnBehalf, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'RcMMeetingModel | null' is not assignable to... Remove this comment to see the full error message meeting: this.meeting, delegators: this.delegators, }); const startTime = getInitializedStartTime(); const hostId = getHostId({ - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'boolean | undefined' is not assignable to ty... Remove this comment to see the full error message enableScheduleOnBehalf: this.enableScheduleOnBehalf, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'RcMMeetingModel | null' is not assignable to... Remove this comment to see the full error message meeting: this.meeting, extensionInfo: this.extensionInfo, }); @@ -335,7 +335,7 @@ export class Meeting } get initialMeetingSetting(): RcMMeetingModel { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'Partial' is not assignable ... Remove this comment to see the full error message return this.getInitialMeetingSetting(); } @@ -364,7 +364,7 @@ export class Meeting } // will follow dynamic brand config - // @ts-expect-error + // @ts-expect-error TS(2416): Property 'enableScheduleOnBehalf' in type 'Meeting... Remove this comment to see the full error message get enableScheduleOnBehalf() { return ( this._deps.brand.brandConfig?.enableRcmScheduleOnBehalf ?? @@ -428,7 +428,7 @@ export class Meeting this.savedDefaultMeetingSetting = savedDefaultMeetingSetting; } - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type '(that: Meeting, isScheduling: bo... Remove this comment to see the full error message @track((that: Meeting, isScheduling: boolean) => { if (!isScheduling) return; return (analytics: Analytics) => { @@ -446,7 +446,7 @@ export class Meeting this.isScheduling = isScheduling; } - // @ts-expect-error + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message protected async onInit() { await this._init(); } @@ -526,7 +526,7 @@ export class Meeting }, }); - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'RcMMeetingModel | null' is not a... Remove this comment to see the full error message this.updatePreferences(prunePreferencesObject(this.meeting)); } @@ -559,7 +559,7 @@ export class Meeting private _comparePreferences(meeting: RcMMeetingModel) { this.updateIsPreferencesChanged( - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'Partial' is not ass... Remove this comment to see the full error message comparePreferences(this.preferences, meeting), ); } @@ -614,7 +614,7 @@ export class Meeting } try { const meetingInfoResponse = await this.fetchPersonalMeeting(extensionId); - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type '{ _requireMeetingPassword: boole... Remove this comment to see the full error message const meeting = this.formatPersonalMeeting(meetingInfoResponse); this.updatePersonalMeeting(meeting); } catch (e: any /** TODO: confirm with instanceof */) { @@ -640,7 +640,7 @@ export class Meeting this.getLockedSettings(), ]); this.updateUserSettings(userSettings); - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type '{ recording: any; scheduleMeetin... Remove this comment to see the full error message this.updateLockedSettings(lockedSettings); } catch (e: any /** TODO: confirm with instanceof */) { console.error('error:', e); @@ -661,7 +661,7 @@ export class Meeting const formattedMeeting = this._format(meeting); this.updateSavedDefaultMeetingSetting({ ...formattedMeeting, - // @ts-expect-error + // @ts-expect-error TS(2339): Property 'notShowAgain' does not exist on type 'Rc... Remove this comment to see the full error message _saved: meeting.notShowAgain, _requireMeetingPassword: meeting._requireMeetingPassword, }); @@ -673,35 +673,35 @@ export class Meeting { isAlertSuccess = true }: { isAlertSuccess?: boolean } = {}, ): Promise { try { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'RcMMeetingModel | null' is not assignable to... Remove this comment to see the full error message meeting = meeting || this.meeting; this.updateIsScheduling(true); // Validate meeting - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'RcMMeetingModel | undefined' is ... Remove this comment to see the full error message this._validate(meeting); - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'RcMMeetingModel | undefined' is ... Remove this comment to see the full error message const formattedMeeting = this._format(meeting); - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. if (this.showSaveAsDefault && meeting.saveAsDefault) { - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'RcMMeetingModel | undefined' is ... Remove this comment to see the full error message this.saveAsDefaultSetting(meeting); } const [resp, serviceInfo] = await Promise.all([ this.postMeeting(formattedMeeting), - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. this.getMeetingServiceInfo(meeting.host?.id), ]); const invitationInfo = await this.getMeetingInvitation( - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message resp.id, this.currentLocale, ); this.updateLastMeetingSetting({ ...formattedMeeting, - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. _saved: meeting._saved, }); @@ -717,18 +717,18 @@ export class Meeting } // Update personal meeting setting - // @ts-expect-error + // @ts-expect-error TS(2339): Property 'usePersonalMeetingId' does not exist on ... Remove this comment to see the full error message if (this.enablePersonalMeeting && resp.usePersonalMeetingId) { this.updatePersonalMeeting( this.formatPersonalMeeting( - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'MeetingResponseResource' is not ... Remove this comment to see the full error message resp, - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. serviceInfo.externalUserInfo.personalMeetingId, ), ); if (this.enableServiceWebSettings) { - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type '{ _pmiPassword: string | undefin... Remove this comment to see the full error message this.update({ ...this.meeting, _pmiPassword: resp.password, @@ -744,11 +744,11 @@ export class Meeting }); }, 50); } - // @ts-expect-error + // @ts-expect-error TS(2322): Type '{ meeting: any; serviceInfo: { mobileDialing... Remove this comment to see the full error message return result; } catch (errors) { await this._errorHandle(errors); - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'ScheduleMee... Remove this comment to see the full error message return null; } finally { this.updateIsScheduling(false); @@ -767,7 +767,7 @@ export class Meeting }); const result = await this._createMeetingPromise; - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Promise (this.updateMeeting as any)._promise = Promise.all([ this.putMeeting(meetingId, formattedMeeting), - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'string | null' is not assignable... Remove this comment to see the full error message this.getMeetingServiceInfo(meeting.host?.id), ]); @@ -824,7 +824,7 @@ export class Meeting ), ); if (this.enableServiceWebSettings) { - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type '{ _pmiPassword: any; allowJoinBe... Remove this comment to see the full error message this.update({ ...this.meeting, _pmiPassword: resp.password, @@ -906,7 +906,7 @@ export class Meeting @proxify async resetPersonalMeeting() { - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'null' is not assignable to param... Remove this comment to see the full error message this._updatePersonalMeeting(null); } @@ -952,7 +952,7 @@ export class Meeting @proxify private async fetchPersonalMeeting(extensionId?: string) { const serviceInfo = await this.getMeetingServiceInfo(extensionId); - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. const personalMeetingId = serviceInfo.externalUserInfo.personalMeetingId; const meetingInfoResponse = await this.getMeeting(personalMeetingId); if (!meetingInfoResponse) { @@ -980,7 +980,7 @@ export class Meeting .account() .extension() .meeting() - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'RcMMeetingModel' is not assignab... Remove this comment to see the full error message .post(formattedMeeting) ); } @@ -992,7 +992,7 @@ export class Meeting .account() .extension() .meeting(meetingId) - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'RcMMeetingModel' is not assignab... Remove this comment to see the full error message .put(formattedMeeting) ); } @@ -1050,7 +1050,7 @@ export class Meeting return apiResponse.json(); } catch (e: any /** TODO: confirm with instanceof */) { console.warn('failed to get user setting', e); - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'UserSetting... Remove this comment to see the full error message return null; } } @@ -1110,7 +1110,7 @@ export class Meeting message: meetingStatus.renderInviteError, }); } - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'RcmInvitati... Remove this comment to see the full error message return null; } } @@ -1149,7 +1149,7 @@ export class Meeting errors.push(meetingStatus.noPassword); } if (schedule) { - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. if (schedule.durationInMinutes < 0) { errors.push(meetingStatus.durationIncorrect); } @@ -1193,21 +1193,21 @@ export class Meeting // Recurring meetings do not have schedule info if (meetingType !== MeetingType.RECURRING) { const _schedule: MeetingScheduleResource = { - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. durationInMinutes: schedule.durationInMinutes, timeZone: { id: this.enableCustomTimezone - ? // @ts-expect-error + ? // @ts-expect-error TS(2532): Object is possibly 'undefined'. schedule.timeZone.id : UTC_TIMEZONE_ID, }, }; - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. if (schedule.startTime) { // Format selected startTime to utc standard time // Timezone information is not included here _schedule.startTime = this.enableCustomTimezone - ? // @ts-expect-error + ? // @ts-expect-error TS(2532): Object is possibly 'undefined'. schedule.startTime : dayjs.utc(schedule?.startTime).format(); } @@ -1237,12 +1237,12 @@ export class Meeting serviceInfo: { ...serviceInfo, mobileDialingNumberTpl: getMobileDialingNumberTpl( - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'DialInNumberResource[] | undefin... Remove this comment to see the full error message serviceInfo.dialInNumbers, resp.id, ), phoneDialingNumberTpl: getPhoneDialingNumberTpl( - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'DialInNumberResource[] | undefin... Remove this comment to see the full error message serviceInfo.dialInNumbers, ), }, @@ -1350,7 +1350,7 @@ export class Meeting if (usePmi) { this.enforcePmiPassword( processedMeeting, - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'RequirePwdTypeForPMI | undefined... Remove this comment to see the full error message requirePwdForPMI, requirePwdIsLockedForPMI, ); @@ -1381,7 +1381,7 @@ export class Meeting } get extensionId(): number { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'number | undefined' is not assignable to typ... Remove this comment to see the full error message return this._deps.extensionInfo.info.id; } @@ -1389,7 +1389,7 @@ export class Meeting return !!this._deps.client.service.platform().discovery(); } - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'string'. rcvBaseWebUri: string = null; async fetchDiscoveryConfig() { @@ -1405,7 +1405,7 @@ export class Meeting } override onReset() { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'string'. this.rcvBaseWebUri = null; } diff --git a/packages/ringcentral-integration/modules/Meeting/constants.ts b/packages/ringcentral-integration/modules/Meeting/constants.ts index 2a5a884148..b72b8ed6a7 100644 --- a/packages/ringcentral-integration/modules/Meeting/constants.ts +++ b/packages/ringcentral-integration/modules/Meeting/constants.ts @@ -50,4 +50,4 @@ export const RCM_ITEM_NAME = { _requireMeetingPassword: '_requireMeetingPassword', } as const; -export type RcmItemType = typeof RCM_ITEM_NAME[keyof typeof RCM_ITEM_NAME]; +export type RcmItemType = (typeof RCM_ITEM_NAME)[keyof typeof RCM_ITEM_NAME]; diff --git a/packages/ringcentral-integration/modules/Meeting/helper.ts b/packages/ringcentral-integration/modules/Meeting/helper.ts index f02a56c603..2b16d3010e 100644 --- a/packages/ringcentral-integration/modules/Meeting/helper.ts +++ b/packages/ringcentral-integration/modules/Meeting/helper.ts @@ -1,6 +1,7 @@ import { find } from 'ramda'; import type { ExtensionInfo } from '../ExtensionInfo'; + import type { MeetingDelegator, RcMMeetingModel } from './Meeting.interface'; export function getExtensionName({ diff --git a/packages/ringcentral-integration/modules/Meeting/i18n/de-DE.ts b/packages/ringcentral-integration/modules/Meeting/i18n/de-DE.ts index a57c939938..887ba86a1c 100644 --- a/packages/ringcentral-integration/modules/Meeting/i18n/de-DE.ts +++ b/packages/ringcentral-integration/modules/Meeting/i18n/de-DE.ts @@ -1,5 +1,5 @@ export default { - meetingTitle: "Besprechung von {extensionName}" + meetingTitle: 'Besprechung von {extensionName}', }; // @key: @#@"meetingTitle"@#@ @source: @#@"{extensionName}'s Meeting"@#@ diff --git a/packages/ringcentral-integration/modules/Meeting/i18n/en-AU.ts b/packages/ringcentral-integration/modules/Meeting/i18n/en-AU.ts index ee0fbafe64..906287190a 100644 --- a/packages/ringcentral-integration/modules/Meeting/i18n/en-AU.ts +++ b/packages/ringcentral-integration/modules/Meeting/i18n/en-AU.ts @@ -1,5 +1,5 @@ export default { - meetingTitle: "{extensionName}’s Meeting" + meetingTitle: '{extensionName}’s Meeting', }; // @key: @#@"meetingTitle"@#@ @source: @#@"{extensionName}'s Meeting"@#@ diff --git a/packages/ringcentral-integration/modules/Meeting/i18n/en-GB.ts b/packages/ringcentral-integration/modules/Meeting/i18n/en-GB.ts index ee0fbafe64..906287190a 100644 --- a/packages/ringcentral-integration/modules/Meeting/i18n/en-GB.ts +++ b/packages/ringcentral-integration/modules/Meeting/i18n/en-GB.ts @@ -1,5 +1,5 @@ export default { - meetingTitle: "{extensionName}’s Meeting" + meetingTitle: '{extensionName}’s Meeting', }; // @key: @#@"meetingTitle"@#@ @source: @#@"{extensionName}'s Meeting"@#@ diff --git a/packages/ringcentral-integration/modules/Meeting/i18n/en-US.ts b/packages/ringcentral-integration/modules/Meeting/i18n/en-US.ts index 66eb3ac80a..1bbb8ab991 100644 --- a/packages/ringcentral-integration/modules/Meeting/i18n/en-US.ts +++ b/packages/ringcentral-integration/modules/Meeting/i18n/en-US.ts @@ -1,3 +1,3 @@ export default { meetingTitle: "{extensionName}'s Meeting", -}; +} as const; diff --git a/packages/ringcentral-integration/modules/Meeting/i18n/es-419.ts b/packages/ringcentral-integration/modules/Meeting/i18n/es-419.ts index 5a1e6c67d3..bda9b2c05d 100644 --- a/packages/ringcentral-integration/modules/Meeting/i18n/es-419.ts +++ b/packages/ringcentral-integration/modules/Meeting/i18n/es-419.ts @@ -1,5 +1,5 @@ export default { - meetingTitle: "Reunión de {extensionName}" + meetingTitle: 'Reunión de {extensionName}', }; // @key: @#@"meetingTitle"@#@ @source: @#@"{extensionName}'s Meeting"@#@ diff --git a/packages/ringcentral-integration/modules/Meeting/i18n/es-ES.ts b/packages/ringcentral-integration/modules/Meeting/i18n/es-ES.ts index 5a1e6c67d3..bda9b2c05d 100644 --- a/packages/ringcentral-integration/modules/Meeting/i18n/es-ES.ts +++ b/packages/ringcentral-integration/modules/Meeting/i18n/es-ES.ts @@ -1,5 +1,5 @@ export default { - meetingTitle: "Reunión de {extensionName}" + meetingTitle: 'Reunión de {extensionName}', }; // @key: @#@"meetingTitle"@#@ @source: @#@"{extensionName}'s Meeting"@#@ diff --git a/packages/ringcentral-integration/modules/Meeting/i18n/fr-CA.ts b/packages/ringcentral-integration/modules/Meeting/i18n/fr-CA.ts index c419fd5dd3..bbf7b9c16b 100644 --- a/packages/ringcentral-integration/modules/Meeting/i18n/fr-CA.ts +++ b/packages/ringcentral-integration/modules/Meeting/i18n/fr-CA.ts @@ -1,5 +1,5 @@ export default { - meetingTitle: "Réunion de {extensionName}" + meetingTitle: 'Réunion de {extensionName}', }; // @key: @#@"meetingTitle"@#@ @source: @#@"{extensionName}'s Meeting"@#@ diff --git a/packages/ringcentral-integration/modules/Meeting/i18n/fr-FR.ts b/packages/ringcentral-integration/modules/Meeting/i18n/fr-FR.ts index c419fd5dd3..bbf7b9c16b 100644 --- a/packages/ringcentral-integration/modules/Meeting/i18n/fr-FR.ts +++ b/packages/ringcentral-integration/modules/Meeting/i18n/fr-FR.ts @@ -1,5 +1,5 @@ export default { - meetingTitle: "Réunion de {extensionName}" + meetingTitle: 'Réunion de {extensionName}', }; // @key: @#@"meetingTitle"@#@ @source: @#@"{extensionName}'s Meeting"@#@ diff --git a/packages/ringcentral-integration/modules/Meeting/i18n/index.ts b/packages/ringcentral-integration/modules/Meeting/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/ringcentral-integration/modules/Meeting/i18n/index.ts +++ b/packages/ringcentral-integration/modules/Meeting/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/ringcentral-integration/modules/Meeting/i18n/it-IT.ts b/packages/ringcentral-integration/modules/Meeting/i18n/it-IT.ts index 6af1cff604..d9891351c9 100644 --- a/packages/ringcentral-integration/modules/Meeting/i18n/it-IT.ts +++ b/packages/ringcentral-integration/modules/Meeting/i18n/it-IT.ts @@ -1,5 +1,5 @@ export default { - meetingTitle: "Riunione {extensionName}" + meetingTitle: 'Riunione {extensionName}', }; // @key: @#@"meetingTitle"@#@ @source: @#@"{extensionName}'s Meeting"@#@ diff --git a/packages/ringcentral-integration/modules/Meeting/i18n/ja-JP.ts b/packages/ringcentral-integration/modules/Meeting/i18n/ja-JP.ts index dec4db7634..7eac5025a0 100644 --- a/packages/ringcentral-integration/modules/Meeting/i18n/ja-JP.ts +++ b/packages/ringcentral-integration/modules/Meeting/i18n/ja-JP.ts @@ -1,5 +1,5 @@ export default { - meetingTitle: "{extensionName}の会議" + meetingTitle: '{extensionName}の会議', }; // @key: @#@"meetingTitle"@#@ @source: @#@"{extensionName}'s Meeting"@#@ diff --git a/packages/ringcentral-integration/modules/Meeting/i18n/ko-KR.ts b/packages/ringcentral-integration/modules/Meeting/i18n/ko-KR.ts index 955e6c82a9..af67741900 100644 --- a/packages/ringcentral-integration/modules/Meeting/i18n/ko-KR.ts +++ b/packages/ringcentral-integration/modules/Meeting/i18n/ko-KR.ts @@ -1,5 +1,5 @@ export default { - meetingTitle: "{extensionName}님의 모임" + meetingTitle: '{extensionName}님의 모임', }; // @key: @#@"meetingTitle"@#@ @source: @#@"{extensionName}'s Meeting"@#@ diff --git a/packages/ringcentral-integration/modules/Meeting/i18n/nl-NL.ts b/packages/ringcentral-integration/modules/Meeting/i18n/nl-NL.ts index 8f9cb20d6b..776807f8fe 100644 --- a/packages/ringcentral-integration/modules/Meeting/i18n/nl-NL.ts +++ b/packages/ringcentral-integration/modules/Meeting/i18n/nl-NL.ts @@ -1,5 +1,5 @@ export default { - meetingTitle: "Meeting van {extensionName}" + meetingTitle: 'Meeting van {extensionName}', }; // @key: @#@"meetingTitle"@#@ @source: @#@"{extensionName}'s Meeting"@#@ diff --git a/packages/ringcentral-integration/modules/Meeting/i18n/pt-BR.ts b/packages/ringcentral-integration/modules/Meeting/i18n/pt-BR.ts index e8077858bc..41375943dd 100644 --- a/packages/ringcentral-integration/modules/Meeting/i18n/pt-BR.ts +++ b/packages/ringcentral-integration/modules/Meeting/i18n/pt-BR.ts @@ -1,5 +1,5 @@ export default { - meetingTitle: "Reunião de {extensionName}" + meetingTitle: 'Reunião de {extensionName}', }; // @key: @#@"meetingTitle"@#@ @source: @#@"{extensionName}'s Meeting"@#@ diff --git a/packages/ringcentral-integration/modules/Meeting/i18n/pt-PT.ts b/packages/ringcentral-integration/modules/Meeting/i18n/pt-PT.ts index e8077858bc..41375943dd 100644 --- a/packages/ringcentral-integration/modules/Meeting/i18n/pt-PT.ts +++ b/packages/ringcentral-integration/modules/Meeting/i18n/pt-PT.ts @@ -1,5 +1,5 @@ export default { - meetingTitle: "Reunião de {extensionName}" + meetingTitle: 'Reunião de {extensionName}', }; // @key: @#@"meetingTitle"@#@ @source: @#@"{extensionName}'s Meeting"@#@ diff --git a/packages/ringcentral-integration/modules/Meeting/i18n/zh-CN.ts b/packages/ringcentral-integration/modules/Meeting/i18n/zh-CN.ts index 3ce661d27c..c859d36d8f 100644 --- a/packages/ringcentral-integration/modules/Meeting/i18n/zh-CN.ts +++ b/packages/ringcentral-integration/modules/Meeting/i18n/zh-CN.ts @@ -1,5 +1,5 @@ export default { - meetingTitle: "{extensionName} 的会议" + meetingTitle: '{extensionName} 的会议', }; // @key: @#@"meetingTitle"@#@ @source: @#@"{extensionName}'s Meeting"@#@ diff --git a/packages/ringcentral-integration/modules/Meeting/i18n/zh-HK.ts b/packages/ringcentral-integration/modules/Meeting/i18n/zh-HK.ts index 99d1d4da4a..352751d697 100644 --- a/packages/ringcentral-integration/modules/Meeting/i18n/zh-HK.ts +++ b/packages/ringcentral-integration/modules/Meeting/i18n/zh-HK.ts @@ -1,5 +1,5 @@ export default { - meetingTitle: "{extensionName} 的會議" + meetingTitle: '{extensionName} 的會議', }; // @key: @#@"meetingTitle"@#@ @source: @#@"{extensionName}'s Meeting"@#@ diff --git a/packages/ringcentral-integration/modules/Meeting/i18n/zh-TW.ts b/packages/ringcentral-integration/modules/Meeting/i18n/zh-TW.ts index 99d1d4da4a..352751d697 100644 --- a/packages/ringcentral-integration/modules/Meeting/i18n/zh-TW.ts +++ b/packages/ringcentral-integration/modules/Meeting/i18n/zh-TW.ts @@ -1,5 +1,5 @@ export default { - meetingTitle: "{extensionName} 的會議" + meetingTitle: '{extensionName} 的會議', }; // @key: @#@"meetingTitle"@#@ @source: @#@"{extensionName}'s Meeting"@#@ diff --git a/packages/ringcentral-integration/modules/Meeting/meetingErrors.ts b/packages/ringcentral-integration/modules/Meeting/meetingErrors.ts index 45587fb20f..4c338d7373 100644 --- a/packages/ringcentral-integration/modules/Meeting/meetingErrors.ts +++ b/packages/ringcentral-integration/modules/Meeting/meetingErrors.ts @@ -1,13 +1,11 @@ export class MeetingErrors { private _errors; - // @ts-expect-error - constructor(type?) { + constructor(type?: any) { this._errors = []; if (type) this._errors.push({ message: type }); } - // @ts-expect-error - push(type) { + push(type: any) { if (type) this._errors.push({ message: type }); } diff --git a/packages/ringcentral-integration/modules/MessageSender/MessageSender.interface.ts b/packages/ringcentral-integration/modules/MessageSender/MessageSender.interface.ts index 22eee33528..16fc524eeb 100644 --- a/packages/ringcentral-integration/modules/MessageSender/MessageSender.interface.ts +++ b/packages/ringcentral-integration/modules/MessageSender/MessageSender.interface.ts @@ -1,12 +1,12 @@ import type { RingCentralClient } from '../../lib/RingCentralClient'; import type { AccountInfo } from '../AccountInfo'; import type { Alert } from '../Alert'; +import type { AppFeatures } from '../AppFeatures'; import type { AvailabilityMonitor } from '../AvailabilityMonitor'; +import type { CompanyContacts } from '../CompanyContacts'; import type { ExtensionInfo } from '../ExtensionInfo'; import type { ExtensionPhoneNumber } from '../ExtensionPhoneNumber'; import type { NumberValidate } from '../NumberValidate'; -import type { AppFeatures } from '../AppFeatures'; -import type { CompanyContacts } from '../CompanyContacts'; export interface Deps { alert: Alert; diff --git a/packages/ringcentral-integration/modules/MessageSender/MessageSender.ts b/packages/ringcentral-integration/modules/MessageSender/MessageSender.ts index 92ec7d3710..0d707acf47 100644 --- a/packages/ringcentral-integration/modules/MessageSender/MessageSender.ts +++ b/packages/ringcentral-integration/modules/MessageSender/MessageSender.ts @@ -1,9 +1,3 @@ -import { EventEmitter } from 'events'; -import { find } from 'ramda'; -import type CreatePagerMessageRequest from 'ringcentral-client/build/definitions/CreatePagerMessageRequest'; -import type GetMessageInfoResponse from 'ringcentral-client/build/definitions/GetMessageInfoResponse'; -import * as uuid from 'uuid'; - import { action, RcModuleV2, @@ -16,12 +10,18 @@ import type { } from '@ringcentral-integration/core/lib/ObjectMap'; import { sleep } from '@ringcentral-integration/utils'; import type { ApiError } from '@ringcentral/sdk'; +import { EventEmitter } from 'events'; +import { find } from 'ramda'; +import type CreatePagerMessageRequest from 'ringcentral-client/build/definitions/CreatePagerMessageRequest'; +import type GetMessageInfoResponse from 'ringcentral-client/build/definitions/GetMessageInfoResponse'; +import * as uuid from 'uuid'; +import { trackEvents } from '../../enums/trackEvents'; import chunkMessage from '../../lib/chunkMessage'; import { Module } from '../../lib/di'; import { isBlank } from '../../lib/isBlank'; import proxify from '../../lib/proxy/proxify'; -import { trackEvents } from '../../enums/trackEvents'; + import type { Attachment, Deps, @@ -214,7 +214,7 @@ export class MessageSender extends RcModuleV2 { }); } } else { - // @ts-expect-error + // @ts-expect-error TS(2339): Property 'numbers' does not exist on type 'Validat... Remove this comment to see the full error message for (const number of numberValidateResult.numbers) { if (number.subAddress && number.subAddress.length > 0) { // remove extension number check when use company contact public api @@ -304,7 +304,7 @@ export class MessageSender extends RcModuleV2 { const pagerResponse = await this._sendPager({ toNumbers: extensionNumbers, text: chunk, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'number | undefined' is not assignable to typ... Remove this comment to see the full error message replyOnMessageId, }); responses.push(pagerResponse); @@ -440,13 +440,13 @@ export class MessageSender extends RcModuleV2 { if ( errResp && !errResp.ok && - // @ts-expect-error + // @ts-expect-error TS(2454): Variable 'errorJson' is used before being assigned... Remove this comment to see the full error message errorJson && (errorJson.errorCode === 'InvalidParameter' || errorJson.errorCode === 'InternationalProhibited' || errorJson.errorCode === 'CMN-408') ) { - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. errorJson.errors.map((err) => { if ( (err.errorCode === 'CMN-101' || @@ -505,7 +505,7 @@ export class MessageSender extends RcModuleV2 { } get senderNumbersList(): SenderNumber[] { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'UserPhoneNumberInfo[]' is not assignable to ... Remove this comment to see the full error message return this._deps.extensionPhoneNumber.smsSenderNumbers; } diff --git a/packages/ringcentral-integration/modules/MessageStore/MessageStore.ts b/packages/ringcentral-integration/modules/MessageStore/MessageStore.ts index 8bfc699e06..af4f3995e2 100644 --- a/packages/ringcentral-integration/modules/MessageStore/MessageStore.ts +++ b/packages/ringcentral-integration/modules/MessageStore/MessageStore.ts @@ -1,10 +1,11 @@ -import { EventEmitter } from 'events'; -import type { ApiError } from '@ringcentral/sdk'; import type GetMessageInfoResponse from '@rc-ex/core/lib/definitions/GetMessageInfoResponse'; import { computed, track, watch } from '@ringcentral-integration/core'; import { sleep } from '@ringcentral-integration/utils'; +import type { ApiError } from '@ringcentral/sdk'; +import { EventEmitter } from 'events'; import { subscriptionFilters } from '../../enums/subscriptionFilters'; +import { trackEvents } from '../../enums/trackEvents'; import type { Message, Messages, @@ -16,9 +17,9 @@ import { debounce } from '../../lib/debounce-throttle'; import { Module } from '../../lib/di'; import * as messageHelper from '../../lib/messageHelper'; import { proxify } from '../../lib/proxy/proxify'; -import { trackEvents } from '../../enums/trackEvents'; import { callingModes } from '../CallingSettings'; import { DataFetcherV2Consumer, DataSource } from '../DataFetcherV2'; + import type { Deps, DispatchedMessageIds, @@ -86,7 +87,7 @@ export class MessageStore extends DataFetcherV2Consumer< protected _dispatchedMessageIds: DispatchedMessageIds = []; - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'GetMessageI... Remove this comment to see the full error message protected _handledRecord: GetMessageInfoResponse[] = null; constructor(deps: T) { @@ -101,7 +102,7 @@ export class MessageStore extends DataFetcherV2Consumer< pollingInterval = DEFAULT_POLLING_INTERVAL, ttl = DEFAULT_TTL, } = this._deps.messageStoreOptions ?? {}; - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'DataSource<{ conversationList: ConversationI... Remove this comment to see the full error message this._source = new DataSource({ ...this._deps.messageStoreOptions, key: 'messageStore', @@ -120,7 +121,10 @@ export class MessageStore extends DataFetcherV2Consumer< override onInit() { if (this._hasPermission) { - this._deps.subscription.subscribe([subscriptionFilters.messageStore]); + this._deps.subscription.subscribe([ + subscriptionFilters.messageStore, + subscriptionFilters.instantMessage, + ]); } } @@ -139,21 +143,26 @@ export class MessageStore extends DataFetcherV2Consumer< watch( this, () => this._deps.subscription.message, - (newValue) => { + async (newValue) => { if ( !this.ready || (this._deps.tabManager && !this._deps.tabManager.active) ) { return; } - const accountExtensionEndPoint = /\/message-store$/; + const messageStoreEvent = /\/message-store$/; + const instantMessageEvent = /\/message-store\/instant\?type=SMS$/; if ( - newValue && - // @ts-expect-error - accountExtensionEndPoint.test(newValue.event) && + messageStoreEvent.test(newValue?.event!) && newValue.body?.changes ) { - this.fetchData({ passive: true }); + try { + await this.fetchData({ passive: true }); + } catch (ex) { + console.error('[MessageStore] > subscription > fetchData', ex); + } + } else if (instantMessageEvent.test(newValue?.event!)) { + this.pushMessage(messageHelper.normalizeInstantEvent(newValue)); } }, ); @@ -188,18 +197,18 @@ export class MessageStore extends DataFetcherV2Consumer< const id = message.conversationId; const newCreationTime = message.creationTime; const isDeleted = messageHelper.messageIsDeleted(message); - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. if (stateMap[id]) { - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. const oldConversation = newState[stateMap[id].index]; const creationTime = oldConversation.creationTime; - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. if (creationTime < newCreationTime && !isDeleted) { - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. newState[stateMap[id].index] = { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'string | undefined' is not assignable to typ... Remove this comment to see the full error message id, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'number | undefined' is not assignable to typ... Remove this comment to see the full error message creationTime: newCreationTime, type: message.type, messageId: message.id, @@ -207,16 +216,15 @@ export class MessageStore extends DataFetcherV2Consumer< } // when user deleted a coversation message if (isDeleted && message.id === oldConversation.messageId) { - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. const oldMessageList = conversationStore[id] || []; const exsitedMessageList = oldMessageList.filter( - // @ts-expect-error - (m) => m.id !== message.id, + (m: any) => m.id !== message.id, ); if (exsitedMessageList.length > 0) { - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. newState[stateMap[id].index] = { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'string | undefined' is not assignable to typ... Remove this comment to see the full error message id, creationTime: exsitedMessageList[0].creationTime, type: exsitedMessageList[0].type, @@ -225,9 +233,9 @@ export class MessageStore extends DataFetcherV2Consumer< return; } // when user delete conversation - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Conversatio... Remove this comment to see the full error message newState[stateMap[id].index] = null; - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. delete stateMap[id]; } return; @@ -236,14 +244,14 @@ export class MessageStore extends DataFetcherV2Consumer< return; } newState.push({ - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'string | undefined' is not assignable to typ... Remove this comment to see the full error message id, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'number | undefined' is not assignable to typ... Remove this comment to see the full error message creationTime: newCreationTime, type: message.type, messageId: message.id, }); - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. stateMap[id] = { index: newState.length - 1, }; @@ -271,37 +279,37 @@ export class MessageStore extends DataFetcherV2Consumer< records.forEach((record) => { const message = messageHelper.normalizeRecord(record); const id = message.conversationId; - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. const newMessages = newState[id] ? [].concat(newState[id]) : []; - // @ts-expect-error + // @ts-expect-error TS(2339): Property 'id' does not exist on type 'never'. const oldMessageIndex = newMessages.findIndex((r) => r.id === record.id); if (messageHelper.messageIsDeleted(message)) { - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. newState[id] = newMessages.filter((m) => m.id !== message.id); - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. if (newState[id].length === 0) { - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. delete newState[id]; } return; } if (oldMessageIndex > -1) { if ( - // @ts-expect-error + // @ts-expect-error TS(2339): Property 'lastModifiedTime' does not exist on type... Remove this comment to see the full error message newMessages[oldMessageIndex].lastModifiedTime < - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. message.lastModifiedTime ) { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'Message' is not assignable to type 'never'. newMessages[oldMessageIndex] = message; } } else if (messageHelper.messageIsAcceptable(message)) { - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'Message' is not assignable to pa... Remove this comment to see the full error message newMessages.push(message); } - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. updatedConversations[id] = 1; - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. newState[id] = newMessages; }); Object.keys(updatedConversations).forEach((id) => { @@ -332,12 +340,12 @@ export class MessageStore extends DataFetcherV2Consumer< .messageSync() .list(params); receivedRecordsLength += records.length; - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. if (!syncInfo.olderRecordsExist || receivedRecordsLength >= recordCount) { return { records, syncInfo }; } await sleep(500); - // @ts-expect-error + // @ts-expect-error TS(2769): No overload matches this call. const olderDateTo = new Date(records[records.length - 1].creationTime); const olderRecordResult = await this._syncFunction({ conversationLoadLength, @@ -350,7 +358,7 @@ export class MessageStore extends DataFetcherV2Consumer< }; } - // @ts-expect-error + // @ts-expect-error TS(2352): Conversion of type 'null' to type 'Date' may be a ... Remove this comment to see the full error message async _syncData({ dateTo = null as Date, passive = false } = {}) { const conversationsLoadLength = this._conversationsLoadLength; const conversationLoadLength = this._conversationLoadLength; @@ -358,42 +366,39 @@ export class MessageStore extends DataFetcherV2Consumer< try { const dateFrom = new Date(); dateFrom.setDate(dateFrom.getDate() - this._daySpan); - let syncToken = dateTo ? null : this.syncInfo?.syncToken; + let syncToken = dateTo ? undefined : this.syncInfo?.syncToken; const recordCount = conversationsLoadLength * conversationLoadLength; - let data; + let data: MessageSyncList; try { data = await this._syncFunction({ recordCount, conversationLoadLength, dateFrom, - // @ts-expect-error syncToken, dateTo, }); } catch (e: unknown) { const error = e as ApiError; + const responseResult = await error.response?.clone().json(); if ( - error.response?.status === 400 && - (await error.response?.clone().json())?.error?.some( - ({ errorCode = '' } = {}) => - INVALID_TOKEN_ERROR_CODES.includes(errorCode), + responseResult?.errors?.some(({ errorCode = '' } = {}) => + INVALID_TOKEN_ERROR_CODES.includes(errorCode), ) ) { data = await this._syncFunction({ recordCount, conversationLoadLength, dateFrom, - // @ts-expect-error - syncToken: null, + syncToken: undefined, dateTo, }); - syncToken = null; + syncToken = undefined; } else { throw error; } } if (this._deps.auth.ownerId === ownerId) { - const records = this._messagesFilter(data.records); + const records = this._messagesFilter(data!.records); const isFSyncSuccess = !syncToken; // this is only executed in passive sync mode (aka. invoked by subscription) if (passive) { @@ -409,12 +414,12 @@ export class MessageStore extends DataFetcherV2Consumer< records, isFSyncSuccess, }), - syncInfo: data.syncInfo, + syncInfo: data!.syncInfo, }; } } catch (error: any /** TODO: confirm with instanceof */) { if (this._deps.auth.ownerId === ownerId) { - console.error(error); + console.error('[MessageStore] > _syncData', error); throw error; } } @@ -426,7 +431,7 @@ export class MessageStore extends DataFetcherV2Consumer< this._updateData(data); if (passive && this._handledRecord) { this._dispatchMessageHandlers(this._handledRecord); - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'GetMessageI... Remove this comment to see the full error message this._handledRecord = null; } } @@ -450,9 +455,9 @@ export class MessageStore extends DataFetcherV2Consumer< // Sort all records by creation time records = records.slice().sort( (a, b) => - // @ts-expect-error + // @ts-expect-error TS(2769): No overload matches this call. new Date(a.creationTime).getTime() - - // @ts-expect-error + // @ts-expect-error TS(2769): No overload matches this call. new Date(b.creationTime).getTime(), ); for (const record of records) { @@ -470,7 +475,7 @@ export class MessageStore extends DataFetcherV2Consumer< if (!this._messageDispatched(record)) { // Mark last 10 messages that dispatched // To present dispatching same record twice - // @ts-expect-error + // @ts-expect-error TS(2322): Type '{ id: number | undefined; lastModifiedTime: ... Remove this comment to see the full error message this._dispatchedMessageIds = [{ id, lastModifiedTime }] .concat(this._dispatchedMessageIds) .slice(0, 20); @@ -481,9 +486,9 @@ export class MessageStore extends DataFetcherV2Consumer< readStatus === 'Unread' && messageStatus === 'Received' && availability === 'Alive' && - // @ts-expect-error + // @ts-expect-error TS(2769): No overload matches this call. new Date(creationTime).getTime() > - // @ts-expect-error + // @ts-expect-error TS(2769): No overload matches this call. new Date(lastModifiedTime).getTime() - 600 * 1000 ) { this._eventEmitter.emit('newInboundMessageNotification', record); @@ -513,7 +518,7 @@ export class MessageStore extends DataFetcherV2Consumer< records, }), }, - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'number | null' is not assignable... Remove this comment to see the full error message this.timestamp, ); } @@ -576,7 +581,7 @@ export class MessageStore extends DataFetcherV2Consumer< conversationList, conversationStore, }, - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'number | null' is not assignable... Remove this comment to see the full error message this.timestamp, ); } @@ -634,7 +639,7 @@ export class MessageStore extends DataFetcherV2Consumer< // If there's only one message, use another api to update its status if (nextLength === 1) { - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'number | undefined' is not assig... Remove this comment to see the full error message const result = await this._updateMessageApi(messageIds[0], status); return [result]; } @@ -647,7 +652,7 @@ export class MessageStore extends DataFetcherV2Consumer< const body = leftIds.map(() => ({ body: { readStatus: status } })); const responses = await this._batchUpdateMessagesApi(leftIds, body); await Promise.all( - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. responses.map(async (res) => { if (res.status === 200) { const result = await res.json(); @@ -686,15 +691,14 @@ export class MessageStore extends DataFetcherV2Consumer< }); async _setConversationAsRead(conversationId: Message['conversationId']) { - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. const messageList = this.conversationStore[conversationId]; if (!messageList || messageList.length === 0) { return; } const unreadMessageIds = messageList .filter(messageHelper.messageIsUnread) - // @ts-expect-error - .map((m) => m.id); + .map((m: any) => m.id); if (unreadMessageIds.length === 0) { return; } @@ -750,7 +754,7 @@ export class MessageStore extends DataFetcherV2Consumer< } @track((that: MessageStore, conversationId: Message['conversationId']) => { - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. const [conversation] = that.conversationStore[conversationId] ?? []; if (!conversation) return; if (conversation.type === 'VoiceMail') { @@ -766,12 +770,12 @@ export class MessageStore extends DataFetcherV2Consumer< } _deleteConversationStore(conversationId: Message['conversationId']) { - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. if (!this.conversationStore[conversationId]) { return this.conversationStore; } const newState = { ...this.conversationStore }; - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. delete newState[conversationId]; return newState; } @@ -789,7 +793,7 @@ export class MessageStore extends DataFetcherV2Consumer< conversationList, conversationStore, }, - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'number | null' is not assignable... Remove this comment to see the full error message this.timestamp, ); } diff --git a/packages/ringcentral-integration/modules/MessageStore/messageStoreHelper.ts b/packages/ringcentral-integration/modules/MessageStore/messageStoreHelper.ts index e28905f77c..e4e331e97d 100644 --- a/packages/ringcentral-integration/modules/MessageStore/messageStoreHelper.ts +++ b/packages/ringcentral-integration/modules/MessageStore/messageStoreHelper.ts @@ -1,4 +1,5 @@ import { syncTypes } from '../../enums/syncTypes'; + import type { SyncFunctionOptions } from './MessageStore.interface'; type GetSyncParamsOptions = Pick< diff --git a/packages/ringcentral-integration/modules/NumberValidate/NumberValidate.interface.ts b/packages/ringcentral-integration/modules/NumberValidate/NumberValidate.interface.ts index 45467733ac..1c71ec7e88 100644 --- a/packages/ringcentral-integration/modules/NumberValidate/NumberValidate.interface.ts +++ b/packages/ringcentral-integration/modules/NumberValidate/NumberValidate.interface.ts @@ -6,12 +6,12 @@ import type { ResultFormattedItem, } from '../../interfaces/NumberParserResponse.interface'; import type { AccountInfo } from '../AccountInfo'; +import type { Alert } from '../Alert'; import type { AppFeatures } from '../AppFeatures'; import type { Brand } from '../Brand'; import type { CompanyContacts } from '../CompanyContacts'; import type { ExtensionInfo } from '../ExtensionInfo'; import type { RegionSettings } from '../RegionSettings'; -import type { Alert } from '../Alert'; export interface NumberValidateOptions { // @@ -94,5 +94,3 @@ export interface ParseResultItem extends ResultFormattedItem { parsedNumber?: string; availableExtension?: string | null; } - -export type ParseResult = Array; diff --git a/packages/ringcentral-integration/modules/NumberValidate/NumberValidate.ts b/packages/ringcentral-integration/modules/NumberValidate/NumberValidate.ts index 643cede440..eb524af0cb 100644 --- a/packages/ringcentral-integration/modules/NumberValidate/NumberValidate.ts +++ b/packages/ringcentral-integration/modules/NumberValidate/NumberValidate.ts @@ -16,10 +16,10 @@ import { isBlank } from '../../lib/isBlank'; import { normalizeNumber } from '../../lib/normalizeNumber'; import { proxify } from '../../lib/proxy/proxify'; import { callErrors } from '../Call/callErrors'; + import type { Deps, ParsePhoneNumberAPIParam, - ParseResult, ParseResultItem, ValidatedPhoneNumbers, ValidateFormattedError, @@ -167,7 +167,7 @@ export class NumberValidate extends RcModuleV2 { const parsedNumbers = await this._numberParser(phoneNumbers); const errors: ValidateParsedError = []; const validatedPhoneNumbers: ValidatedPhoneNumbers = []; - parsedNumbers.map((phoneNumber) => { + parsedNumbers.forEach((phoneNumber) => { const isSpecial = this._isSpecial(phoneNumber); const number = phoneNumber.originalString; @@ -187,18 +187,16 @@ export class NumberValidate extends RcModuleV2 { ); if (!availableExtension) { errors.push({ - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'string | undefined' is not assignable to typ... Remove this comment to see the full error message phoneNumber: phoneNumber.originalString, type: 'notAnExtension', }); - return null; + return; } extensionObj.availableExtension = availableExtension; } validatedPhoneNumbers.push({ ...phoneNumber, ...extensionObj }); - - return null; }); return { result: errors.length === 0, @@ -218,12 +216,12 @@ export class NumberValidate extends RcModuleV2 { normalizedNumbers, homeCountry, ); - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. return response.phoneNumbers.map((phoneNumber) => ({ ...phoneNumber, international: !!phoneNumber.country && - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. phoneNumber.country.callingCode !== response.homeCountry.callingCode, })); } @@ -268,9 +266,12 @@ export class NumberValidate extends RcModuleV2 { } @proxify - async parseNumbers(inputs: string[]): Promise { + async parseNumbers(inputs: string[]): Promise { const { countryCode, defaultAreaCode } = this._deps.regionSettings; - const brandId = this._deps.brand.brandConfig.id; + // TODO: API has not supported sub-brand. As a workaround, we use brandId instead of uBrandId here + const brandId = + this._deps.accountInfo.serviceInfo?.brand?.id || + this._deps.brand.brandConfig.id; const phoneNumbers = inputs.map((input: string) => cleanNumber(input)); const data: ParsePhoneNumberAPIParam = { originalStrings: phoneNumbers, @@ -293,7 +294,9 @@ export class NumberValidate extends RcModuleV2 { return response?.results.map((result) => this.handleResult(result)); } - // whether the number is an empty string or contains invalid characters + /** + * Whether the number is an empty string or contains invalid characters + */ validate(numbers: string[]): ValidateFormattingResult { const errors: ValidateFormattedError = []; numbers.forEach((phoneNumber) => { diff --git a/packages/ringcentral-integration/modules/Presence/Presence.interface.ts b/packages/ringcentral-integration/modules/Presence/Presence.interface.ts index a895f1fa10..2c14781f5a 100644 --- a/packages/ringcentral-integration/modules/Presence/Presence.interface.ts +++ b/packages/ringcentral-integration/modules/Presence/Presence.interface.ts @@ -10,6 +10,7 @@ import type { import type { ExtensionFeatures } from '../ExtensionFeatures'; import type { Subscription } from '../Subscription'; import type { TabManager } from '../TabManager'; + import type { dndStatus } from './dndStatus'; export interface Deps extends DataFetcherV2ConsumerBaseDeps { diff --git a/packages/ringcentral-integration/modules/Presence/Presence.ts b/packages/ringcentral-integration/modules/Presence/Presence.ts index be2fad3ecf..9406de3137 100644 --- a/packages/ringcentral-integration/modules/Presence/Presence.ts +++ b/packages/ringcentral-integration/modules/Presence/Presence.ts @@ -1,5 +1,3 @@ -import { filter, map } from 'ramda'; -import type { Unsubscribe } from 'redux'; import type DetailedExtensionPresenceEvent from '@rc-ex/core/lib/definitions/DetailedExtensionPresenceEvent'; import type GetPresenceInfo from '@rc-ex/core/lib/definitions/GetPresenceInfo'; import type PresenceInfoResponse from '@rc-ex/core/lib/definitions/PresenceInfoResponse'; @@ -11,6 +9,8 @@ import { storage, } from '@ringcentral-integration/core'; import type { ObjectMapValue } from '@ringcentral-integration/core/lib/ObjectMap'; +import { filter, map } from 'ramda'; +import type { Unsubscribe } from 'redux'; import { presenceStatus } from '../../enums/presenceStatus.enum'; import { subscriptionFilters } from '../../enums/subscriptionFilters'; @@ -26,8 +26,9 @@ import { debounce } from '../../lib/debounce-throttle'; import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; import { DataFetcherV2Consumer, DataSource } from '../DataFetcherV2'; -import { dndStatus } from './dndStatus'; + import type { Deps, UpdatePresenceParams } from './Presence.interface'; +import { dndStatus } from './dndStatus'; import { removeIntermediateCall } from './removeIntermediateCall'; export const DEFAULT_TTL = 62 * 1000; @@ -61,9 +62,9 @@ const acceptCallQueueToggles = [ export class Presence extends DataFetcherV2Consumer { protected _debouncedFetchData: DebouncedFunction; - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Unsubscribe... Remove this comment to see the full error message protected _stopWatchingConnectivity: Unsubscribe = null; - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Unsubscribe... Remove this comment to see the full error message protected _stopWatchingSubscription: Unsubscribe = null; constructor(deps: Deps) { @@ -100,7 +101,7 @@ export class Presence extends DataFetcherV2Consumer { ); return { sequence: this._sequence, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'unknown[]' is not assignable to type 'Active... Remove this comment to see the full error message activeCalls, dndStatus, meetingStatus, @@ -174,10 +175,10 @@ export class Presence extends DataFetcherV2Consumer { if (activeCalls.length < totalActiveCalls) { return this.activeCalls; } - // @ts-expect-error + // @ts-expect-error TS(2769): No overload matches this call. return map((activeCall) => { const existingCall = this.activeCalls.find( - // @ts-expect-error + // @ts-expect-error TS(2339): Property 'sessionId' does not exist on type 'never... Remove this comment to see the full error message (call) => call.sessionId === activeCall.sessionId, ); if (!existingCall) { @@ -194,7 +195,7 @@ export class Presence extends DataFetcherV2Consumer { ...existingCall, ...normalizeStartTime(normalizeFromTo(activeCall)), }; - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'ActiveCallInfo[]' is not assigna... Remove this comment to see the full error message }, removeIntermediateCall([], activeCalls)); } @@ -203,7 +204,7 @@ export class Presence extends DataFetcherV2Consumer { if ( this.ready && (this._source.disableCache || (this._deps.tabManager?.active ?? true)) && - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message regExp.test(message.event) && message.body ) { @@ -228,7 +229,7 @@ export class Presence extends DataFetcherV2Consumer { this._updateData( { sequence, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'unknown[]' is not assignable to type 'Active... Remove this comment to see the full error message activeCalls, dndStatus, meetingStatus, @@ -278,10 +279,10 @@ export class Presence extends DataFetcherV2Consumer { override onReset() { this._stopWatchingConnectivity?.(); - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Unsubscribe... Remove this comment to see the full error message this._stopWatchingConnectivity = null; this._stopWatchingSubscription?.(); - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Unsubscribe... Remove this comment to see the full error message this._stopWatchingSubscription = null; this._debouncedFetchData.cancel(); } @@ -314,26 +315,33 @@ export class Presence extends DataFetcherV2Consumer { @proxify async _update(params: UpdatePresenceParams) { - if (!this._deps.extensionFeatures.features?.EditPresenceStatus?.available) { - return; - } - const ownerId = this._deps.auth.ownerId; - const response = await this._deps.client.service - .platform() - .put('/restapi/v1.0/account/~/extension/~/presence', params); - const data: PresenceInfoResponse = await response.json(); - - if (ownerId === this._deps.auth.ownerId) { - const newDndStatus = ((data.dndStatus !== 'Unknown' && data.dndStatus) ?? - this.data.dndStatus) as ObjectMapValue; - this._setLastDndStatus(this._calculateLastDndStatus(newDndStatus)); - this._updateData({ - presenceStatus: data.presenceStatus, - userStatus: data.userStatus, - telephonyStatus: data.telephonyStatus, - dndStatus: newDndStatus, - meetingStatus: data.meetingStatus, - }); + try { + if ( + !this._deps.extensionFeatures.features?.EditPresenceStatus?.available + ) { + return; + } + const ownerId = this._deps.auth.ownerId; + const response = await this._deps.client.service + .platform() + .put('/restapi/v1.0/account/~/extension/~/presence', params); + const data: PresenceInfoResponse = await response?.json(); + + if (ownerId === this._deps.auth.ownerId) { + const newDndStatus = ((data.dndStatus !== 'Unknown' && + data.dndStatus) ?? + this.data.dndStatus) as ObjectMapValue; + this._setLastDndStatus(this._calculateLastDndStatus(newDndStatus)); + this._updateData({ + presenceStatus: data.presenceStatus, + userStatus: data.userStatus, + telephonyStatus: data.telephonyStatus, + dndStatus: newDndStatus, + meetingStatus: data.meetingStatus, + }); + } + } catch (e) { + console.error('put presence failed', e); } } @@ -351,7 +359,6 @@ export class Presence extends DataFetcherV2Consumer { _getUpdateStatusParams(userStatus: GetPresenceInfo['userStatus']) { const params: UpdatePresenceParams = { - // @ts-expect-error dndStatus: this.dndStatus, userStatus, }; @@ -447,23 +454,23 @@ export class Presence extends DataFetcherV2Consumer { } get telephonyStatus() { - return this.data?.telephonyStatus ?? null; + return this.data?.telephonyStatus; } get dndStatus() { - return this.data?.dndStatus ?? null; + return this.data?.dndStatus; } get userStatus() { - return this.data?.userStatus ?? null; + return this.data?.userStatus; } get presenceStatus() { - return this.data?.presenceStatus ?? null; + return this.data?.presenceStatus; } get meetingStatus() { - return this.data?.meetingStatus ?? null; + return this.data?.meetingStatus; } get presenceOption() { @@ -486,7 +493,7 @@ export class Presence extends DataFetcherV2Consumer { return presenceStatus.available; } - // @ts-expect-error + // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message async fetchData() { this._debouncedFetchData.cancel(); return this._deps.dataFetcherV2.fetchData(this._source); diff --git a/packages/ringcentral-integration/modules/Presence/dndStatus.ts b/packages/ringcentral-integration/modules/Presence/dndStatus.ts index 7ab41226d8..d4fb79b584 100644 --- a/packages/ringcentral-integration/modules/Presence/dndStatus.ts +++ b/packages/ringcentral-integration/modules/Presence/dndStatus.ts @@ -7,4 +7,4 @@ export const dndStatus = ObjectMap.fromObject({ takeDepartmentCallsOnly: 'TakeDepartmentCallsOnly', } as const); -export type DNDStatusValueType = typeof dndStatus[keyof typeof dndStatus]; +export type DNDStatusValueType = (typeof dndStatus)[keyof typeof dndStatus]; diff --git a/packages/ringcentral-integration/modules/Presence/removeIntermediateCall.ts b/packages/ringcentral-integration/modules/Presence/removeIntermediateCall.ts index 7bd6df64de..0e51acfe9d 100644 --- a/packages/ringcentral-integration/modules/Presence/removeIntermediateCall.ts +++ b/packages/ringcentral-integration/modules/Presence/removeIntermediateCall.ts @@ -1,24 +1,24 @@ -import { find, reduce } from 'ramda'; import type GetPresenceInfo from '@rc-ex/core/lib/definitions/GetPresenceInfo'; +import { find, reduce } from 'ramda'; import { isIntermediateCall } from '../../lib/callLogHelpers'; export const removeIntermediateCall = reduce( (result, activeCall: GetPresenceInfo['activeCalls']): any => { if ( - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'ActiveCallInfo[] | undefined' is... Remove this comment to see the full error message !isIntermediateCall(activeCall) && !find( (item) => - // @ts-expect-error + // @ts-expect-error TS(2571): Object is of type 'unknown'. item.sessionId === activeCall.sessionId && - // @ts-expect-error + // @ts-expect-error TS(2571): Object is of type 'unknown'. item.direction === activeCall.direction, - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message result, ) ) { - // @ts-expect-error + // @ts-expect-error TS(2571): Object is of type 'unknown'. result.push(activeCall); } return result; diff --git a/packages/ringcentral-integration/modules/QuickAccess/QuickAccess.ts b/packages/ringcentral-integration/modules/QuickAccess/QuickAccess.ts index 61c28c0381..128c0a2e47 100644 --- a/packages/ringcentral-integration/modules/QuickAccess/QuickAccess.ts +++ b/packages/ringcentral-integration/modules/QuickAccess/QuickAccess.ts @@ -7,6 +7,7 @@ import { import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; + import type { Deps } from './QuickAccess.interface'; @Module({ diff --git a/packages/ringcentral-integration/modules/RateLimiter/RateLimiter.ts b/packages/ringcentral-integration/modules/RateLimiter/RateLimiter.ts index 028475b539..801377757a 100644 --- a/packages/ringcentral-integration/modules/RateLimiter/RateLimiter.ts +++ b/packages/ringcentral-integration/modules/RateLimiter/RateLimiter.ts @@ -9,8 +9,9 @@ import type { ApiError } from '@ringcentral/sdk'; import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; -import { errorMessages } from './errorMessages'; + import type { Deps } from './RateLimiter.interface'; +import { errorMessages } from './errorMessages'; const DEFAULT_THROTTLE_DURATION = 61 * 1000; @@ -25,7 +26,7 @@ const DEFAULT_THROTTLE_DURATION = 61 * 1000; ], }) export class RateLimiter extends RcModuleV2 { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Timeout'. protected _timeoutId: NodeJS.Timeout = null; protected _unbindHandlers?: () => void; protected _throttleDuration: number = DEFAULT_THROTTLE_DURATION; @@ -47,12 +48,12 @@ export class RateLimiter extends RcModuleV2 { @globalStorage @state - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'number'. timestamp: number = null; @globalStorage @state - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'string'. rateLimitAlertId: string = null; @action @@ -62,7 +63,7 @@ export class RateLimiter extends RcModuleV2 { @action stopThrottle() { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'number'. this.timestamp = null; } @@ -70,7 +71,7 @@ export class RateLimiter extends RcModuleV2 { if (this._deps.environment) { watch( this, - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. () => this._deps.environment.changeCounter, () => { if (this.ready) { @@ -95,7 +96,7 @@ export class RateLimiter extends RcModuleV2 { }; _checkTimestamp = () => { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Timeout'. this._timeoutId = null; if (!this.throttling) { this.stopThrottle(); @@ -158,7 +159,7 @@ export class RateLimiter extends RcModuleV2 { client.events.beforeRequest, this._beforeRequestHandler, ); - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type '(() => void... Remove this comment to see the full error message this._unbindHandlers = null; }; } diff --git a/packages/ringcentral-integration/modules/RcVideo/RcVideo.interface.ts b/packages/ringcentral-integration/modules/RcVideo/RcVideo.interface.ts index 626a0a89b0..a752395ffa 100644 --- a/packages/ringcentral-integration/modules/RcVideo/RcVideo.interface.ts +++ b/packages/ringcentral-integration/modules/RcVideo/RcVideo.interface.ts @@ -1,5 +1,5 @@ -import type Client from 'ringcentral-client'; import type GetExtensionInfoResponse from '@rc-ex/core/lib/definitions/GetExtensionInfoResponse'; +import type Client from 'ringcentral-client'; import type { RcVDialInNumberObj } from '../../interfaces/Rcv.model'; import type { AccountInfo } from '../AccountInfo'; @@ -11,6 +11,7 @@ import type { ExtensionInfo } from '../ExtensionInfo'; import type { Locale } from '../Locale'; import type { Storage } from '../Storage'; import type { VideoConfiguration } from '../VideoConfiguration'; + import type { DISABLE_E2EE_WHEN_RELATED_OPTION_MATCH } from './constants'; export type DisableE2eeWhenRelatedOptionMatch = diff --git a/packages/ringcentral-integration/modules/RcVideo/RcVideo.ts b/packages/ringcentral-integration/modules/RcVideo/RcVideo.ts index a70e7cfa62..883914b74a 100644 --- a/packages/ringcentral-integration/modules/RcVideo/RcVideo.ts +++ b/packages/ringcentral-integration/modules/RcVideo/RcVideo.ts @@ -1,5 +1,3 @@ -import { filter, find } from 'ramda'; - import { action, computed, @@ -10,7 +8,10 @@ import { } from '@ringcentral-integration/core'; import type { ObjectMapValue } from '@ringcentral-integration/core/lib/ObjectMap'; import { DEFAULT_LOCALE } from '@ringcentral-integration/i18n'; +import { format } from '@ringcentral-integration/utils'; +import { filter, find } from 'ramda'; +import { trackEvents } from '../../enums/trackEvents'; import { getInitializedStartTime } from '../../helpers/meetingHelper'; import { renameTurkey, renameTurkeyCountry } from '../../helpers/renameTurkey'; import type { IMeeting } from '../../interfaces/Meeting.interface'; @@ -30,8 +31,14 @@ import type { import background from '../../lib/background'; import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; -import { trackEvents } from '../../enums/trackEvents'; import { MeetingErrors, meetingStatus } from '../Meeting'; + +import type { + Deps, + InvitationBridgesResponse, + RcvDelegator, + RcVideoResponse, +} from './RcVideo.interface'; import type { RcvWaitingRoomModeProps } from './constants'; import { ASSISTED_USERS_MYSELF, @@ -39,12 +46,7 @@ import { RCV_E2EE_API_KEYS, RCV_WAITING_ROOM_API_KEYS, } from './constants'; -import type { - Deps, - InvitationBridgesResponse, - RcvDelegator, - RcVideoResponse, -} from './RcVideo.interface'; +import i18n from './i18n'; import { assignObject, comparePreferences, @@ -130,7 +132,7 @@ export class RcVideo extends RcModuleV2 implements IMeeting { @storage @state - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Partial = null; // when migrate to rcv v2, computed defaultVideoSetting has conflict with storage key 'defaultVideoSetting' @@ -140,7 +142,7 @@ export class RcVideo extends RcModuleV2 implements IMeeting { savedDefaultSetting: Partial = {}; @state - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Partial = null; @state @@ -156,7 +158,7 @@ export class RcVideo extends RcModuleV2 implements IMeeting { settingLocks: RcVSettingLocksGET = {}; @state - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'RcvDelegato... Remove this comment to see the full error message delegator: RcvDelegator = null; @state @@ -213,7 +215,7 @@ export class RcVideo extends RcModuleV2 implements IMeeting { @track((that: RcVideo, status: string) => { if (status !== videoStatus.creating) return; return (analytics) => { - // @ts-expect-error + // @ts-expect-error TS(2339): Property 'getTrackTarget' does not exist on type '... Remove this comment to see the full error message const target = analytics.getTrackTarget(); if (target) { return [ @@ -303,7 +305,7 @@ export class RcVideo extends RcModuleV2 implements IMeeting { } const hostId = `${userExtensionId}`; - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'RcvDelegator | undefined' is not assignable ... Remove this comment to see the full error message const delegator: RcvDelegator = find( (user: RcvDelegator) => user.extensionId === hostId, this.delegators, @@ -427,9 +429,9 @@ export class RcVideo extends RcModuleV2 implements IMeeting { muteAudio, muteVideo, isMeetingSecret, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'RcvWaitingRoomModeProps | undefined' is not ... Remove this comment to see the full error message waitingRoomMode, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'boolean | undefined' is not assignable to ty... Remove this comment to see the full error message e2ee, }; if (notShowAgain) { @@ -486,8 +488,7 @@ export class RcVideo extends RcModuleV2 implements IMeeting { // After Create const invitationInfo = await this.getMeetingInvitation({ - // @ts-expect-error - hostName: extensionInfo.name, + hostName: extensionInfo.name!, shortId: newMeeting.shortId, id: newMeeting.id, e2ee: newMeeting.e2ee, @@ -496,7 +497,7 @@ export class RcVideo extends RcModuleV2 implements IMeeting { meetingPasswordPSTN: newMeeting.meetingPasswordPSTN, meetingPasswordMasked: newMeeting.meetingPasswordMasked, joinUri: newMeeting.joinUri || '', - // @ts-expect-error + // @ts-ignore dialInNumbers: dialInNumber, currentLocale: this.currentLocale, brandName: this._deps.brand.name, @@ -566,7 +567,6 @@ export class RcVideo extends RcModuleV2 implements IMeeting { return this.createMeeting( { ...meeting, - // @ts-expect-error expiresIn: null, type: RcVideoTypes.meeting, }, @@ -667,11 +667,11 @@ export class RcVideo extends RcModuleV2 implements IMeeting { if (countryDialinNumbers.length > 0) { return countryDialinNumbers; } - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'RcVDialInNumberObj | undefined' is not assig... Remove this comment to see the full error message return [defaultPhoneNumber]; } - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. return defaultPhoneNumber.phoneNumber; } return []; @@ -794,19 +794,23 @@ export class RcVideo extends RcModuleV2 implements IMeeting { // when meeting is rcv pmi, use pmi default name if (meeting?.usePersonalMeetingId) { - meetingDetail.name = this.personalMeeting.name || ''; + meetingDetail.name = format( + i18n.getString('rcvPmiMeetingTitle', this.currentLocale), + { + extensionName: this.extensionName, + }, + ); } const [newMeeting, dialInNumber, extensionInfo] = await Promise.all([ - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'string | null | undefined' is no... Remove this comment to see the full error message this._patchBridges(meeting.id, meetingDetail), this._getDialinNumbers(), this.getExtensionInfo(this.currentUser.extensionId), ]); const invitationInfo = await this.getMeetingInvitation({ - // @ts-expect-error - hostName: extensionInfo.name, + hostName: extensionInfo.name!, shortId: newMeeting.shortId, id: newMeeting.id, e2ee: newMeeting.e2ee, @@ -815,7 +819,7 @@ export class RcVideo extends RcModuleV2 implements IMeeting { meetingPasswordPSTN: newMeeting.meetingPasswordPSTN, meetingPasswordMasked: newMeeting.meetingPasswordMasked, joinUri: newMeeting.joinUri || '', - // @ts-expect-error + // @ts-ignore dialInNumbers: dialInNumber, currentLocale: this.currentLocale, brandName: this._deps.brand.name, @@ -875,7 +879,7 @@ export class RcVideo extends RcModuleV2 implements IMeeting { e2ee: true, ...RCV_E2EE_DEFAULT_SECURITY_OPTIONS, // if jbh is locked, do not change its value - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. allowJoinBeforeHost: this.meeting.settingLock.allowJoinBeforeHost ? this.meeting.allowJoinBeforeHost : RCV_E2EE_DEFAULT_SECURITY_OPTIONS.allowJoinBeforeHost, @@ -902,7 +906,7 @@ export class RcVideo extends RcModuleV2 implements IMeeting { { ...processedMeeting, isMeetingPasswordValid: this.validatePasswordSettings( - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message processedMeeting.meetingPassword ?? this.meeting?.meetingPassword, processedMeeting.isMeetingSecret ?? this.meeting?.isMeetingSecret, ), @@ -956,12 +960,12 @@ export class RcVideo extends RcModuleV2 implements IMeeting { } get personalMeeting(): Partial { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'Partial | null' is not assignabl... Remove this comment to see the full error message return this._enablePersonalMeeting ? this.personalVideo : null; } get savedDefaultVideoSetting(): Partial { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'Partial | null' is not assi... Remove this comment to see the full error message return this._showSaveAsDefault ? this.savedDefaultSetting : null; } @@ -979,12 +983,12 @@ export class RcVideo extends RcModuleV2 implements IMeeting { } get extensionId(): number { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'number | undefined' is not assignable to typ... Remove this comment to see the full error message return this._deps.extensionInfo.info.id; } get accountId(): number { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'number | undefined' is not assignable to typ... Remove this comment to see the full error message return this._deps.accountInfo.id; } @@ -1014,7 +1018,7 @@ export class RcVideo extends RcModuleV2 implements IMeeting { // will follow dynamic brand config get enableE2EE() { - // @ts-expect-error + // @ts-expect-error TS(2339): Property 'enableE2EE' does not exist on type '{ id... Remove this comment to see the full error message return this._deps.brand.brandConfig?.enableE2EE ?? this._enableE2EE; } @@ -1115,7 +1119,6 @@ export class RcVideo extends RcModuleV2 implements IMeeting { meetingPassword: generateRandomPassword(10), startTime: new Date(getInitializedStartTime()), isMeetingPasswordValid: true, // generated random password is valid - // @ts-expect-error id: null, usePersonalMeetingId: false, settingLock: { @@ -1145,7 +1148,7 @@ export class RcVideo extends RcModuleV2 implements IMeeting { ]) get defaultTopic() { return getTopic({ - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'string | undefined' is not assignable to typ... Remove this comment to see the full error message extensionName: this.extensionName, brandName: this.brandName, shortName: this._deps.brand.shortName, diff --git a/packages/ringcentral-integration/modules/RcVideo/constants.ts b/packages/ringcentral-integration/modules/RcVideo/constants.ts index 10a67f9afd..b2528b4a40 100644 --- a/packages/ringcentral-integration/modules/RcVideo/constants.ts +++ b/packages/ringcentral-integration/modules/RcVideo/constants.ts @@ -53,13 +53,13 @@ const RCV_ITEM_NAME = { allowScreenSharing: 'allowScreenSharing', } as const; -type RcvItemType = typeof RCV_ITEM_NAME[keyof typeof RCV_ITEM_NAME]; +type RcvItemType = (typeof RCV_ITEM_NAME)[keyof typeof RCV_ITEM_NAME]; -type AUTH_USER = typeof AUTH_USER_TYPE[keyof typeof AUTH_USER_TYPE]; +type AUTH_USER = (typeof AUTH_USER_TYPE)[keyof typeof AUTH_USER_TYPE]; // eslint-disable-next-line max-len type RcvWaitingRoomModeProps = - typeof RCV_WAITING_ROOM_MODE[keyof typeof RCV_WAITING_ROOM_MODE]; + (typeof RCV_WAITING_ROOM_MODE)[keyof typeof RCV_WAITING_ROOM_MODE]; // eslint-disable-next-line max-len type RcvWaitingRoomType = Exclude; diff --git a/packages/ringcentral-integration/modules/RcVideo/i18n/de-DE.js b/packages/ringcentral-integration/modules/RcVideo/i18n/de-DE.js index 7ec0468811..d87547b6dd 100644 --- a/packages/ringcentral-integration/modules/RcVideo/i18n/de-DE.js +++ b/packages/ringcentral-integration/modules/RcVideo/i18n/de-DE.js @@ -1,5 +1,7 @@ export default { - tollFree: "Gebührenfrei" + tollFree: "Gebührenfrei", + rcvPmiMeetingTitle: "Videobesprechung von {extensionName}" }; // @key: @#@"tollFree"@#@ @source: @#@"Toll-Free"@#@ +// @key: @#@"rcvPmiMeetingTitle"@#@ @source: @#@"{extensionName}'s video meeting"@#@ diff --git a/packages/ringcentral-integration/modules/RcVideo/i18n/en-AU.js b/packages/ringcentral-integration/modules/RcVideo/i18n/en-AU.js index 7ee16f76b6..b4675bf8ef 100644 --- a/packages/ringcentral-integration/modules/RcVideo/i18n/en-AU.js +++ b/packages/ringcentral-integration/modules/RcVideo/i18n/en-AU.js @@ -1,5 +1,7 @@ export default { - tollFree: "Freephone" + tollFree: "Freephone", + rcvPmiMeetingTitle: "{extensionName}’s video meeting" }; // @key: @#@"tollFree"@#@ @source: @#@"Toll-Free"@#@ +// @key: @#@"rcvPmiMeetingTitle"@#@ @source: @#@"{extensionName}'s video meeting"@#@ diff --git a/packages/ringcentral-integration/modules/RcVideo/i18n/en-GB.js b/packages/ringcentral-integration/modules/RcVideo/i18n/en-GB.js index 7ee16f76b6..b4675bf8ef 100644 --- a/packages/ringcentral-integration/modules/RcVideo/i18n/en-GB.js +++ b/packages/ringcentral-integration/modules/RcVideo/i18n/en-GB.js @@ -1,5 +1,7 @@ export default { - tollFree: "Freephone" + tollFree: "Freephone", + rcvPmiMeetingTitle: "{extensionName}’s video meeting" }; // @key: @#@"tollFree"@#@ @source: @#@"Toll-Free"@#@ +// @key: @#@"rcvPmiMeetingTitle"@#@ @source: @#@"{extensionName}'s video meeting"@#@ diff --git a/packages/ringcentral-integration/modules/RcVideo/i18n/en-US.ts b/packages/ringcentral-integration/modules/RcVideo/i18n/en-US.ts index 9aec2f8945..690d1430de 100644 --- a/packages/ringcentral-integration/modules/RcVideo/i18n/en-US.ts +++ b/packages/ringcentral-integration/modules/RcVideo/i18n/en-US.ts @@ -1,3 +1,4 @@ export default { tollFree: 'Toll-Free', -}; + rcvPmiMeetingTitle: "{extensionName}'s video meeting", +} as const; diff --git a/packages/ringcentral-integration/modules/RcVideo/i18n/es-419.js b/packages/ringcentral-integration/modules/RcVideo/i18n/es-419.js index 0c4aa34c78..f3605b8452 100644 --- a/packages/ringcentral-integration/modules/RcVideo/i18n/es-419.js +++ b/packages/ringcentral-integration/modules/RcVideo/i18n/es-419.js @@ -1,5 +1,7 @@ export default { - tollFree: "Línea gratuita" + tollFree: "Línea gratuita", + rcvPmiMeetingTitle: "Reunión por video de {extensionName}" }; // @key: @#@"tollFree"@#@ @source: @#@"Toll-Free"@#@ +// @key: @#@"rcvPmiMeetingTitle"@#@ @source: @#@"{extensionName}'s video meeting"@#@ diff --git a/packages/ringcentral-integration/modules/RcVideo/i18n/es-ES.js b/packages/ringcentral-integration/modules/RcVideo/i18n/es-ES.js index 403d64af16..22b540f8a3 100644 --- a/packages/ringcentral-integration/modules/RcVideo/i18n/es-ES.js +++ b/packages/ringcentral-integration/modules/RcVideo/i18n/es-ES.js @@ -1,5 +1,7 @@ export default { - tollFree: "Gratuito" + tollFree: "Gratuito", + rcvPmiMeetingTitle: "Reunión de vídeo de {extensionName}" }; // @key: @#@"tollFree"@#@ @source: @#@"Toll-Free"@#@ +// @key: @#@"rcvPmiMeetingTitle"@#@ @source: @#@"{extensionName}'s video meeting"@#@ diff --git a/packages/ringcentral-integration/modules/RcVideo/i18n/fi-FI.js b/packages/ringcentral-integration/modules/RcVideo/i18n/fi-FI.js index 619f6e3dbf..fac70b3bf2 100644 --- a/packages/ringcentral-integration/modules/RcVideo/i18n/fi-FI.js +++ b/packages/ringcentral-integration/modules/RcVideo/i18n/fi-FI.js @@ -1,5 +1,7 @@ export default { - tollFree: "Maksuton" + tollFree: "Maksuton", + rcvPmiMeetingTitle: "{extensionName} – videotapaaminen" }; // @key: @#@"tollFree"@#@ @source: @#@"Toll-Free"@#@ +// @key: @#@"rcvPmiMeetingTitle"@#@ @source: @#@"{extensionName}'s video meeting"@#@ diff --git a/packages/ringcentral-integration/modules/RcVideo/i18n/fr-CA.js b/packages/ringcentral-integration/modules/RcVideo/i18n/fr-CA.js index 3859f23cf4..d64f1344db 100644 --- a/packages/ringcentral-integration/modules/RcVideo/i18n/fr-CA.js +++ b/packages/ringcentral-integration/modules/RcVideo/i18n/fr-CA.js @@ -1,5 +1,7 @@ export default { - tollFree: "Numéro sans frais" + tollFree: "Numéro sans frais", + rcvPmiMeetingTitle: "Réunion vidéo de {extensionName}" }; // @key: @#@"tollFree"@#@ @source: @#@"Toll-Free"@#@ +// @key: @#@"rcvPmiMeetingTitle"@#@ @source: @#@"{extensionName}'s video meeting"@#@ diff --git a/packages/ringcentral-integration/modules/RcVideo/i18n/fr-FR.js b/packages/ringcentral-integration/modules/RcVideo/i18n/fr-FR.js index 3e06997daf..af8d785a92 100644 --- a/packages/ringcentral-integration/modules/RcVideo/i18n/fr-FR.js +++ b/packages/ringcentral-integration/modules/RcVideo/i18n/fr-FR.js @@ -1,5 +1,7 @@ export default { - tollFree: "Gratuit" + tollFree: "Gratuit", + rcvPmiMeetingTitle: "Visioconférence de {extensionName}" }; // @key: @#@"tollFree"@#@ @source: @#@"Toll-Free"@#@ +// @key: @#@"rcvPmiMeetingTitle"@#@ @source: @#@"{extensionName}'s video meeting"@#@ diff --git a/packages/ringcentral-integration/modules/RcVideo/i18n/index.ts b/packages/ringcentral-integration/modules/RcVideo/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/ringcentral-integration/modules/RcVideo/i18n/index.ts +++ b/packages/ringcentral-integration/modules/RcVideo/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/ringcentral-integration/modules/RcVideo/i18n/it-IT.js b/packages/ringcentral-integration/modules/RcVideo/i18n/it-IT.js index 403d64af16..994fd7cab1 100644 --- a/packages/ringcentral-integration/modules/RcVideo/i18n/it-IT.js +++ b/packages/ringcentral-integration/modules/RcVideo/i18n/it-IT.js @@ -1,5 +1,7 @@ export default { - tollFree: "Gratuito" + tollFree: "Gratuito", + rcvPmiMeetingTitle: "Riunione video di {extensionName}" }; // @key: @#@"tollFree"@#@ @source: @#@"Toll-Free"@#@ +// @key: @#@"rcvPmiMeetingTitle"@#@ @source: @#@"{extensionName}'s video meeting"@#@ diff --git a/packages/ringcentral-integration/modules/RcVideo/i18n/ja-JP.js b/packages/ringcentral-integration/modules/RcVideo/i18n/ja-JP.js index 5a9d4487d9..e948fde513 100644 --- a/packages/ringcentral-integration/modules/RcVideo/i18n/ja-JP.js +++ b/packages/ringcentral-integration/modules/RcVideo/i18n/ja-JP.js @@ -1,5 +1,7 @@ export default { - tollFree: "フリーダイヤル" + tollFree: "フリーダイヤル", + rcvPmiMeetingTitle: "{extensionName}のビデオ会議" }; // @key: @#@"tollFree"@#@ @source: @#@"Toll-Free"@#@ +// @key: @#@"rcvPmiMeetingTitle"@#@ @source: @#@"{extensionName}'s video meeting"@#@ diff --git a/packages/ringcentral-integration/modules/RcVideo/i18n/ko-KR.js b/packages/ringcentral-integration/modules/RcVideo/i18n/ko-KR.js index 8f60322238..e8dcb711dc 100644 --- a/packages/ringcentral-integration/modules/RcVideo/i18n/ko-KR.js +++ b/packages/ringcentral-integration/modules/RcVideo/i18n/ko-KR.js @@ -1,5 +1,7 @@ export default { - tollFree: "무료" + tollFree: "무료", + rcvPmiMeetingTitle: "{extensionName} 님의 화상 모임" }; // @key: @#@"tollFree"@#@ @source: @#@"Toll-Free"@#@ +// @key: @#@"rcvPmiMeetingTitle"@#@ @source: @#@"{extensionName}'s video meeting"@#@ diff --git a/packages/ringcentral-integration/modules/RcVideo/i18n/nl-NL.js b/packages/ringcentral-integration/modules/RcVideo/i18n/nl-NL.js index 1b790557c4..beba25e9cc 100644 --- a/packages/ringcentral-integration/modules/RcVideo/i18n/nl-NL.js +++ b/packages/ringcentral-integration/modules/RcVideo/i18n/nl-NL.js @@ -1,5 +1,7 @@ export default { - tollFree: "Gratis" + tollFree: "Gratis", + rcvPmiMeetingTitle: "Videovergadering van {extensionName}" }; // @key: @#@"tollFree"@#@ @source: @#@"Toll-Free"@#@ +// @key: @#@"rcvPmiMeetingTitle"@#@ @source: @#@"{extensionName}'s video meeting"@#@ diff --git a/packages/ringcentral-integration/modules/RcVideo/i18n/pt-BR.js b/packages/ringcentral-integration/modules/RcVideo/i18n/pt-BR.js index 4a78d9f16b..a1a047489a 100644 --- a/packages/ringcentral-integration/modules/RcVideo/i18n/pt-BR.js +++ b/packages/ringcentral-integration/modules/RcVideo/i18n/pt-BR.js @@ -1,5 +1,7 @@ export default { - tollFree: "Chamada gratuita" + tollFree: "Chamada gratuita", + rcvPmiMeetingTitle: "Reunião por vídeo de {extensionName}" }; // @key: @#@"tollFree"@#@ @source: @#@"Toll-Free"@#@ +// @key: @#@"rcvPmiMeetingTitle"@#@ @source: @#@"{extensionName}'s video meeting"@#@ diff --git a/packages/ringcentral-integration/modules/RcVideo/i18n/pt-PT.js b/packages/ringcentral-integration/modules/RcVideo/i18n/pt-PT.js index 403d64af16..9398f7540d 100644 --- a/packages/ringcentral-integration/modules/RcVideo/i18n/pt-PT.js +++ b/packages/ringcentral-integration/modules/RcVideo/i18n/pt-PT.js @@ -1,5 +1,7 @@ export default { - tollFree: "Gratuito" + tollFree: "Gratuito", + rcvPmiMeetingTitle: "Videoconferência de {extensionName}" }; // @key: @#@"tollFree"@#@ @source: @#@"Toll-Free"@#@ +// @key: @#@"rcvPmiMeetingTitle"@#@ @source: @#@"{extensionName}'s video meeting"@#@ diff --git a/packages/ringcentral-integration/modules/RcVideo/i18n/zh-CN.js b/packages/ringcentral-integration/modules/RcVideo/i18n/zh-CN.js index e49749c388..18ea3456ae 100644 --- a/packages/ringcentral-integration/modules/RcVideo/i18n/zh-CN.js +++ b/packages/ringcentral-integration/modules/RcVideo/i18n/zh-CN.js @@ -1,5 +1,7 @@ export default { - tollFree: "免费电话" + tollFree: "免费电话", + rcvPmiMeetingTitle: "{extensionName} 的视频会议" }; // @key: @#@"tollFree"@#@ @source: @#@"Toll-Free"@#@ +// @key: @#@"rcvPmiMeetingTitle"@#@ @source: @#@"{extensionName}'s video meeting"@#@ diff --git a/packages/ringcentral-integration/modules/RcVideo/i18n/zh-HK.js b/packages/ringcentral-integration/modules/RcVideo/i18n/zh-HK.js index 1b130484bf..6c9154cb40 100644 --- a/packages/ringcentral-integration/modules/RcVideo/i18n/zh-HK.js +++ b/packages/ringcentral-integration/modules/RcVideo/i18n/zh-HK.js @@ -1,5 +1,7 @@ export default { - tollFree: "免付費電話" + tollFree: "免付費電話", + rcvPmiMeetingTitle: "{extensionName} 的視訊會議" }; // @key: @#@"tollFree"@#@ @source: @#@"Toll-Free"@#@ +// @key: @#@"rcvPmiMeetingTitle"@#@ @source: @#@"{extensionName}'s video meeting"@#@ diff --git a/packages/ringcentral-integration/modules/RcVideo/i18n/zh-TW.js b/packages/ringcentral-integration/modules/RcVideo/i18n/zh-TW.js index 1b130484bf..6c9154cb40 100644 --- a/packages/ringcentral-integration/modules/RcVideo/i18n/zh-TW.js +++ b/packages/ringcentral-integration/modules/RcVideo/i18n/zh-TW.js @@ -1,5 +1,7 @@ export default { - tollFree: "免付費電話" + tollFree: "免付費電話", + rcvPmiMeetingTitle: "{extensionName} 的視訊會議" }; // @key: @#@"tollFree"@#@ @source: @#@"Toll-Free"@#@ +// @key: @#@"rcvPmiMeetingTitle"@#@ @source: @#@"{extensionName}'s video meeting"@#@ diff --git a/packages/ringcentral-integration/modules/RcVideo/videoHelper.ts b/packages/ringcentral-integration/modules/RcVideo/videoHelper.ts index 50a5e02a7b..8ec4059456 100644 --- a/packages/ringcentral-integration/modules/RcVideo/videoHelper.ts +++ b/packages/ringcentral-integration/modules/RcVideo/videoHelper.ts @@ -1,6 +1,5 @@ -import { filter, map, omit, pick, pipe, toPairs } from 'ramda'; - import { format } from '@ringcentral-integration/utils'; +import { filter, map, omit, pick, pipe, toPairs } from 'ramda'; import type { MeetingProviderTypesProps, @@ -17,6 +16,8 @@ import type { RcVSettingLocks, RcVSettingLocksGET, } from '../../interfaces/Rcv.model'; + +import type { RcvInvitationRequestV2, TopicProps } from './RcVideo.interface'; import { RCV_E2EE_API_KEYS, RCV_PASSWORD_REGEX, @@ -25,7 +26,6 @@ import { RCV_WAITING_ROOM_MODE_REVERSE, } from './constants'; import i18n from './i18n'; -import type { RcvInvitationRequestV2, TopicProps } from './RcVideo.interface'; /* TODO: this meetingProviderTypes is only used for calender-addon * if you want to use meetingProviderTypes @@ -194,7 +194,7 @@ function getDefaultVideoSettings({ isOnlyCoworkersJoin: false, allowScreenSharing: true, waitingRoomMode: RCV_WAITING_ROOM_MODE.off, - // @ts-expect-error + // @ts-expect-error TS(2322): Type '{ allowJoinBeforeHost: false; isMeetingSecre... Remove this comment to see the full error message settingLock: { allowJoinBeforeHost: false, // muteVideo: false, @@ -254,23 +254,23 @@ function transformPreferences( isInstantMeeting = false, ): RcVPreferences { return { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'boolean | undefined' is not assignable to ty... Remove this comment to see the full error message allowJoinBeforeHost: preferences.join_before_host, // muteVideo: preferences.join_video_off, // muteAudio: preferences.join_audio_mute, e2ee: preferences.e2ee, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'boolean | undefined' is not assignable to ty... Remove this comment to see the full error message isMeetingSecret: isInstantMeeting ? preferences.password_instant : preferences.password_scheduled, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'boolean | undefined' is not assignable to ty... Remove this comment to see the full error message isOnlyAuthUserJoin: preferences.guest_join, isOnlyCoworkersJoin: preferences.guest_join ? preferences.join_authenticated_from_account_only === 'only_co_workers' : false, allowScreenSharing: preferences.screen_sharing_host_only === 'all', waitingRoomMode: preferences.waiting_room - ? // @ts-expect-error + ? // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. RCV_WAITING_ROOM_MODE[preferences.waiting_room_guests_only] : RCV_WAITING_ROOM_MODE.off, }; @@ -281,22 +281,22 @@ function transformSettingLocks( isInstantMeeting = false, ): RcVSettingLocks { return { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'boolean | undefined' is not assignable to ty... Remove this comment to see the full error message allowJoinBeforeHost: settingLocks.join_before_host, // muteVideo: settingLocks.join_video_off, // muteAudio: settingLocks.join_audio_mute, e2ee: settingLocks.e2ee, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'boolean | undefined' is not assignable to ty... Remove this comment to see the full error message isMeetingSecret: isInstantMeeting ? settingLocks.password_instant : settingLocks.password_scheduled, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'boolean | undefined' is not assignable to ty... Remove this comment to see the full error message isOnlyAuthUserJoin: settingLocks.guest_join, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'boolean | undefined' is not assignable to ty... Remove this comment to see the full error message isOnlyCoworkersJoin: settingLocks.join_authenticated_from_account_only, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'boolean | undefined' is not assignable to ty... Remove this comment to see the full error message allowScreenSharing: settingLocks.screen_sharing_host_only, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'boolean | undefined' is not assignable to ty... Remove this comment to see the full error message waitingRoomMode: settingLocks.waiting_room, }; } @@ -316,7 +316,7 @@ function reversePreferences( screen_sharing_host_only: preferences.allowScreenSharing ? 'all' : 'host', waiting_room: !!preferences.waitingRoomMode, waiting_room_guests_only: - // @ts-expect-error + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. RCV_WAITING_ROOM_MODE_REVERSE[preferences.waitingRoomMode], e2ee: preferences.e2ee, }; @@ -400,7 +400,7 @@ function patchWaitingRoomRelated( // for pmi setting, waitingRoom, joinAfterMe option maybe not avaliable if ( !getAvaliableWaitingRoomOpions(settings.isOnlyCoworkersJoin).includes( - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'number | undefined' is not assig... Remove this comment to see the full error message settings.waitingRoomMode, ) ) { @@ -428,7 +428,7 @@ function formatMainPhoneNumber( } if (!dialInNumber || dialInNumber.length === 0) { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'undefined' is not assignable to type 'string... Remove this comment to see the full error message return undefined; } diff --git a/packages/ringcentral-integration/modules/RecentCalls/RecentCalls.ts b/packages/ringcentral-integration/modules/RecentCalls/RecentCalls.ts index d2ed894295..f97425e6ab 100644 --- a/packages/ringcentral-integration/modules/RecentCalls/RecentCalls.ts +++ b/packages/ringcentral-integration/modules/RecentCalls/RecentCalls.ts @@ -8,7 +8,7 @@ import concurrentExecute from '../../lib/concurrentExecute'; import { Module } from '../../lib/di'; import getDateFrom from '../../lib/getDateFrom'; import type { HistoryCall } from '../CallHistory'; -import { callStatus } from './callStatus'; + import type { CleanUpCallsOptions, Deps, @@ -22,6 +22,7 @@ import { flattenToRecords, sortByTime, } from './RecentCallsHelper'; +import { callStatus } from './callStatus'; @Module({ name: 'RecentCalls', @@ -149,9 +150,11 @@ export class RecentCalls extends RcModuleV2 { // Get all calls related to this contact return calls.reduce((acc, call) => { if (call && call.to && call.from) { + // @ts-expect-error TS(2532): Object is possibly 'undefined'. const matches = phoneNumbers.find(filterPhoneNumber(call)); // Check if calls is within certain days + // @ts-expect-error TS(2769): No overload matches this call. if (!!matches && new Date(call.startTime) > dateFrom) { return acc.concat(call); } @@ -175,6 +178,7 @@ export class RecentCalls extends RcModuleV2 { }; // CallLog API doesn't support plus sign in phoneNumber + // @ts-expect-error TS(2532): Object is possibly 'undefined'. const recentCallsPromises = phoneNumbers.reduce( (acc, { phoneType, phoneNumber }) => { phoneNumber = phoneNumber.replace('+', ''); diff --git a/packages/ringcentral-integration/modules/RecentCalls/RecentCallsHelper.ts b/packages/ringcentral-integration/modules/RecentCalls/RecentCallsHelper.ts index 01fc13ac8a..6e82fc80d8 100644 --- a/packages/ringcentral-integration/modules/RecentCalls/RecentCallsHelper.ts +++ b/packages/ringcentral-integration/modules/RecentCalls/RecentCallsHelper.ts @@ -6,13 +6,18 @@ import type { HistoryCall } from '../CallHistory'; export const filterPhoneNumber = (call: HistoryCall) => ({ phoneNumber }: { phoneNumber: string }) => + // @ts-expect-error TS(2532): Object is possibly 'undefined'. phoneNumber === call.from.phoneNumber || + // @ts-expect-error TS(2532): Object is possibly 'undefined'. phoneNumber === call.to.phoneNumber || + // @ts-expect-error TS(2532): Object is possibly 'undefined'. phoneNumber === call.from.extensionNumber || + // @ts-expect-error TS(2532): Object is possibly 'undefined'. phoneNumber === call.to.extensionNumber; export const flattenToRecords = (items: CallLogResponse[]) => items.reduce( + // @ts-expect-error TS(2769): No overload matches this call. (acc, { records }) => acc.concat(records), [] as CallLogRecord[], ); @@ -22,13 +27,17 @@ export const flattenToRecords = (items: CallLogResponse[]) => export const sortByTime = ( a: { startTime?: number }, b: { startTime?: number }, + // @ts-expect-error TS(2769): No overload matches this call. ) => new Date(b.startTime).getTime() - new Date(a.startTime).getTime(); export const dedup = (calls: HistoryCall[]) => { const hash: Record = {}; return calls.reduce((acc, cur) => { + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. if (hash[cur.id]) return acc; + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. hash[cur.id] = true; + // @ts-expect-error TS(2769): No overload matches this call. return acc.concat(cur); }, []); }; diff --git a/packages/ringcentral-integration/modules/RecentMessages/RecentMessages.ts b/packages/ringcentral-integration/modules/RecentMessages/RecentMessages.ts index 5ea10b81db..edc6acbaf3 100644 --- a/packages/ringcentral-integration/modules/RecentMessages/RecentMessages.ts +++ b/packages/ringcentral-integration/modules/RecentMessages/RecentMessages.ts @@ -15,7 +15,7 @@ import getDateFrom from '../../lib/getDateFrom'; import { sortByDate } from '../../lib/messageHelper'; import { proxify } from '../../lib/proxy/proxify'; import type { MessageStoreConversations } from '../MessageStore'; -import { MessageStatus } from './messageStatus'; + import type { CleanUpMessagesOptions, Deps, @@ -25,6 +25,7 @@ import type { LoadSuccessOptions, RecentMessage, } from './RecentMessages.interface'; +import { MessageStatus } from './messageStatus'; import { dedup, filterPhoneNumber, @@ -56,6 +57,7 @@ export class RecentMessages extends RcModuleV2 { for (const key of Object.keys(this.contacts)) { this.getMessages({ currentContact: this.contacts[key], + // @ts-expect-error TS(2322): Type 'string | null' is not assignable to type 'st... Remove this comment to see the full error message sessionId: key.indexOf('-') > -1 ? key.split('-')[1] : null, fromLocal: false, forceUpdate: true, @@ -73,6 +75,7 @@ export class RecentMessages extends RcModuleV2 { messages: Record = {}; @state + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'MessageStat... Remove this comment to see the full error message messageStatus: MessageStatus = null; @action @@ -115,6 +118,7 @@ export class RecentMessages extends RcModuleV2 { @proxify async getMessages({ currentContact, + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'string'. sessionId = null, fromLocal = false, forceUpdate = false, @@ -143,6 +147,7 @@ export class RecentMessages extends RcModuleV2 { }); } + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'string'. cleanUpMessages({ contact, sessionId = null }: CleanUpMessagesOptions) { this.loadReset({ contact, @@ -181,7 +186,8 @@ export class RecentMessages extends RcModuleV2 { if (!fromLocal && recentMessages.length < length) { const dateTo = recentMessages.length > 0 - ? new Date(recentMessages[recentMessages.length - 1].creationTime) + ? // @ts-expect-error TS(2769): No overload matches this call. + new Date(recentMessages[recentMessages.length - 1].creationTime) : undefined; try { @@ -221,10 +227,13 @@ export class RecentMessages extends RcModuleV2 { const conversation = conversations[i]; const messageList = this._deps.messageStore.conversationStore[ + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. conversation.conversationId ] || []; + // @ts-expect-error TS(2532): Object is possibly 'undefined'. matches = phoneNumbers.find(filterPhoneNumber(conversation)); // Check if message is within certain days + // @ts-expect-error TS(2769): No overload matches this call. if (!!matches && new Date(conversation.creationTime) > dateFrom) { recentMessages = recentMessages.concat(messageList); } @@ -248,6 +257,7 @@ export class RecentMessages extends RcModuleV2 { messageType: ['SMS', 'Text', 'Pager'], perPage: length, }; + // @ts-expect-error TS(2532): Object is possibly 'undefined'. const recentMessagesPromise = phoneNumbers.reduce( (acc, { phoneNumber }) => { if (phoneNumber) { diff --git a/packages/ringcentral-integration/modules/RecentMessages/index.ts b/packages/ringcentral-integration/modules/RecentMessages/index.ts index 0f2f06503e..b4e826ec8d 100644 --- a/packages/ringcentral-integration/modules/RecentMessages/index.ts +++ b/packages/ringcentral-integration/modules/RecentMessages/index.ts @@ -1,4 +1,5 @@ export * from './RecentMessages'; +// @ts-expect-error TS(2308): Module './RecentMessages' has already exported a m... Remove this comment to see the full error message export * from './RecentMessages.interface'; export * from './messageStatus'; export * from './recentMessagesHelper'; diff --git a/packages/ringcentral-integration/modules/RecentMessages/recentMessagesHelper.ts b/packages/ringcentral-integration/modules/RecentMessages/recentMessagesHelper.ts index c0676b99cf..5d4a30cd70 100644 --- a/packages/ringcentral-integration/modules/RecentMessages/recentMessagesHelper.ts +++ b/packages/ringcentral-integration/modules/RecentMessages/recentMessagesHelper.ts @@ -4,18 +4,25 @@ import type GetMessageList from '@rc-ex/core/lib/definitions/GetMessageList'; import type { Entity } from '../../interfaces/Entity.interface'; import type { Message } from '../../interfaces/MessageStore.model'; import type { MessageStoreItem } from '../MessageStore'; + import type { RecentMessage } from './RecentMessages.interface'; export const filterPhoneNumber = (message: MessageStoreItem) => { + // @ts-expect-error TS(2537): Type 'EntityPhoneNumberItem[] | undefined' has no ... Remove this comment to see the full error message return ({ phoneNumber }: Entity['phoneNumbers'][number]) => + // @ts-expect-error TS(2532): Object is possibly 'undefined'. phoneNumber === message.from.phoneNumber || + // @ts-expect-error TS(2532): Object is possibly 'undefined'. !!message.to.find((to) => to.phoneNumber === phoneNumber) || + // @ts-expect-error TS(2532): Object is possibly 'undefined'. phoneNumber === message.from.extensionNumber || + // @ts-expect-error TS(2532): Object is possibly 'undefined'. !!message.to.find((to) => to.extensionNumber === phoneNumber); }; export const flattenToMessageRecords = (allMessages: GetMessageList[]) => { return allMessages.reduce( + // @ts-expect-error TS(2769): No overload matches this call. (acc, { records }) => acc.concat(records), [] as GetMessageInfoResponse[], ); @@ -25,6 +32,7 @@ export const sortMessages = (recentMessages: RecentMessage[]) => { // Sort by time in descending order return recentMessages.sort( (a, b) => + // @ts-expect-error TS(2769): No overload matches this call. new Date(b.creationTime).getTime() - new Date(a.creationTime).getTime(), ); }; @@ -41,8 +49,11 @@ export const markAsRemoteMessage = (messages: GetMessageInfoResponse[]) => { export const dedup = (messages: (Message | RecentMessage)[]) => { const hash: Record = {}; return messages.reduce((acc, cur) => { + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. if (hash[cur.id]) return acc; + // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. hash[cur.id] = true; + // @ts-expect-error TS(2769): No overload matches this call. return acc.concat(cur); }, []); }; diff --git a/packages/ringcentral-integration/modules/RegionSettings/RegionSettings.interface.ts b/packages/ringcentral-integration/modules/RegionSettings/RegionSettings.interface.ts index 5c9e4d037f..4a15bf24f2 100644 --- a/packages/ringcentral-integration/modules/RegionSettings/RegionSettings.interface.ts +++ b/packages/ringcentral-integration/modules/RegionSettings/RegionSettings.interface.ts @@ -25,7 +25,7 @@ export interface Deps { storage: Storage; tabManager?: TabManager; regionSettingsOptions?: RegionSettingsOptions; - extensionNumberAreaCode?: ExtensionNumberAreaCode; + extensionNumberAreaCode: ExtensionNumberAreaCode; extensionPhoneNumber: ExtensionPhoneNumber; appFeatures: AppFeatures; } diff --git a/packages/ringcentral-integration/modules/RegionSettings/RegionSettings.ts b/packages/ringcentral-integration/modules/RegionSettings/RegionSettings.ts index 6e8b735c51..43e10b9293 100644 --- a/packages/ringcentral-integration/modules/RegionSettings/RegionSettings.ts +++ b/packages/ringcentral-integration/modules/RegionSettings/RegionSettings.ts @@ -1,8 +1,4 @@ import type CountryInfoShortModel from '@rc-ex/core/lib/definitions/CountryInfoShortModel'; -import type { CountryCode } from 'libphonenumber-js'; -import { getCountryCallingCode, parsePhoneNumber } from 'libphonenumber-js'; -import { find, includes } from 'ramda'; - import { action, computed, @@ -11,10 +7,14 @@ import { storage, watch, } from '@ringcentral-integration/core'; +import type { CountryCode } from 'libphonenumber-js'; +import { getCountryCallingCode, parsePhoneNumber } from 'libphonenumber-js'; +import { find, includes } from 'ramda'; import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; import validateAreaCode from '../../lib/validateAreaCode'; + import type { Deps, RegionSettingsData } from './RegionSettings.interface'; import { regionSettingsMessages } from './regionSettingsMessages'; @@ -28,7 +28,7 @@ import { regionSettingsMessages } from './regionSettingsMessages'; 'Storage', 'ExtensionPhoneNumber', 'AppFeatures', - { dep: 'ExtensionNumberAreaCode', optional: true }, + 'ExtensionNumberAreaCode', { dep: 'TabManager', optional: true }, { dep: 'RegionSettingsOptions', optional: true }, ], @@ -212,7 +212,7 @@ export class RegionSettings extends RcModuleV2 { that.areaCode, that.countryCode, that._deps.appFeatures.isEDPEnabled, - that._deps.extensionNumberAreaCode?.defaultAreaCode, + that._deps.extensionNumberAreaCode.defaultAreaCode, ]) get defaultAreaCode() { const isEDPEnabled = this._deps.appFeatures.isEDPEnabled; @@ -239,7 +239,7 @@ export class RegionSettings extends RcModuleV2 { primaryNumberCallingCode === callingCode || mainNumberCallingCode === callingCode; if (canUseExtensionAreaCode) { - return this._deps.extensionNumberAreaCode?.defaultAreaCode; + return this._deps.extensionNumberAreaCode.defaultAreaCode; } } } diff --git a/packages/ringcentral-integration/modules/RingCentralExtensions/RingCentralExtensions.interface.ts b/packages/ringcentral-integration/modules/RingCentralExtensions/RingCentralExtensions.interface.ts index 5310bd6cdc..f301a7f59d 100644 --- a/packages/ringcentral-integration/modules/RingCentralExtensions/RingCentralExtensions.interface.ts +++ b/packages/ringcentral-integration/modules/RingCentralExtensions/RingCentralExtensions.interface.ts @@ -3,9 +3,9 @@ import type { WebSocketOptions } from '@rc-ex/ws/lib/types'; import type { RingCentralClient } from '../../lib/RingCentralClient'; import type { Auth } from '../Auth'; +import type { AvailabilityMonitor } from '../AvailabilityMonitor'; import type { SleepDetector } from '../SleepDetector'; import type { TabManager } from '../TabManager'; -import type { AvailabilityMonitor } from '../AvailabilityMonitor'; export interface RingCentralExtensionsOptions { debugMode?: boolean; diff --git a/packages/ringcentral-integration/modules/RingCentralExtensions/RingCentralExtensions.ts b/packages/ringcentral-integration/modules/RingCentralExtensions/RingCentralExtensions.ts index 1e35cd3e15..8a73e69420 100644 --- a/packages/ringcentral-integration/modules/RingCentralExtensions/RingCentralExtensions.ts +++ b/packages/ringcentral-integration/modules/RingCentralExtensions/RingCentralExtensions.ts @@ -1,5 +1,3 @@ -import WebSocket from 'isomorphic-ws'; - import CoreExtension from '@rc-ex/core'; import DebugExtension from '@rc-ex/debug'; import RcSdkExtension from '@rc-ex/rcsdk'; @@ -13,12 +11,14 @@ import { watch, } from '@ringcentral-integration/core'; import type { SDK } from '@ringcentral/sdk'; +import WebSocket from 'isomorphic-ws'; import background from '../../lib/background'; import { debounce } from '../../lib/debounce-throttle'; import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; import type { TabEvent } from '../TabManager'; + import type { Deps } from './RingCentralExtensions.interface'; import type { WebSocketReadyState } from './webSocketReadyStates'; import { webSocketReadyStates } from './webSocketReadyStates'; @@ -162,16 +162,21 @@ export class RingCentralExtensions extends RcModuleV2 { } // register SleepDetector - this._deps.sleepDetector?.on('detected', () => { - this.recoverWebSocketConnection(); + this._deps.sleepDetector?.on('detected', async () => { + await this.recoverWebSocketConnection(); }); // hook auth events - this._deps.auth.addAfterLoggedInHandler(() => { - this.recoverWebSocketConnection(); + this._deps.auth.addAfterLoggedInHandler(async () => { + await this.recoverWebSocketConnection(); }); - this._deps.auth.addBeforeLogoutHandler(() => { - this.revokeWebSocketConnection(); + this._deps.auth.addBeforeLogoutHandler(async () => { + await this.revokeWebSocketConnection(); + }); + this._deps.auth.addRefreshErrorHandler(async (refreshTokenValid) => { + if (!refreshTokenValid) { + await this.revokeWebSocketConnection(); + } }); // multiple tabs support @@ -197,7 +202,7 @@ export class RingCentralExtensions extends RcModuleV2 { if (process.env.NODE_ENV !== 'test') { console.log('[RingCentralExtensions] > tab > inactive'); } - await this._debouncedOnTabActive.cancel(); + this._debouncedOnTabActive.cancel(); } }, ); @@ -233,6 +238,18 @@ export class RingCentralExtensions extends RcModuleV2 { } } + private _setWsAutoRecover(enabled: boolean) { + // when auto recover is NOT configured as disabled (it is enabled by default) + if ( + this._webSocketExtension && + this._deps.ringCentralExtensionsOptions?.webSocketOptions?.autoRecover + ?.enabled !== false + ) { + // enable/disable ws auto recover + this._webSocketExtension.options.autoRecover!.enabled = enabled; + } + } + private _debouncedOnTabActive = debounce({ threshold: RECOVER_DEBOUNCE_THRESHOLD, fn: this._onTabActive, @@ -246,7 +263,7 @@ export class RingCentralExtensions extends RcModuleV2 { // as an active tab, inactive other tabs await this._inactiveOtherTabs(); // recover WebSocket for current tab and other tabs will being disconnected automatically - this.recoverWebSocketConnection(); + await this.recoverWebSocketConnection(); } private _tabMessageHandler(event: TabEvent) { @@ -255,7 +272,7 @@ export class RingCentralExtensions extends RcModuleV2 { } if (event.name === InactiveTabEventName) { // as an inactive tab, disable auto recover - this._webSocketExtension.options.autoRecover!.enabled = false; + this._setWsAutoRecover(false); } else if (event.name === SyncTokensTabEventName) { // as an inactive tab, sync and use with tokens that are received from active tab this._setTokens(event.args![0], event.args![1], event.args![2]); @@ -270,13 +287,7 @@ export class RingCentralExtensions extends RcModuleV2 { // inactive other tabs, for stopping WebSocket auto recover await this._deps.tabManager?.send(InactiveTabEventName); // when auto recover of active tab is NOT configured as disabled - if ( - this._deps.ringCentralExtensionsOptions?.webSocketOptions?.autoRecover - ?.enabled !== false - ) { - // enable auto recover - this._webSocketExtension.options.autoRecover!.enabled = true; - } + this._setWsAutoRecover(true); } private async _syncTokensToOtherTabs() { @@ -358,13 +369,14 @@ export class RingCentralExtensions extends RcModuleV2 { } else { // recover directly await this._webSocketExtension.recover(); + this._webSocketExtension.enable(); } this._exposeConnectionEvents(); } @proxify async revokeWebSocketConnection() { - if (!this.ready || !this.isWebSocketReady) { + if (!this.ready) { return; } if (this.disconnectOnInactive && !this.isTabActive) { diff --git a/packages/ringcentral-integration/modules/Ringout/Ringout.ts b/packages/ringcentral-integration/modules/Ringout/Ringout.ts index c3e88cd3a2..1077810cfa 100644 --- a/packages/ringcentral-integration/modules/Ringout/Ringout.ts +++ b/packages/ringcentral-integration/modules/Ringout/Ringout.ts @@ -1,10 +1,11 @@ -import type GetRingOutStatusResponse from '@rc-ex/core/lib/definitions/GetRingOutStatusResponse'; import type RestException from '@rc-ex/core/lib/RestException'; +import type GetRingOutStatusResponse from '@rc-ex/core/lib/definitions/GetRingOutStatusResponse'; import { action, RcModuleV2, state } from '@ringcentral-integration/core'; import { sleep } from '@ringcentral-integration/utils'; import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; + import type { Deps, MakeCallOptions } from './Ringout.interface'; import { ringoutErrors } from './ringoutErrors'; import { ringoutStatus } from './ringoutStatus'; @@ -66,10 +67,12 @@ export class Ringout extends RcModuleV2 { playPrompt: prompt, }); - if (this._deps.contactMatcher) { - await this._deps.contactMatcher.forceMatchBatchNumbers({ + try { + this._deps.contactMatcher?.forceMatchBatchNumbers({ phoneNumbers: [fromNumber, toNumber], }); + } catch (error) { + console.error('makeCall forceMatchBatchNumbers error', error); } const startTime = Date.now(); diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/RingtoneConfiguration.interface.ts b/packages/ringcentral-integration/modules/RingtoneConfiguration/RingtoneConfiguration.interface.ts new file mode 100644 index 0000000000..76fc0cd4fa --- /dev/null +++ b/packages/ringcentral-integration/modules/RingtoneConfiguration/RingtoneConfiguration.interface.ts @@ -0,0 +1,27 @@ +import type { AudioSettings } from '../AudioSettings'; +import type { Storage } from '../Storage'; +import type { Webphone } from '../Webphone'; + +export interface RingtoneConfigurationOptions { + enableCustomRingtone?: boolean; + defaultRingtoneList?: RingtoneItem[]; +} + +export interface Deps { + audioSettings: AudioSettings; + storage: Storage; + webphone: Webphone; + ringtoneConfigurationOptions?: RingtoneConfigurationOptions; +} + +export type RingtoneItem = { + id: string; + url: string; + type: 'custom' | 'default'; + name: string; +}; + +export type AudioInfo = { + fileName: string; + dataUrl: string; +}; diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/RingtoneConfiguration.ts b/packages/ringcentral-integration/modules/RingtoneConfiguration/RingtoneConfiguration.ts new file mode 100644 index 0000000000..0302bd8646 --- /dev/null +++ b/packages/ringcentral-integration/modules/RingtoneConfiguration/RingtoneConfiguration.ts @@ -0,0 +1,145 @@ +import { + action, + computed, + RcModuleV2, + state, + storage, + track, +} from '@ringcentral-integration/core'; + +import { trackEvents } from '../../enums/trackEvents'; +import { Module } from '../../lib/di'; +import { proxify } from '../../lib/proxy/proxify'; + +import type { + AudioInfo, + Deps, + RingtoneItem, +} from './RingtoneConfiguration.interface'; +import { DEFAULT_RINGTONE_LIST, RINGS_TYPE } from './const'; +import { getFileNameWithoutExt } from './helper'; + +@Module({ + name: 'RingtoneConfiguration', + deps: ['Storage', 'AudioSettings', 'Webphone'], +}) +export class RingtoneConfiguration extends RcModuleV2 { + constructor(deps: Deps) { + super({ + deps, + storageKey: 'RingtoneConfiguration', + enableCache: true, + }); + } + + override onInit() { + this.updateIncomingRingtone(); + } + + @storage + @state + selectedRingtoneId: string = this.defaultRingtoneList[0].id; + + @storage + @state + customRingtoneList: RingtoneItem[] = []; + + @action + private _setSelectedRingtoneId(id: string) { + this.selectedRingtoneId = id; + } + + @proxify + async setSelectedRingtoneId(id: string) { + this._setSelectedRingtoneId(id); + } + + @action + private _pushCustomRingtone(ringtone: RingtoneItem) { + this.customRingtoneList.push(ringtone); + } + + @action + private _removeCustomRingtone(id: string) { + this.customRingtoneList = this.customRingtoneList.filter( + (ringtone) => ringtone.id !== id, + ); + } + + @track(trackEvents.uploadRingtone) + @proxify + async uploadCustomRingtone(ringtone: RingtoneItem) { + this._pushCustomRingtone(ringtone); + } + + @track(trackEvents.deleteRingtone) + @proxify + async removeCustomRingtone(id: string) { + this._removeCustomRingtone(id); + } + + @proxify + async updateIncomingRingtone() { + if ( + this.selectedRingtoneAudio && + this.selectedRingtoneAudio.dataUrl !== this._deps.webphone.incomingAudio + ) { + this._deps.webphone.setIncomingAudio(this.selectedRingtoneAudio); + } + } + + @computed((that: RingtoneConfiguration) => [ + that.fullRingtoneList, + that.selectedRingtoneId, + ]) + get selectedRingtoneAudio(): AudioInfo | null { + const ringtone = this.fullRingtoneList.find( + (ringtone) => ringtone.id === this.selectedRingtoneId, + ); + if (!ringtone?.url) { + if (ringtone?.type === 'default' && ringtone?.id === RINGS_TYPE.Off) { + return { + fileName: RINGS_TYPE.Off, + dataUrl: '', + }; + } + return null; + } + return { + fileName: getFileNameWithoutExt(ringtone.name), + dataUrl: ringtone.url, + }; + } + + get defaultRingtoneList(): RingtoneItem[] { + return ( + this._deps.ringtoneConfigurationOptions?.defaultRingtoneList ?? + DEFAULT_RINGTONE_LIST + ).map((item) => ({ + ...item, + type: 'default', + name: item.id, + })); + } + + get customRingtoneSortedList() { + return this.customRingtoneList.sort((a, b) => { + const nameA = a.name!.toLowerCase(); + const nameB = b.name!.toLowerCase(); + + if (nameA < nameB) return -1; + if (nameA > nameB) return 1; + return 0; + }); + } + + get fullRingtoneList() { + return [...this.customRingtoneSortedList, ...this.defaultRingtoneList]; + } + + get enableCustomRingtone() { + return ( + this._deps.ringtoneConfigurationOptions?.enableCustomRingtone ?? true + ); + } +} diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/AcousticDreams.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/AcousticDreams.mp3 new file mode 100644 index 0000000000..9bed68065d Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/AcousticDreams.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/AirRaid.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/AirRaid.mp3 new file mode 100644 index 0000000000..ddb8b35f09 Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/AirRaid.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Allusive.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Allusive.mp3 new file mode 100644 index 0000000000..400fca9ab3 Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Allusive.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Attention.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Attention.mp3 new file mode 100644 index 0000000000..9e8cd6ddcf Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Attention.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/BlubBlub.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/BlubBlub.mp3 new file mode 100644 index 0000000000..7498526fd2 Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/BlubBlub.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Buzzy.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Buzzy.mp3 new file mode 100644 index 0000000000..ba6eda3934 Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Buzzy.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/ChannelOpen.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/ChannelOpen.mp3 new file mode 100644 index 0000000000..72625d94aa Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/ChannelOpen.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Contemplation.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Contemplation.mp3 new file mode 100644 index 0000000000..e4115589de Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Contemplation.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/CrystalBall.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/CrystalBall.mp3 new file mode 100644 index 0000000000..4c53a3e2f1 Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/CrystalBall.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Disco.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Disco.mp3 new file mode 100644 index 0000000000..7ec2d55b8b Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Disco.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/DoorBell.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/DoorBell.mp3 new file mode 100644 index 0000000000..b66c510f1c Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/DoorBell.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Fairy.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Fairy.mp3 new file mode 100644 index 0000000000..11c0beb3f5 Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Fairy.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/FastBells.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/FastBells.mp3 new file mode 100644 index 0000000000..7113a1ac5f Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/FastBells.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/HighGong.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/HighGong.mp3 new file mode 100644 index 0000000000..c2444e34d4 Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/HighGong.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Immersion.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Immersion.mp3 new file mode 100644 index 0000000000..c7278d13e1 Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Immersion.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Indeed.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Indeed.mp3 new file mode 100644 index 0000000000..c6e146af45 Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Indeed.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/LazyDay.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/LazyDay.mp3 new file mode 100644 index 0000000000..2101ee41ce Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/LazyDay.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/NeuralFunk.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/NeuralFunk.mp3 new file mode 100644 index 0000000000..d5da3c38cc Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/NeuralFunk.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Nice.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Nice.mp3 new file mode 100644 index 0000000000..3960796a0a Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Nice.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/PhoneRing.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/PhoneRing.mp3 new file mode 100644 index 0000000000..3e9663f2d7 Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/PhoneRing.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Ring.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Ring.mp3 new file mode 100644 index 0000000000..db697e1eb5 Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Ring.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/RingingBells.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/RingingBells.mp3 new file mode 100644 index 0000000000..234c781582 Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/RingingBells.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Simple.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Simple.mp3 new file mode 100644 index 0000000000..6f7d16524e Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Simple.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Soothing.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Soothing.mp3 new file mode 100644 index 0000000000..b60a4c6ad0 Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Soothing.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Sunshine.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Sunshine.mp3 new file mode 100644 index 0000000000..7dd7cbf632 Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/Sunshine.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/incoming.mp3 b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/incoming.mp3 new file mode 100644 index 0000000000..ef38076bed Binary files /dev/null and b/packages/ringcentral-integration/modules/RingtoneConfiguration/audio/incoming.mp3 differ diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/const.ts b/packages/ringcentral-integration/modules/RingtoneConfiguration/const.ts new file mode 100644 index 0000000000..adc8839739 --- /dev/null +++ b/packages/ringcentral-integration/modules/RingtoneConfiguration/const.ts @@ -0,0 +1,90 @@ +import acousticDreamsMP3 from './audio/AcousticDreams.mp3'; +import airRaidMP3 from './audio/AirRaid.mp3'; +import allusiveMP3 from './audio/Allusive.mp3'; +import attentionMP3 from './audio/Attention.mp3'; +import blubBlubMP3 from './audio/BlubBlub.mp3'; +import buzzyMP3 from './audio/Buzzy.mp3'; +import channelOpenMP3 from './audio/ChannelOpen.mp3'; +import contemplationMP3 from './audio/Contemplation.mp3'; +import crystalBallMP3 from './audio/CrystalBall.mp3'; +import discoMP3 from './audio/Disco.mp3'; +import doorBellMP3 from './audio/DoorBell.mp3'; +import fabMP3 from './audio/Fairy.mp3'; +import fastBellsMP3 from './audio/FastBells.mp3'; +import highGongMP3 from './audio/HighGong.mp3'; +import immersionMP3 from './audio/Immersion.mp3'; +import indeedMP3 from './audio/Indeed.mp3'; +import lazyDayMP3 from './audio/LazyDay.mp3'; +import neuralFunkMP3 from './audio/NeuralFunk.mp3'; +import niceMP3 from './audio/Nice.mp3'; +import phoneRingMP3 from './audio/PhoneRing.mp3'; +import ringMP3 from './audio/Ring.mp3'; +import ringingBellsMP3 from './audio/RingingBells.mp3'; +import simpleMP3 from './audio/Simple.mp3'; +import soothingMP3 from './audio/Soothing.mp3'; +import sunshineMP3 from './audio/Sunshine.mp3'; +import incomingMP3 from './audio/incoming.mp3'; + +export enum RINGS_TYPE { + Phone_Ring1 = 'phone_ring1', + Phone_Ring2 = 'phone_ring2', + Acoustic_Dreams = 'acoustic_dreams', + Air_Raid = 'air_raid', + Allusive = 'allusive', + Attention = 'attention', + Blub_Blub = 'blub_blub', + Buzzy = 'buzzy', + Channel_Open = 'channel_open', + Contemplation = 'contemplation', + Crystal_Ball = 'crystal_ball', + Disco = 'disco', + Door_Bell = 'door_bell', + Fairy = 'fairy', + Fast_Bells = 'fast_bells', + High_Gong = 'high_gong', + Immersion = 'immersion', + Indeed = 'indeed', + Lazy_Day = 'lazy_day', + Neural_Funk = 'neural_funk', + Nice = 'nice', + Ring = 'ring', + Ringing_Bells = 'ringing_bells', + Simple = 'simple', + Soothing = 'soothing', + Sunshine = 'sunshine', + Off = 'off', +} + +export const DEFAULT_RINGTONE_LIST = [ + { id: RINGS_TYPE.Phone_Ring1, url: incomingMP3 }, + { id: RINGS_TYPE.Phone_Ring2, url: phoneRingMP3 }, + { id: RINGS_TYPE.Acoustic_Dreams, url: acousticDreamsMP3 }, + { id: RINGS_TYPE.Air_Raid, url: airRaidMP3 }, + { id: RINGS_TYPE.Allusive, url: allusiveMP3 }, + { id: RINGS_TYPE.Attention, url: attentionMP3 }, + { id: RINGS_TYPE.Blub_Blub, url: blubBlubMP3 }, + { id: RINGS_TYPE.Buzzy, url: buzzyMP3 }, + { id: RINGS_TYPE.Channel_Open, url: channelOpenMP3 }, + { id: RINGS_TYPE.Contemplation, url: contemplationMP3 }, + { id: RINGS_TYPE.Crystal_Ball, url: crystalBallMP3 }, + { id: RINGS_TYPE.Disco, url: discoMP3 }, + { id: RINGS_TYPE.Door_Bell, url: doorBellMP3 }, + { id: RINGS_TYPE.Fairy, url: fabMP3 }, + { id: RINGS_TYPE.Fast_Bells, url: fastBellsMP3 }, + { id: RINGS_TYPE.High_Gong, url: highGongMP3 }, + { id: RINGS_TYPE.Immersion, url: immersionMP3 }, + { id: RINGS_TYPE.Indeed, url: indeedMP3 }, + { id: RINGS_TYPE.Lazy_Day, url: lazyDayMP3 }, + { id: RINGS_TYPE.Neural_Funk, url: neuralFunkMP3 }, + { id: RINGS_TYPE.Nice, url: niceMP3 }, + { id: RINGS_TYPE.Ring, url: ringMP3 }, + { id: RINGS_TYPE.Ringing_Bells, url: ringingBellsMP3 }, + { id: RINGS_TYPE.Simple, url: simpleMP3 }, + { id: RINGS_TYPE.Soothing, url: soothingMP3 }, + { id: RINGS_TYPE.Sunshine, url: sunshineMP3 }, + { id: RINGS_TYPE.Off, url: '' }, +]; + +export const MAX_CUSTOM_RINGTONE_COUNT = 10; + +export const MAX_RINGTONE_SIZE = 5 * 1024 * 1024; diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/helper.ts b/packages/ringcentral-integration/modules/RingtoneConfiguration/helper.ts new file mode 100644 index 0000000000..47d93b33d0 --- /dev/null +++ b/packages/ringcentral-integration/modules/RingtoneConfiguration/helper.ts @@ -0,0 +1,80 @@ +import { AudioInfo } from './RingtoneConfiguration.interface'; + +export const isMp3 = (buf: Uint8Array | null): boolean => { + if (!buf || buf.length < 3) { + return false; + } + + return ( + (buf[0] === 0x49 && buf[1] === 0x44 && buf[2] === 0x33) || + (buf[0] === 0xff && (buf[1] & 0xe0) === 0xe0) || + (buf[0] === 0x54 && buf[1] === 0x41 && buf[2] === 0x47) + ); +}; + +export const isWav = (buf: Uint8Array | null): boolean => { + if (!buf || buf.length < 12) { + return false; + } + + return ( + buf[0] === 82 && + buf[1] === 73 && + buf[2] === 70 && + buf[3] === 70 && + buf[8] === 87 && + buf[9] === 65 && + buf[10] === 86 && + buf[11] === 69 + ); +}; + +export const fileToArrayBuffer = async (file: File): Promise => { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + + reader.onload = (event) => { + if (event.target?.result instanceof ArrayBuffer) { + resolve(event.target.result); + } else { + reject(new Error('Failed to convert file to ArrayBuffer.')); + } + }; + + reader.onerror = (event) => { + reject(new Error('Error reading file: ' + event.target?.error)); + }; + + reader.readAsArrayBuffer(file); + }); +}; + +export const isAudioFile = async (file: File): Promise => { + const arrayBuffer = await fileToArrayBuffer(file); + const signature = new Uint8Array(arrayBuffer); + + return isMp3(signature) || isWav(signature); +}; + +export const readAudioFile = async (file: File): Promise => { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + + reader.onload = () => { + resolve({ + fileName: file.name, + dataUrl: reader.result as string, + }); + }; + reader.onerror = () => { + reject('upload failed'); + }; + reader.readAsDataURL(file); + }); +}; + +export const getFileNameWithoutExt = (fileName: string) => { + const lastDotIndex = fileName.lastIndexOf('.'); + if (lastDotIndex === -1) return fileName; + return fileName.substring(0, lastDotIndex); +}; diff --git a/packages/ringcentral-integration/modules/RingtoneConfiguration/index.ts b/packages/ringcentral-integration/modules/RingtoneConfiguration/index.ts new file mode 100644 index 0000000000..2171fc99a9 --- /dev/null +++ b/packages/ringcentral-integration/modules/RingtoneConfiguration/index.ts @@ -0,0 +1,4 @@ +export * from './RingtoneConfiguration'; +export * from './RingtoneConfiguration.interface'; +export * from './const'; +export * from './helper'; diff --git a/packages/ringcentral-integration/modules/SleepDetector/SleepDetector.ts b/packages/ringcentral-integration/modules/SleepDetector/SleepDetector.ts index fc697f2ddf..f70b0cf3d7 100644 --- a/packages/ringcentral-integration/modules/SleepDetector/SleepDetector.ts +++ b/packages/ringcentral-integration/modules/SleepDetector/SleepDetector.ts @@ -1,7 +1,8 @@ import { RcModuleV2 } from '@ringcentral-integration/core'; -import { Module } from '../../lib/di'; import { SleepDetection } from '../../lib/SleepDetection'; +import { Module } from '../../lib/di'; + import type { Deps } from './SleepDetector.interface'; @Module({ diff --git a/packages/ringcentral-integration/modules/Softphone/Softphone.ts b/packages/ringcentral-integration/modules/Softphone/Softphone.ts index 1fb69d1035..bdfb381398 100644 --- a/packages/ringcentral-integration/modules/Softphone/Softphone.ts +++ b/packages/ringcentral-integration/modules/Softphone/Softphone.ts @@ -1,11 +1,11 @@ -import bowser from 'bowser'; - import { action, RcModuleV2, state } from '@ringcentral-integration/core'; -import { sleep } from '@ringcentral-integration/utils'; +import { sleep, normalizeUniversalLink } from '@ringcentral-integration/utils'; +import bowser from 'bowser'; import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; import { callingModes } from '../CallingSettings/callingModes'; + import type { CallHandlerContext, CallUriInfo, @@ -33,15 +33,15 @@ export class Softphone extends RcModuleV2 { super({ deps, }); - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type 'ContactMatcher | u... Remove this comment to see the full error message this._ignoreModuleReadiness(deps.contactMatcher); this._extensionMode = this._deps.softphoneOptions?.extensionMode ?? false; - // @ts-expect-error + // @ts-expect-error TS(2322): Type '((context: CallHandlerContext) => any) | und... Remove this comment to see the full error message this._callHandler = this._deps.softphoneOptions?.callHandler; } @state - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'string'. connectingPhoneNumber: string = null; @state @@ -56,7 +56,7 @@ export class Softphone extends RcModuleV2 { @action connectComplete() { this.softphoneStatus = softphoneStatus.idle; - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'string'. this.connectingPhoneNumber = null; } @@ -67,7 +67,7 @@ export class Softphone extends RcModuleV2 { } get spartanProtocol() { - // @ts-expect-error + // @ts-expect-error TS(2532): Object is possibly 'undefined'. return this._deps.brand.brandConfig.callWithSoftphone.protocol; } @@ -90,6 +90,7 @@ export class Softphone extends RcModuleV2 { let command = `call?number=${encodeURIComponent(phoneNumber)}`; let protocol = this.spartanProtocol; let isJupiterUniversalLink = false; + // jupiter const isCallWithJupiter = [ callingModes.jupiter, @@ -98,11 +99,11 @@ export class Softphone extends RcModuleV2 { if (isCallWithJupiter) { // jupiter doesn't recognize encoded string for now command = `r/call?number=${phoneNumber}`; - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'boolean | undefined' is not assignable to ty... Remove this comment to see the full error message isJupiterUniversalLink = this._useJupiterUniversalLink(callingMode); - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'string | undefined' is not assignable to typ... Remove this comment to see the full error message protocol = isJupiterUniversalLink - ? this.jupiterUniversalLink + ? normalizeUniversalLink(this.jupiterUniversalLink!) : this.jupiterProtocol; } return { @@ -158,10 +159,10 @@ export class Softphone extends RcModuleV2 { if (openLink) { window.open(uri); - // @ts-expect-error + // @ts-expect-error TS(2339): Property 'msLaunchUri' does not exist on type 'Nav... Remove this comment to see the full error message } else if (window.navigator.msLaunchUri) { // to support ie to start the service - // @ts-expect-error + // @ts-expect-error TS(2339): Property 'msLaunchUri' does not exist on type 'Nav... Remove this comment to see the full error message window.navigator.msLaunchUri(uri); } else { // open via iframe @@ -169,7 +170,7 @@ export class Softphone extends RcModuleV2 { frame.style.display = 'none'; document.body.appendChild(frame); await sleep(100); - // @ts-expect-error + // @ts-expect-error TS(2531): Object is possibly 'null'. frame.contentWindow.location.href = uri; await sleep(300); document.body.removeChild(frame); diff --git a/packages/ringcentral-integration/modules/Storage/Storage.ts b/packages/ringcentral-integration/modules/Storage/Storage.ts index 2def2996d8..9fbb86c4d8 100644 --- a/packages/ringcentral-integration/modules/Storage/Storage.ts +++ b/packages/ringcentral-integration/modules/Storage/Storage.ts @@ -1,8 +1,9 @@ // @ts-nocheck -import { Module } from '../../lib/di'; import type { IStorage } from '../../lib/StorageBase'; import { StorageBase } from '../../lib/StorageBase'; +import { Module } from '../../lib/di'; import { loginStatus } from '../Auth'; + import type { Deps } from './Storage.interface'; const dataFetcherKey = 'dataFetcherV2-storageData'; diff --git a/packages/ringcentral-integration/modules/Subscription/Subscription.ts b/packages/ringcentral-integration/modules/Subscription/Subscription.ts index 790d23b759..3ed0057fa8 100644 --- a/packages/ringcentral-integration/modules/Subscription/Subscription.ts +++ b/packages/ringcentral-integration/modules/Subscription/Subscription.ts @@ -1,5 +1,3 @@ -import { concat, equals, map, uniq } from 'ramda'; - import { action, RcModuleV2, @@ -10,6 +8,7 @@ import type { ObjectMapValue } from '@ringcentral-integration/core/lib/ObjectMap import type { ApiError } from '@ringcentral/sdk'; import Subscriptions from '@ringcentral/subscriptions'; import type { SubscriptionData } from '@ringcentral/subscriptions/src/subscription/Subscription'; +import { concat, equals, map, uniq } from 'ramda'; import type { subscriptionFilters } from '../../enums/subscriptionFilters'; import type { @@ -19,8 +18,9 @@ import type { import { debounce, promisedDebounce } from '../../lib/debounce-throttle'; import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; -import { normalizeEventFilter } from './normalizeEventFilter'; + import type { Deps, MessageBase } from './Subscription.interface'; +import { normalizeEventFilter } from './normalizeEventFilter'; import { subscriptionStatus } from './subscriptionStatus'; const DEFAULT_TIME_TO_RETRY = 20 * 1000; @@ -38,11 +38,11 @@ const SUBSCRIPTION_LOCK_KEY = 'subscription-creating-lock'; ], }) export class Subscription extends RcModuleV2 { - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Subscriptio... Remove this comment to see the full error message protected _subscription: ReturnType = null; - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Timeout'. protected _retryTimeoutId: NodeJS.Timeout = null; protected _debouncedRegister: PromisedDebounceFunction< @@ -74,7 +74,7 @@ export class Subscription extends RcModuleV2 { } @state - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'MessageBase... Remove this comment to see the full error message message: MessageBase = null; @state @@ -86,7 +86,7 @@ export class Subscription extends RcModuleV2 { @storage @state - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Subscriptio... Remove this comment to see the full error message cachedSubscription: SubscriptionData = null; @state @@ -158,7 +158,7 @@ export class Subscription extends RcModuleV2 { override async onReset() { this._setStates({ filters: [], - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'MessageBase... Remove this comment to see the full error message message: null, status: subscriptionStatus.notSubscribed, }); @@ -172,7 +172,7 @@ export class Subscription extends RcModuleV2 { if (this._subscription) { this._subscription.reset(); this._subscription.removeAllListeners(); - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Subscriptio... Remove this comment to see the full error message this._subscription = null; } } @@ -180,7 +180,7 @@ export class Subscription extends RcModuleV2 { protected _onRemoveSuccess() { this._setStates({ status: subscriptionStatus.notSubscribed, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Subscriptio... Remove this comment to see the full error message cachedSubscription: null, }); } @@ -188,7 +188,7 @@ export class Subscription extends RcModuleV2 { protected _onRemoveError(error: ApiError | Error) { this._setStates({ status: subscriptionStatus.notSubscribed, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Subscriptio... Remove this comment to see the full error message cachedSubscription: null, }); } @@ -206,12 +206,12 @@ export class Subscription extends RcModuleV2 { if (this._subscription) { this._subscription.reset(); this._subscription.removeAllListeners(); - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Subscriptio... Remove this comment to see the full error message this._subscription = null; } this._setStates({ status: subscriptionStatus.notSubscribed, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Subscriptio... Remove this comment to see the full error message cachedSubscription: null, }); if (this.ready) { @@ -233,7 +233,7 @@ export class Subscription extends RcModuleV2 { protected _onSubscribeError(error: ApiError | Error) { this._setStates({ status: subscriptionStatus.notSubscribed, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Subscriptio... Remove this comment to see the full error message cachedSubscription: null, }); if (this.ready) { @@ -338,7 +338,7 @@ export class Subscription extends RcModuleV2 { if (this._subscription) { this._subscription.reset(); this._subscription.removeAllListeners(); - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'Subscriptio... Remove this comment to see the full error message this._subscription = null; } this._setStates({ @@ -352,7 +352,7 @@ export class Subscription extends RcModuleV2 { if (this.ready) { const oldFiltersCount = this._subscription?.eventFilters().length ?? 0; // use [].concat for potential compatibility issue - // @ts-expect-error + // @ts-expect-error TS(2769): No overload matches this call. this._addFilters([].concat(events)); if (oldFiltersCount !== this.filters.length) { await this._createSubscriptionWithLock(); diff --git a/packages/ringcentral-integration/modules/TabManager/TabManager.ts b/packages/ringcentral-integration/modules/TabManager/TabManager.ts index e3d28b1b44..fbe22fb31e 100644 --- a/packages/ringcentral-integration/modules/TabManager/TabManager.ts +++ b/packages/ringcentral-integration/modules/TabManager/TabManager.ts @@ -1,6 +1,4 @@ // @ts-nocheck -import { combineReducers } from 'redux'; - import { action, identifierKey, @@ -10,10 +8,12 @@ import { state, usmAction, } from '@ringcentral-integration/core'; +import { combineReducers } from 'redux'; +import { Tabbie } from '../../lib/Tabbie'; import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; -import { Tabbie } from '../../lib/Tabbie'; + import type { Deps, TabEvent } from './TabManager.interface'; @Module({ diff --git a/packages/ringcentral-integration/modules/Theme/Theme.interface.ts b/packages/ringcentral-integration/modules/Theme/Theme.interface.ts index d53c2aa4f6..ce0697a612 100644 --- a/packages/ringcentral-integration/modules/Theme/Theme.interface.ts +++ b/packages/ringcentral-integration/modules/Theme/Theme.interface.ts @@ -1,6 +1,13 @@ +import type { RcTheme } from '@ringcentral/juno'; + import type { Brand } from '../Brand'; -export interface ThemeOptions {} +export interface ThemeOptions { + /** + * process theme before set to juno + */ + processTheme?: (type: string, theme?: RcTheme) => RcTheme | undefined; +} export interface Deps { brand: Brand; themeOptions?: ThemeOptions; diff --git a/packages/ringcentral-integration/modules/Theme/Theme.ts b/packages/ringcentral-integration/modules/Theme/Theme.ts index 829799081e..b49919481f 100644 --- a/packages/ringcentral-integration/modules/Theme/Theme.ts +++ b/packages/ringcentral-integration/modules/Theme/Theme.ts @@ -6,12 +6,14 @@ import { state, watch, } from '@ringcentral-integration/core'; -import type { RcTheme } from '@ringcentral/juno'; +import { createTheme, type RcTheme } from '@ringcentral/juno'; import { Module } from '../../lib/di'; +import type { BrandThemeMap } from '../Brand/Brand.interface'; import type { CssModuleVariable } from '../Brand/BrandConfig.interface'; -import { defaultCssVariable } from './defaultCssVariable'; + import type { Deps } from './Theme.interface'; +import { defaultCssVariable } from './defaultCssVariable'; @Module({ name: 'Theme', @@ -26,6 +28,15 @@ export class Theme extends RcModuleV2 { }); } + @globalStorage + @state + themeId: string | null = null; + + @action + setThemeId(val: string | null) { + this.themeId = val; + } + @globalStorage @state themeType = ''; @@ -48,18 +59,33 @@ export class Theme extends RcModuleV2 { const newDefaultThemeType = newValue?.defaultTheme; if (newDefaultThemeType && newDefaultThemeType !== this.themeType) { - this.setThemeType(newValue.defaultTheme); + this.setThemeType(newDefaultThemeType); } }, ); } + @computed((that: Theme) => [ + that.themeType, + that._deps.brand.themeMap, + that._deps.brand.brandConfig.theme?.themeMap, + that._deps.brand.defaultConfig.theme?.themeMap, + ]) get theme() { - const curr = this._deps.brand.brandConfig.theme?.themeMap?.[ - this.themeType - ] as any; + const themeType = this.themeType as keyof BrandThemeMap; + + // when themeType not be set, use light as default theme to find correct theme + const targetThemeType = themeType || 'light'; + const curr = (this._deps.brand.brandConfig.theme?.themeMap?.[ + targetThemeType + ] || + this._deps.brand.defaultConfig.theme?.themeMap?.[targetThemeType] || + // when still not found, use default juno theme + // we must have default theme, that will use in c2d variable + createTheme()) as RcTheme; - return curr as RcTheme; + const processTheme = this._deps.themeOptions?.processTheme; + return processTheme ? processTheme(targetThemeType, curr) : curr; } @computed((that: Theme) => [that._deps.brand.brandConfig.theme?.variable]) diff --git a/packages/ringcentral-integration/modules/TierChecker/TierChecker.ts b/packages/ringcentral-integration/modules/TierChecker/TierChecker.ts index 7cd81ac94a..fb2bc471f4 100644 --- a/packages/ringcentral-integration/modules/TierChecker/TierChecker.ts +++ b/packages/ringcentral-integration/modules/TierChecker/TierChecker.ts @@ -3,6 +3,7 @@ import { RcModuleV2, watch } from '@ringcentral-integration/core'; import { permissionsMessages } from '../../enums/permissionsMessages'; import { Module } from '../../lib/di'; import { loginStatus } from '../Auth'; + import type { Deps } from './TierChecker.interface'; @Module({ diff --git a/packages/ringcentral-integration/modules/Timezone/Timezone.ts b/packages/ringcentral-integration/modules/Timezone/Timezone.ts index 3a03031085..6cb851c27f 100644 --- a/packages/ringcentral-integration/modules/Timezone/Timezone.ts +++ b/packages/ringcentral-integration/modules/Timezone/Timezone.ts @@ -1,6 +1,5 @@ import type ITimezone from '@rc-ex/core/lib/definitions/GetTimezoneInfoResponse'; import type ITimezoneList from '@rc-ex/core/lib/definitions/GetTimezoneListResponse'; - import { action, computed, @@ -11,6 +10,7 @@ import { import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; + import type { Deps } from './Timezone.interface'; const CACHE_TTL = 60 * 60e3; diff --git a/packages/ringcentral-integration/modules/UserGuide/UserGuide.interface.ts b/packages/ringcentral-integration/modules/UserGuide/UserGuide.interface.ts index 8510162f60..6a24558073 100644 --- a/packages/ringcentral-integration/modules/UserGuide/UserGuide.interface.ts +++ b/packages/ringcentral-integration/modules/UserGuide/UserGuide.interface.ts @@ -5,10 +5,7 @@ import type { Locale } from '../Locale'; import type { Storage } from '../Storage'; import type { Webphone } from '../Webphone'; -export interface UserGuideOptions { - // TODO: fix type with `@types/webpack-env` - `RequireContext` - context: any; -} +export interface UserGuideOptions {} export interface Deps { auth: Auth; diff --git a/packages/ringcentral-integration/modules/UserGuide/UserGuide.ts b/packages/ringcentral-integration/modules/UserGuide/UserGuide.ts index 7740faa07f..114085ef17 100644 --- a/packages/ringcentral-integration/modules/UserGuide/UserGuide.ts +++ b/packages/ringcentral-integration/modules/UserGuide/UserGuide.ts @@ -1,5 +1,3 @@ -import { includes } from 'ramda'; - import { action, computed, @@ -9,10 +7,12 @@ import { track, watch, } from '@ringcentral-integration/core'; +import { includes } from 'ramda'; +import { trackEvents } from '../../enums/trackEvents'; import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; -import { trackEvents } from '../../enums/trackEvents'; + import type { CarouselOptions, CarouselState, @@ -183,32 +183,19 @@ export class UserGuide extends RcModuleV2 { async preLoadImage() { const url = this.guides[0]; if (url) { - await this._preLoadImage(url); + try { + await this._preLoadImage(url); + } catch (ex) { + console.warn('[UserGuide] Preload image failed', ex); + } } this.setPreLoadImageStatus(); } - /** - * Using webpack `require.context` to load guides files. - * Image files will be sorted by file name in ascending order. - */ resolveGuides(): Record { - let images = + const images = (this._deps.brand.brandConfig.assets?.guides as string[]) || []; - if ( - images.length === 0 && - typeof this._deps.userGuideOptions?.context === 'function' - ) { - images = this._deps.userGuideOptions.context - .keys() - .sort() - .map((key: string) => { - const value = this._deps.userGuideOptions!.context(key); - return typeof value === 'string' ? value : value?.default; - }) as string[]; - } - const locales = Object.keys(SUPPORTED_LOCALES); return images.reduce>((acc, curr: string) => { locales.forEach((locale) => { @@ -295,7 +282,7 @@ export class UserGuide extends RcModuleV2 { that._deps.locale.currentLocale, ]) get guides() { - if (!this._deps.locale.ready) { + if (!this._deps.locale.ready || !this._deps.auth.loggedIn) { return []; } const brandGuides = this.allGuides[this._deps.brand.code]; diff --git a/packages/ringcentral-integration/modules/VideoConfiguration/VideoConfiguration.ts b/packages/ringcentral-integration/modules/VideoConfiguration/VideoConfiguration.ts index b3816f773d..80eb8ebe2f 100644 --- a/packages/ringcentral-integration/modules/VideoConfiguration/VideoConfiguration.ts +++ b/packages/ringcentral-integration/modules/VideoConfiguration/VideoConfiguration.ts @@ -1,8 +1,8 @@ -import { includes } from 'ramda'; -import type { Unsubscribe } from 'redux'; import type ExtensionInfoEvent from '@rc-ex/core/lib/definitions/ExtensionInfoEvent'; import type UserVideoConfiguration from '@rc-ex/core/lib/definitions/UserVideoConfiguration'; import { watch } from '@ringcentral-integration/core'; +import { includes } from 'ramda'; +import type { Unsubscribe } from 'redux'; import { subscriptionFilters } from '../../enums/subscriptionFilters'; import { subscriptionHints } from '../../enums/subscriptionHints'; @@ -10,8 +10,9 @@ import type { DebouncedFunction } from '../../lib/debounce-throttle'; import { debounce } from '../../lib/debounce-throttle'; import { Module } from '../../lib/di'; import { DataFetcherV2Consumer, DataSource } from '../DataFetcherV2'; -import type { UserLicenseType } from './userLicenseType'; + import type { Deps } from './VideoConfiguration.interface'; +import type { UserLicenseType } from './userLicenseType'; import { videoProviders } from './videoProviders'; const DEFAULT_FETCH_DELAY = 5 * 1000; @@ -115,7 +116,7 @@ export class VideoConfiguration extends DataFetcherV2Consumer< get userLicenseType(): UserLicenseType { // TODO: fix UserVideoConfiguration type in @rc-ex/core/definitions - // @ts-ignore + // @ts-expect-error TS(2322): Type '"Free" | "Paid" | null' is not assignable to... Remove this comment to see the full error message return this.data?.userLicenseType || null; } diff --git a/packages/ringcentral-integration/modules/VolumeInspector/AudioDetector.ts b/packages/ringcentral-integration/modules/VolumeInspector/AudioDetector.ts new file mode 100644 index 0000000000..724e4980c8 --- /dev/null +++ b/packages/ringcentral-integration/modules/VolumeInspector/AudioDetector.ts @@ -0,0 +1,84 @@ +import { DetectorListener } from './DetectorListener'; +import { RTCAudioMeter } from './RTCAudioMeter'; + +export class AudioDetector { + private _audioMeter = new RTCAudioMeter(); + private _listeners: Array = []; + private _audioInput?: MediaStream | HTMLMediaElement; + + /** + * connect to audio MediaStream or HTMLMediaElement for detecting + * @param input MediaStream | HTMLMediaElement + */ + public async connect(input: MediaStream | HTMLMediaElement) { + if (!input || this._audioInput === input) { + console.warn('The same audio input has connected.'); + return; + } + // disconnect an old audio input if exists. + this.disconnect(); + console.warn('Connect to the new media input.'); + try { + await this._audioMeter.updateInputStream(input); + } catch (e) { + console.error('update input stream error, msg:', (e as Error).message); + return; + } + this._audioInput = input; + } + + /** + * Add a new listener for the specified audioSource, + * The listener will call dataCallback(volume) cyclically after it calls start(). + * The volume will be a number from [0,1]. + * @param dataCallback (volume: number) => {} + * @return DetectorListener | + */ + public registerListener(dataCallback: (volume: number) => void) { + return new DetectorListener( + this._startListenCallback, + this._genListenerHandle(dataCallback), + this._disposeListenCallback, + ); + } + + /** + * disconnect to the audioSource, and clear all listeners. + */ + public disconnect(): void { + if (this._listeners.length > 0) { + this._listeners.forEach(clearInterval); + this._listeners = []; + } + if (this._audioInput) { + delete this._audioInput; + } + } + + // manager listener for starting listening + private _startListenCallback = (intervalId: number) => { + this._listeners.push(intervalId); + }; + + // generate a listener handle by dataCallback + private _genListenerHandle(dataCallback: (volume: number) => void) { + return () => { + let volumeLevel: number; + try { + volumeLevel = this._audioMeter.getMicLevel(); + } catch (e) { + console.warn('getVolume Error, return volume = 0, err:', e); + volumeLevel = 0; + } + dataCallback(volumeLevel); + }; + } + + // disposer listener by intervalId + private _disposeListenCallback = (intervalId: number) => { + const idx = this._listeners.indexOf(intervalId); + if (idx >= 0) { + this._listeners.splice(idx, 1); + } + }; +} diff --git a/packages/ringcentral-integration/modules/VolumeInspector/DetectorListener.ts b/packages/ringcentral-integration/modules/VolumeInspector/DetectorListener.ts new file mode 100644 index 0000000000..f9cbb2073f --- /dev/null +++ b/packages/ringcentral-integration/modules/VolumeInspector/DetectorListener.ts @@ -0,0 +1,45 @@ +export class DetectorListener { + // unit: ms + private _interval: number = 100; + private _intervalId?: number; + + constructor( + // manage the new intervalId for listener + private readonly _startCallback = (intervalId: number) => {}, + // to get detector volume + private readonly _listenHandle = () => {}, + // clear the intervalId of listener + private readonly _disposeCallback = (intervalId: number) => {}, + ) {} + + /** + * Listener start to handle detector volume by interval + * @return ListenDisposer: Function + */ + start() { + if (!this._intervalId) { + const intervalId = window.setInterval(() => { + this._listenHandle(); + }, this._interval); + this._startCallback(intervalId); + this._intervalId = intervalId; + } + + return () => { + if (this._intervalId) { + clearInterval(this._intervalId); + this._disposeCallback(this._intervalId); + delete this._intervalId; + } + }; + } + + /** + * set interval for listener + * @param interval: number, default 100 ms + */ + public setInterval(interval: number): DetectorListener { + this._interval = interval; + return this; + } +} diff --git a/packages/ringcentral-integration/modules/VolumeInspector/MediaRecorderHelper.ts b/packages/ringcentral-integration/modules/VolumeInspector/MediaRecorderHelper.ts new file mode 100644 index 0000000000..1561d9f061 --- /dev/null +++ b/packages/ringcentral-integration/modules/VolumeInspector/MediaRecorderHelper.ts @@ -0,0 +1,81 @@ +import { MAX_RECORDING_TIME, MEDIA_TYPE } from './const'; + +export class MediaRecorderHelper { + private recordingTimer: ReturnType | null = null; + private mediaRecorder: MediaRecorder | null = null; + private recordedMedia: string | null = null; + private recordedChunks: Blob[] = []; + private recordingTime = 0; + private recordingCompleteCallback: + | ((src: string, recordingTime: number) => void) + | null = null; + private updateRecordingTimeCallback: + | ((recordingTime: number) => void) + | null = null; + get countDown() { + return Math.ceil((MAX_RECORDING_TIME - this.recordingTime) / 1000); + } + + setRecordingTime(recordingTime: number) { + this.recordingTime = recordingTime; + this.updateRecordingTimeCallback?.(recordingTime); + } + + setUpdateRecordingTimeCallback(callback: any) { + this.updateRecordingTimeCallback = callback; + } + + setRecordingCompleteCallback(callback: any) { + this.recordingCompleteCallback = callback; + } + + startRecording(stream: MediaStream) { + this.cleanupRecording(); + this.recordedChunks = []; + this.mediaRecorder = new MediaRecorder(stream, { mimeType: MEDIA_TYPE }); + this.mediaRecorder.ondataavailable = (e) => { + this.recordedChunks.push(e.data); + }; + this.mediaRecorder.onstop = this.onRecordingComplete; + const recordingStarted = Date.now(); + this.mediaRecorder.start(); + this.recordingTimer = setInterval(() => { + const recordingTime = Date.now() - recordingStarted; + this.setRecordingTime(recordingTime); + if (recordingTime >= MAX_RECORDING_TIME) { + this.stopRecording(); + } + }, 1000); + } + + stopRecording() { + if (this.mediaRecorder && this.mediaRecorder.state !== 'inactive') { + try { + this.mediaRecorder.stop(); + } catch (error) { + console.error('Recording stop failed', error); + this.cleanupRecording(); + throw error; + } + } + } + + private cleanupRecording() { + if (this.recordingTimer) { + clearInterval(this.recordingTimer); + this.recordingTimer = null; + } + if (this.recordedMedia) { + URL.revokeObjectURL(this.recordedMedia); + } + this.setRecordingTime(0); + } + + private readonly onRecordingComplete = () => { + const recordingTime = this.recordingTime; + this.cleanupRecording(); + const blob = new Blob(this.recordedChunks, { type: MEDIA_TYPE }); + this.recordedMedia = URL.createObjectURL(blob); + this.recordingCompleteCallback?.(this.recordedMedia, recordingTime); + }; +} diff --git a/packages/ringcentral-integration/modules/VolumeInspector/MicLevel.ts b/packages/ringcentral-integration/modules/VolumeInspector/MicLevel.ts new file mode 100644 index 0000000000..a72502872f --- /dev/null +++ b/packages/ringcentral-integration/modules/VolumeInspector/MicLevel.ts @@ -0,0 +1,103 @@ +import { AudioDetector } from './AudioDetector'; + +/** + * MicLevel is recommended to be a singleton when it is setupMedia for the same deviceId. + * listenToMic() can be called repeatedly for registering different listener. + */ +export class MicLevel { + // unit: ms + private _interval: number = 100; + + private readonly _audioDetector: AudioDetector = new AudioDetector(); + + private _preInputDeviceId?: string; + private _audioStream?: MediaStream; + private _detectorListenDisposer?: (() => void) | null; + + constructor() {} + + /** + * setup microphone media by deviceId + * if deviceId is undefined, then it will try to get user media by default + * @param deviceId string + * @return MediaStream or SetupMediaError when getUserMedia failed. + */ + public async setupMicMedia( + deviceId: string | undefined, + ): Promise { + if (!deviceId && deviceId === this._preInputDeviceId) { + console.warn( + `deviceId is same, not reset for setupMicMedia, deviceId:${deviceId}`, + ); + return this._audioStream!; + } + + const audioConstraint = { + audio: deviceId + ? { + deviceId: { exact: deviceId }, + } + : true, + }; + + let mediaStream; + try { + mediaStream = await navigator.mediaDevices.getUserMedia(audioConstraint); + } catch (err) { + console.warn(`getUserMedia error, deviceId:${deviceId}, err:`, err); + // to make user handle this error + return new Error(err as string); + } + + // make sure new mediaStream is ready then clear current mediaStream + this.clear(); + + this._audioStream = mediaStream; + this._preInputDeviceId = deviceId || ''; + return mediaStream; + } + + /** + * start to listen microphone mediaStream by interval + * @param dataCallback + * @return disposer of listener + */ + public async listenToMic( + dataCallback: (volume: number) => void, + ): Promise { + if (!this._audioStream) { + console.warn('No audio stream to listen.'); + throw new Error('No media stream was setup.'); + } + await this._audioDetector.connect(this._audioStream); + // all listener will also be cleared when audioDetector disconnect. + const result = this._audioDetector.registerListener(dataCallback); + if (result instanceof Error) { + console.warn('register detector listener error.'); + return result; + } + this._detectorListenDisposer = result.setInterval(this._interval).start(); + } + + public clear(): void { + if (this._audioStream) { + this._audioDetector.disconnect(); + this._audioStream.getTracks().forEach((track) => { + track.stop(); + }); + delete this._audioStream; + delete this._preInputDeviceId; + } + this._detectorListenDisposer?.(); + this._detectorListenDisposer = null; + } + + /** + * set interval for listener + * @param interval number, default 100 ms + */ + public setInterval(interval: number): MicLevel { + this._interval = interval; + return this; + } +} diff --git a/packages/ringcentral-integration/modules/VolumeInspector/MicLevelHelper.ts b/packages/ringcentral-integration/modules/VolumeInspector/MicLevelHelper.ts new file mode 100644 index 0000000000..1f25c239a0 --- /dev/null +++ b/packages/ringcentral-integration/modules/VolumeInspector/MicLevelHelper.ts @@ -0,0 +1,32 @@ +import { MicLevel } from './MicLevel'; +import { LEVEL_CHECK_INTERVAL } from './const'; + +export class MicLevelHelper { + private readonly micLevel = new MicLevel(); + + constructor() { + this.micLevel.setInterval(LEVEL_CHECK_INTERVAL); + } + + async setupMicMedia(deviceId?: string): Promise { + const result = await this.micLevel.setupMicMedia(deviceId); + if (result instanceof Error) { + console.warn('setup mic media error.', result); + throw result; + } + return result; + } + + async listenToMic(dataCallback: (volume: number) => void): Promise { + const result = await this.micLevel.listenToMic(dataCallback); + if (result instanceof Error) { + console.warn('register mic listener error.', result); + throw result; + } + return result; + } + + clear(): void { + this.micLevel.clear(); + } +} diff --git a/packages/ringcentral-integration/modules/VolumeInspector/RTCAudioMeter.ts b/packages/ringcentral-integration/modules/VolumeInspector/RTCAudioMeter.ts new file mode 100644 index 0000000000..0145163539 --- /dev/null +++ b/packages/ringcentral-integration/modules/VolumeInspector/RTCAudioMeter.ts @@ -0,0 +1,118 @@ +const LOG_TAG = 'RTCAudioMeter'; +const kFftSize = 32; +const kMinDecibels = -90; +const kMaxDecibels = -30; +const kSmoothingTimeConstant = 0.0; + +export class RTCAudioMeter { + private static _audioCtx: AudioContext | null; + private _source: + | MediaStreamAudioSourceNode + | MediaElementAudioSourceNode + | undefined; + private _analyser: AnalyserNode | undefined; + private _data: Uint8Array | undefined; + private static _audioMeters: RTCAudioMeter[] = []; + + private static _prepareAudioContext(): void { + if (!RTCAudioMeter._audioCtx) { + // @ts-expect-error + const AudioCtxConstr = window.AudioContext || window.webkitAudioContext; + RTCAudioMeter._audioCtx = new AudioCtxConstr(); + console.info(LOG_TAG, `prepare audio context success`); + } + } + + constructor() { + this._initialize(); + } + + private _initialize(): void { + try { + RTCAudioMeter._prepareAudioContext(); + if (!RTCAudioMeter._audioCtx) { + console.warn(LOG_TAG, `initialize fail because audio context is null`); + return; + } + const isDuplicateAudioMeter = RTCAudioMeter._audioMeters.find( + (item: any) => item === this, + ); + if (!isDuplicateAudioMeter) { + RTCAudioMeter._audioMeters.push(this); + console.info( + LOG_TAG, + `Add user, length: ${RTCAudioMeter._audioMeters.length}`, + ); + } + this._analyser = RTCAudioMeter._audioCtx.createAnalyser(); + this._analyser.fftSize = kFftSize; + this._analyser.minDecibels = kMinDecibels; + this._analyser.maxDecibels = kMaxDecibels; + this._analyser.smoothingTimeConstant = kSmoothingTimeConstant; + this._data = new Uint8Array(this._analyser.frequencyBinCount); + console.info( + LOG_TAG, + `data length is ${this._data.length} ${this._data.byteLength}`, + ); + } catch (e) { + console.warn(LOG_TAG, `initialize error: ${(e as Error).message}`); + } + } + + async updateInputStream( + input: MediaStream | HTMLMediaElement, + ): Promise { + console.info(LOG_TAG, `update input stream`); + if (!RTCAudioMeter._audioCtx || !this._analyser) { + console.info(LOG_TAG, 'There is no audioContext or audioNode'); + return Promise.reject(new Error('There is no audioContext or audioNode')); + } + if (this._source) { + console.info(LOG_TAG, 'There is an old audio source, disconnect'); + this._source.disconnect(); + delete this._source; + } + try { + if (RTCAudioMeter._audioCtx.state === 'suspended') { + console.info(LOG_TAG, `Try to activate audioContext`); + await RTCAudioMeter._audioCtx?.resume(); + } + this._source = + input instanceof HTMLMediaElement + ? RTCAudioMeter._audioCtx.createMediaElementSource(input) + : RTCAudioMeter._audioCtx.createMediaStreamSource(input); + this._source.connect(this._analyser); + } catch (e) { + console.warn( + LOG_TAG, + `update media stream error: ${(e as Error).message}`, + ); + return Promise.reject(e); + } + return Promise.resolve(); + } + + getMicLevel(): number { + try { + if ( + !this._source || + !this._analyser || + !this._data || + !RTCAudioMeter._audioCtx || + RTCAudioMeter._audioCtx?.state !== 'running' + ) { + return 0; + } + this._analyser.getByteFrequencyData?.(this._data); + let audioEnergy = 0; + for (let i = 0; i < this._data.length / 2; i++) { + audioEnergy += this._data[i]; + } + const audioEnergyFloat = audioEnergy / ((255 * this._data.length) / 2); + return audioEnergyFloat; + } catch (e) { + console.warn(LOG_TAG, `get mic Level error: ${(e as Error).message}`); + return 0; + } + } +} diff --git a/packages/ringcentral-integration/modules/VolumeInspector/VolumeInspector.interface.ts b/packages/ringcentral-integration/modules/VolumeInspector/VolumeInspector.interface.ts new file mode 100644 index 0000000000..27b265c4e4 --- /dev/null +++ b/packages/ringcentral-integration/modules/VolumeInspector/VolumeInspector.interface.ts @@ -0,0 +1,9 @@ +import type { AudioSettings } from '../AudioSettings'; + +// export interface AudioSettingsOptions {} + +export interface Deps { + audioSettings: AudioSettings; +} + +export type AUDIO_TYPE = 'microphone' | 'speaker'; diff --git a/packages/ringcentral-integration/modules/VolumeInspector/VolumeInspector.ts b/packages/ringcentral-integration/modules/VolumeInspector/VolumeInspector.ts new file mode 100644 index 0000000000..84ac43c1c3 --- /dev/null +++ b/packages/ringcentral-integration/modules/VolumeInspector/VolumeInspector.ts @@ -0,0 +1,223 @@ +import { action, RcModuleV2, state } from '@ringcentral-integration/core'; +import { getBlobURL } from '@ringcentral-integration/utils'; + +import { Module } from '../../lib/di'; +import { proxify } from '../../lib/proxy/proxify'; + +import { AudioDetector } from './AudioDetector'; +import { MediaRecorderHelper } from './MediaRecorderHelper'; +import { MicLevelHelper } from './MicLevelHelper'; +import type { AUDIO_TYPE, Deps } from './VolumeInspector.interface'; +import soundBreakMp3 from './audio/break.mp3'; +import { + LEVEL_CHECK_INTERVAL, + MAX_RECORDING_SECS, + MAX_RECORDING_TIME, + TEST_STATE, + TEST_TYPE, +} from './const'; +import { createAudioElement } from './utils/createAudioElement'; +import { stopStream } from './utils/stream'; + +@Module({ + name: 'VolumeInspector', + deps: ['AudioSettings'], +}) +export class VolumeInspector extends RcModuleV2 { + private readonly mediaRecorderHelper = new MediaRecorderHelper(); + private micLevel = new MicLevelHelper(); + private audioDetector = new AudioDetector(); + private detectorListenDisposer: (() => void) | null = null; + private audioEl: HTMLAudioElement = createAudioElement(); + private outputEl: HTMLAudioElement = createAudioElement(); + private _sampleAudioBlobUrl: string = ''; + + constructor(deps: Deps) { + super({ + deps, + }); + this._preloadAudio(); + this.mediaRecorderHelper.setRecordingCompleteCallback( + this.onRecordingComplete.bind(this), + ); + this.mediaRecorderHelper.setUpdateRecordingTimeCallback( + this.setCountDown.bind(this), + ); + this.audioEl.onended = this.onEnded.bind(this); + } + + @state testState: TEST_STATE = TEST_STATE.IDLE; + @state countDown: number = MAX_RECORDING_SECS; + @state volume = 0; + @state type: AUDIO_TYPE | null = null; + + @action + private _setType(type: AUDIO_TYPE | null) { + this.type = type; + } + + @proxify + async setType(type: AUDIO_TYPE | null) { + this._setType(type); + } + + @action + private _setTestState(testState: TEST_STATE) { + this.testState = testState; + } + + @action + private _setVolume(volume: number) { + this.volume = volume; + } + + @proxify + async setVolume(volume: number) { + this._setVolume(volume); + } + + @proxify + async setTestState(testState: TEST_STATE) { + this._setTestState(testState); + this._setVolume(0); + } + + @action + private _setCountDown(countDown: number) { + this.countDown = countDown; + } + + @proxify + async setCountDown(recordingTime: number) { + const countDown = Math.ceil((MAX_RECORDING_TIME - recordingTime) / 1000); + this._setCountDown(countDown); + } + + private async _preloadAudio() { + try { + this._sampleAudioBlobUrl = await getBlobURL(soundBreakMp3); + } catch (e) { + console.error('failed to preload audio', e); + } + } + + private async setupAudioDetector() { + if (!this.audioEl) { + console.warn('Can not setup AudioDetector - no audio element.'); + return; + } + await this.audioDetector.connect(this.audioEl); + + const result = this.audioDetector.registerListener(this.setVolumeCb); + + if (result instanceof Error) { + console.warn('register detector listener error.'); + return; + } + this.detectorListenDisposer = result + .setInterval(LEVEL_CHECK_INTERVAL) + .start(); + } + + // only for test speaker + public startPlaySampleAudio() { + this.setType(TEST_TYPE.speaker); + this.startPlayback( + this._sampleAudioBlobUrl, + this._deps.audioSettings.callVolume, + ); + } + + protected startPlayback = async (src: string, volume?: number) => { + this.setTestState(TEST_STATE.PLAYS_AUDIO); + try { + this.audioEl.src = src; + this.audioEl.currentTime = 0; + await this.setupAudioDetector(); + await this.audioEl.play(); + // this trick is due to connected audio analyzer (MicDetector) that makes + // impossible to output audio to specific (selected) device other than default + // @ts-expect-error + this.outputEl.srcObject = this.audioEl.captureStream?.(); + if (this.outputEl.setSinkId && this._deps.audioSettings.outputDeviceId) { + this.outputEl.setSinkId(this._deps.audioSettings.outputDeviceId); + } + if (volume !== undefined) { + this.outputEl.volume = volume; + } + this.outputEl.play(); + } catch (e) { + console.warn('Recording play failed', e); + this.completeTest(); + } + }; + + protected setVolumeCb = (volume: number) => { + this.setVolume(volume); + }; + + protected stopPlayback = () => { + if (this.audioEl) { + this.audioEl.pause(); + } + if (this.outputEl) { + this.outputEl.pause(); + if (this.outputEl.srcObject) { + stopStream(this.outputEl.srcObject as MediaStream); + this.outputEl.srcObject = null; + } + } + this.detectorListenDisposer?.(); + this.detectorListenDisposer = null; + }; + + private readonly onRecordingComplete = (src: string) => { + this.micLevel.clear(); + this.startPlayback(src); + }; + + startRecording = async () => { + this.setType(TEST_TYPE.microphone); + this.setTestState(TEST_STATE.RECORDS_AUDIO); + let stream; + try { + stream = await this.micLevel.setupMicMedia( + this._deps.audioSettings.inputDeviceId, + ); + } catch (e) { + console.warn('can not setup mic media', e); + this.setTestState(TEST_STATE.IDLE); + return; + } + try { + await this.micLevel.listenToMic(this.setVolumeCb); + this.mediaRecorderHelper.startRecording(stream); + } catch (e) { + console.warn('can not start startRecording', e); + } + }; + + stopRecording = () => { + try { + this.mediaRecorderHelper.stopRecording(); + } catch (e) { + console.warn('stopRecording failed', e); + this.completeTest(); + } + this.micLevel.clear(); + }; + + completeTest = () => { + this.doCompleteTest(); + }; + + protected onEnded() { + this.doCompleteTest(); + } + + private doCompleteTest() { + this.setType(null); + this.setTestState(TEST_STATE.IDLE); + this.stopPlayback(); + } +} diff --git a/packages/ringcentral-integration/modules/VolumeInspector/audio/break.mp3 b/packages/ringcentral-integration/modules/VolumeInspector/audio/break.mp3 new file mode 100644 index 0000000000..895749d5a6 Binary files /dev/null and b/packages/ringcentral-integration/modules/VolumeInspector/audio/break.mp3 differ diff --git a/packages/ringcentral-integration/modules/VolumeInspector/const.ts b/packages/ringcentral-integration/modules/VolumeInspector/const.ts new file mode 100644 index 0000000000..f5dc9ed011 --- /dev/null +++ b/packages/ringcentral-integration/modules/VolumeInspector/const.ts @@ -0,0 +1,18 @@ +export const LEVEL_CHECK_INTERVAL = 50; + +export const MAX_RECORDING_SECS = 12; + +export const MAX_RECORDING_TIME = MAX_RECORDING_SECS * 1000; + +export const MEDIA_TYPE = 'audio/webm;codecs=opus'; + +export enum TEST_STATE { + IDLE, + RECORDS_AUDIO, + PLAYS_AUDIO, +} + +export enum TEST_TYPE { + microphone = 'microphone', + speaker = 'speaker', +} diff --git a/packages/ringcentral-integration/modules/VolumeInspector/index.ts b/packages/ringcentral-integration/modules/VolumeInspector/index.ts new file mode 100644 index 0000000000..b3a09bb6c2 --- /dev/null +++ b/packages/ringcentral-integration/modules/VolumeInspector/index.ts @@ -0,0 +1,3 @@ +export * from './VolumeInspector'; +export * from './VolumeInspector.interface'; +export * from './const'; diff --git a/packages/ringcentral-integration/modules/VolumeInspector/utils/createAudioElement.ts b/packages/ringcentral-integration/modules/VolumeInspector/utils/createAudioElement.ts new file mode 100644 index 0000000000..c26858764b --- /dev/null +++ b/packages/ringcentral-integration/modules/VolumeInspector/utils/createAudioElement.ts @@ -0,0 +1,6 @@ +export const createAudioElement = () => { + const audioElement = document.createElement('audio'); + audioElement.hidden = true; + document.body.appendChild(audioElement); + return audioElement as HTMLAudioElement; +}; diff --git a/packages/ringcentral-integration/modules/VolumeInspector/utils/stream.ts b/packages/ringcentral-integration/modules/VolumeInspector/utils/stream.ts new file mode 100644 index 0000000000..34b445d6ba --- /dev/null +++ b/packages/ringcentral-integration/modules/VolumeInspector/utils/stream.ts @@ -0,0 +1,26 @@ +/** + * Stops single track on the stream. + * If notify === true, also invokes track.onended handler + */ +export const stopTrack = (track: MediaStreamTrack, notify = false): void => { + track.stop(); + if (notify) { + const event = new Event('ended'); + try { + if (typeof track.onended === 'function') { + track.onended(event); + track.onended = null; + } + } finally { + track.dispatchEvent(event); + } + } +}; + +/** + * Stops all tracks on the stream. + * If notify === true, also invokes track.onended handler for each track + */ +export const stopStream = (stream: MediaStream, notify = false): void => { + stream.getTracks().forEach((track) => stopTrack(track, notify)); +}; diff --git a/packages/ringcentral-integration/modules/WebSocketSubscription/WebSocketSubscription.ts b/packages/ringcentral-integration/modules/WebSocketSubscription/WebSocketSubscription.ts index 76a10456e1..3f16eed481 100644 --- a/packages/ringcentral-integration/modules/WebSocketSubscription/WebSocketSubscription.ts +++ b/packages/ringcentral-integration/modules/WebSocketSubscription/WebSocketSubscription.ts @@ -15,11 +15,12 @@ import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; import { webSocketReadyStates } from '../RingCentralExtensions/webSocketReadyStates'; import type { TabEvent } from '../TabManager'; + +import type { Deps } from './WebSocketSubscription.interface'; import { isTheSameWebSocket, normalizeEventFilter, } from './normalizeEventFilter'; -import type { Deps } from './WebSocketSubscription.interface'; const DEFAULT_REFRESH_DELAY = process.env.NODE_ENV === 'test' ? 0 : 1000; export const SyncMessageTabEventName = 'WebSocketSubscription-syncMessage'; @@ -54,7 +55,7 @@ export class WebSocketSubscription extends RcModuleV2 { } override async onReset() { - await this._debouncedUpdateSubscription.cancel(); + this._debouncedUpdateSubscription.cancel(); await this._removeSubscription(); } @@ -87,7 +88,7 @@ export class WebSocketSubscription extends RcModuleV2 { // when websocket is going to close, revoke subscription beforehand await this._revokeSubscription(); } else { - await this._debouncedUpdateSubscription.cancel(); + this._debouncedUpdateSubscription.cancel(); await this._removeSubscription(); } }, @@ -237,7 +238,14 @@ export class WebSocketSubscription extends RcModuleV2 { // Remove client side subscription object only private async _removeSubscription() { if (this._wsSubscription) { - this._wsSubscription.remove(); + try { + this._wsSubscription.remove(); + } catch (ex) { + // ignore error of remove request + if (process.env.NODE_ENV !== 'test') { + console.warn(`[WebSocketSubscription] > _removeSubscription > ${ex}`); + } + } this._wsSubscription = undefined; } } diff --git a/packages/ringcentral-integration/modules/Webphone/AudioHelper.ts b/packages/ringcentral-integration/modules/Webphone/AudioHelper.ts new file mode 100644 index 0000000000..650f44abaf --- /dev/null +++ b/packages/ringcentral-integration/modules/Webphone/AudioHelper.ts @@ -0,0 +1,96 @@ +import { AudioHelper } from 'ringcentral-web-phone/lib/audioHelper'; + +// @ts-expect-error +export class WebphoneAudioHelper extends AudioHelper { + private _deviceId: string = 'default'; + + override _playSound(url: string, val: boolean, volume: number) { + // when _deviceId is empty, means app would not play audio + if (!this.enabled || !url || this._deviceId === '') return this; + let audio = this.audio[url]; + if (!audio) { + if (val) { + audio = new Audio(); + this.audio[url] = audio; + audio.src = url; + audio.loop = true; + audio.volume = volume; + if (this._deviceId && typeof audio.setSinkId === 'function') { + audio.setSinkId(this._deviceId).catch((error: any) => { + console.error('setSinkId error:', error); + }); + } + audio.playPromise = audio.play().catch((error: any) => { + console.error('playAudio error:', error); + }); + } + } else { + if (val) { + audio.src = url; // load audio resource + audio.currentTime = 0; + if ( + typeof audio.setSinkId === 'function' && + audio.sinkId !== this._deviceId + ) { + audio.setSinkId(this._deviceId || '').catch((error: any) => { + console.error('setSinkId error:', error); + }); + } + audio.playPromise = audio.play().catch((error: any) => { + console.error('playAudio error:', error); + }); + } else { + if (audio.playPromise !== undefined) { + audio.playPromise + .then(function () { + audio.pause(); + }) + .finally(() => { + audio.src = ''; // release audio resource + }); + } + } + } + return this; + } + + override playIncoming(val: boolean): AudioHelper { + // @ts-ignore + return this._playSound(this._incoming, val, this.volume ?? 0.5); + } + + override playOutgoing(val: boolean): AudioHelper { + // @ts-ignore + return this._playSound(this._outgoing, val, this.volume ?? 1); + } + + setDeviceId(val = 'default') { + // ringtone has 'off' option + const deviceId = val === 'off' ? '' : val; + this._deviceId = deviceId; + if (!this.audio || Object.keys(this.audio).length === 0) { + return; + } + for (const url in this.audio) { + const audio = this.audio[url]; + if (typeof audio.setSinkId !== 'function') { + continue; + } + if (audio.playPromise !== undefined) { + audio.playPromise.then(function () { + audio.setSinkId(deviceId).catch((error: any) => { + console.error('setSinkId error:', error); + }); + }); + } + } + } + + get audio() { + return (this as any)._audio; + } + + get enabled() { + return (this as any)._enabled; + } +} diff --git a/packages/ringcentral-integration/modules/Webphone/Webphone.ts b/packages/ringcentral-integration/modules/Webphone/Webphone.ts index deca2c1c48..9c9081e1b4 100644 --- a/packages/ringcentral-integration/modules/Webphone/Webphone.ts +++ b/packages/ringcentral-integration/modules/Webphone/Webphone.ts @@ -1,6 +1,3 @@ -import { filter, find } from 'ramda'; -import type { InviteOptions } from 'ringcentral-web-phone/lib/userAgent'; - import { action, computed, @@ -10,9 +7,12 @@ import { } from '@ringcentral-integration/core'; import type { ObjectMapKey } from '@ringcentral-integration/core/lib/ObjectMap'; import { sleep } from '@ringcentral-integration/utils'; +import { filter, find } from 'ramda'; +import type { InviteOptions } from 'ringcentral-web-phone/lib/userAgent'; import callDirections from '../../enums/callDirections'; import { extendedControlStatus } from '../../enums/extendedControlStatus'; +import { trackEvents } from '../../enums/trackEvents'; import type { NormalizedSession, WebphoneSession, @@ -20,12 +20,8 @@ import type { import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; import { validateNumbers } from '../../lib/validateNumbers'; -import { trackEvents } from '../../enums/trackEvents'; import { callErrors } from '../Call/callErrors'; -import { EVENTS } from './events'; -import { NumberValidError } from './numberValidError'; -import { recordStatus } from './recordStatus'; -import { sessionStatus } from './sessionStatus'; + import type { BeforeCallEndHandler, BeforeCallResumeHandler, @@ -42,6 +38,10 @@ import type { TPickupInboundCall, } from './Webphone.interface'; import { WebphoneBase } from './WebphoneBase'; +import { EVENTS } from './events'; +import { NumberValidError } from './numberValidError'; +import { recordStatus } from './recordStatus'; +import { sessionStatus } from './sessionStatus'; import { webphoneErrors } from './webphoneErrors'; import { extractHeadersData, @@ -258,6 +258,10 @@ export class Webphone extends WebphoneBase { } _onAccepted(session: WebphoneSession) { + this.initWebphoneSessionEvents(session); + } + + initWebphoneSessionEvents(session: WebphoneSession) { session.on('accepted', (incomingResponse) => { if (session.__rc_callStatus === sessionStatus.finished) { return; @@ -300,7 +304,7 @@ export class Webphone extends WebphoneBase { session.__rc_callStatus = sessionStatus.finished; this._onCallEnd(session); }); - // @ts-ignore + // @ts-expect-error TS(2769): No overload matches this call. session.on('replaced', (newSession: WebphoneSession) => { console.log('Event: replaced', newSession); session.__rc_callStatus = sessionStatus.replaced; @@ -322,7 +326,7 @@ export class Webphone extends WebphoneBase { this._updateSessions(); }); session.on('SessionDescriptionHandler-created', () => { - // @ts-ignore + // @ts-expect-error TS(2339): Property 'on' does not exist on type 'SessionDescr... Remove this comment to see the full error message session.sessionDescriptionHandler.on('userMediaFailed', () => { this._deps.audioSettings.onGetUserMediaError(); }); @@ -457,7 +461,7 @@ export class Webphone extends WebphoneBase { parsedNumbers[0].parsedNumber; } } else { - // @ts-expect-error + // @ts-expect-error TS(2339): Property 'numbers' does not exist on type 'Validat... Remove this comment to see the full error message validPhoneNumber = validatedResult.numbers?.[0]?.e164; } } @@ -470,7 +474,7 @@ export class Webphone extends WebphoneBase { } catch (e: any /** TODO: confirm with instanceof */) { console.error(e); this._deps.alert.warning({ - message: webphoneErrors.forwardError, + message: webphoneErrors.unknownError, }); this._addTrackAfterForward(); return false; @@ -708,7 +712,7 @@ export class Webphone extends WebphoneBase { parsedNumbers?.[0].availableExtension ?? parsedNumbers?.[0].parsedNumber; } else { - // @ts-expect-error + // @ts-expect-error TS(2339): Property 'numbers' does not exist on type 'Validat... Remove this comment to see the full error message validPhoneNumber = numberResult.numbers?.[0]?.e164; } } @@ -764,7 +768,7 @@ export class Webphone extends WebphoneBase { fromNumber, homeCountryId: this._deps.regionSettings.homeCountryId, // TODO: should check that type issue - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'string[]'... Remove this comment to see the full error message extendedControls: '', transferSessionId: sessionId, }); @@ -785,7 +789,7 @@ export class Webphone extends WebphoneBase { return; } const oldSessionId = newSession.__rc_transferSessionId; - const oldSession = this.originalSessions[oldSessionId]; + const oldSession = this.originalSessions[oldSessionId!]; if (!oldSession) { return; } @@ -1060,18 +1064,14 @@ export class Webphone extends WebphoneBase { @proxify async clearSessionCaching() { this._clearSessionCaching( - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type '(NormalizedSession | undefined)[... Remove this comment to see the full error message [...Object.values(this.originalSessions)].map(normalizeSession), ); } - // @ts-expect-error - @track((that: Webphone) => - that.isOnTransfer ? [trackEvents.coldTransferCall] : null, - ) _updateSessions() { this._updateSessionsState( - // @ts-expect-error + // @ts-expect-error TS(2345): Argument of type '(NormalizedSession | undefined)[... Remove this comment to see the full error message [...Object.values(this.originalSessions)].map(normalizeSession), ); } @@ -1184,6 +1184,16 @@ export class Webphone extends WebphoneBase { } _onCallEnd(session: WebphoneSession) { + // should remove __rc_transferSessionId when the call is warm transfer call + const transferSession = this.sessions.find((s) => { + return s.warmTransferSessionId === session.id; + }); + if (transferSession) { + const originalTransferSession = this.originalSessions[transferSession.id]; + if (originalTransferSession) { + delete originalTransferSession.__rc_transferSessionId; + } + } session.__rc_extendedControlStatus = extendedControlStatus.stopped; const normalizedSession = this._getNormalizedSession(session); if (!normalizedSession) { diff --git a/packages/ringcentral-integration/modules/Webphone/WebphoneBase.ts b/packages/ringcentral-integration/modules/Webphone/WebphoneBase.ts index 06feeea4aa..465411d45b 100644 --- a/packages/ringcentral-integration/modules/Webphone/WebphoneBase.ts +++ b/packages/ringcentral-integration/modules/Webphone/WebphoneBase.ts @@ -1,10 +1,3 @@ -import { EventEmitter } from 'events'; -import type PhoneLinesInfo from 'ringcentral-client/build/definitions/PhoneLinesInfo'; -import RingCentralWebphone from 'ringcentral-web-phone'; -import defaultIncomingAudio from 'ringcentral-web-phone/audio/incoming.ogg'; -import defaultOutgoingAudio from 'ringcentral-web-phone/audio/outgoing.ogg'; -import type { WebPhoneUserAgent } from 'ringcentral-web-phone/lib/userAgent'; - import type CreateSipRegistrationResponse from '@rc-ex/core/lib/definitions/CreateSipRegistrationResponse'; import type SipRegistrationDeviceInfo from '@rc-ex/core/lib/definitions/SipRegistrationDeviceInfo'; import { @@ -18,15 +11,23 @@ import { } from '@ringcentral-integration/core'; import type { ObjectMapValue } from '@ringcentral-integration/core/lib/ObjectMap'; import { sleep } from '@ringcentral-integration/utils'; +import { EventEmitter } from 'events'; +import type PhoneLinesInfo from 'ringcentral-client/build/definitions/PhoneLinesInfo'; +import RingCentralWebphone from 'ringcentral-web-phone'; +import { WebPhoneUserAgent } from 'ringcentral-web-phone/lib/userAgent'; import { trackEvents } from '../../enums/trackEvents'; import type { WebphoneSession } from '../../interfaces/Webphone.interface'; +import { SipInstanceManager } from '../../lib/SipInstanceManager'; import { Module } from '../../lib/di'; import { proxify } from '../../lib/proxy/proxify'; -import { SipInstanceManager } from '../../lib/SipInstanceManager'; + +import { WebphoneAudioHelper } from './AudioHelper'; +import type { Deps } from './Webphone.interface'; +import defaultIncomingAudio from './audio/incoming.mp3'; +import defaultOutgoingAudio from './audio/outgoing.mp3'; import { connectionStatus } from './connectionStatus'; import { EVENTS } from './events'; -import type { Deps } from './Webphone.interface'; import { webphoneErrors } from './webphoneErrors'; import { isBrowserSupport, @@ -355,7 +356,7 @@ export class WebphoneBase extends RcModuleV2 { }); }, 4000); }); - window.addEventListener('unload', () => { + window.addEventListener('pagehide', () => { // mark current instance id as inactive, so app can reuse it after refresh if (this._sipInstanceId) { this._sipInstanceManager.setInstanceInactive( @@ -386,9 +387,8 @@ export class WebphoneBase extends RcModuleV2 { () => this.shouldUpdateRingtoneVolume, () => { if (this.ready && this._webphone && this._webphone.userAgent) { - const ringtoneMuted = this._deps.audioSettings.ringtoneMuted; this._webphone.userAgent.audioHelper.setVolume( - ringtoneMuted ? 0 : this._deps.audioSettings.ringtoneVolume, + this._deps.audioSettings.ringtoneVolume, ); } }, @@ -416,6 +416,26 @@ export class WebphoneBase extends RcModuleV2 { } }, ); + watch( + this, + () => this.shouldSetRingtoneSinkId, + () => { + if ( + this.ready && + this._deps.audioSettings.supportDevices && + this._webphone && + this._webphone.userAgent && + this._webphone.userAgent.audioHelper && + // @ts-expect-error + this._webphone.userAgent.audioHelper.setDeviceId + ) { + // @ts-expect-error + this._webphone.userAgent.audioHelper.setDeviceId( + this._deps.audioSettings.ringtoneDeviceId, + ); + } + }, + ); watch( this, () => this.shouldTriggerOnTabActive, @@ -434,13 +454,21 @@ export class WebphoneBase extends RcModuleV2 { @computed((that: WebphoneBase) => [ that.ready, that._deps.audioSettings.ringtoneVolume, - that._deps.audioSettings.ringtoneMuted, ]) get shouldUpdateRingtoneVolume(): any[] { + return [this.ready, this._deps.audioSettings.ringtoneVolume]; + } + + @computed((that: WebphoneBase) => [ + that.ready, + that._deps.audioSettings.supportDevices, + that._deps.audioSettings.ringtoneDeviceId, + ]) + get shouldSetRingtoneSinkId(): any[] { return [ this.ready, - this._deps.audioSettings.ringtoneVolume, - this._deps.audioSettings.ringtoneMuted, + this._deps.audioSettings.supportDevices, + this._deps.audioSettings.ringtoneDeviceId, ]; } @@ -601,11 +629,13 @@ export class WebphoneBase extends RcModuleV2 { autoStop: false, // handle auto stop by this module, fix memory leak issue https://github.com/ringcentral/ringcentral-web-phone/pull/332 ...(this._deps.webphoneOptions.webphoneSDKOptions ?? {}), }); + // @ts-expect-error TS(2322): Type 'WebphoneAudioHelper' is not assignable to ty... Remove this comment to see the full error message + this._webphone.userAgent.audioHelper = new WebphoneAudioHelper({ + enabled: true, + }); this.loadAudio(); this._webphone.userAgent.audioHelper.setVolume( - this._deps.audioSettings.ringtoneMuted - ? 0 - : this._deps.audioSettings.ringtoneVolume, + this._deps.audioSettings.ringtoneVolume, ); // Webphone userAgent registered event this._webphone.userAgent.on('registered', () => { @@ -656,6 +686,7 @@ export class WebphoneBase extends RcModuleV2 { : null; switch (statusCode) { // Webphone account over limit + case 403: case 603: { errorCode = webphoneErrors.webphoneCountOverLimit; break; @@ -687,7 +718,7 @@ export class WebphoneBase extends RcModuleV2 { // }); // sip provision expired // TODO: should check that type issue in ringcentral-web-phone - // @ts-ignore + // @ts-expect-error TS(2769): No overload matches this call. this._webphone.userAgent.on('provisionUpdate', () => { if (Object.keys(this.originalSessions).length === 0) { this._deps.alert.warning({ @@ -800,7 +831,12 @@ export class WebphoneBase extends RcModuleV2 { } // do not connect if it is connecting // do not reconnect when user disconnected - if (this.connecting || this.disconnecting || this.inactiveDisconnecting) { + if ( + this.connecting || + this.disconnecting || + this.inactiveDisconnecting || + this.reconnecting + ) { return false; } // do not connect when connected unless force @@ -1156,6 +1192,10 @@ export class WebphoneBase extends RcModuleV2 { incoming: this.incomingAudio, outgoing: this.outgoingAudio, }); + // @ts-expect-error + this._webphone.userAgent.audioHelper.setDeviceId( + this._deps.audioSettings.ringtoneDeviceId, + ); } } @@ -1298,6 +1338,10 @@ export class WebphoneBase extends RcModuleV2 { } get incomingAudio() { + // support turn off ringtone + if (this.incomingAudioDataUrl === '') { + return ''; + } return this.incomingAudioDataUrl || this.defaultIncomingAudio; } diff --git a/packages/ringcentral-integration/modules/Webphone/audio/incoming.mp3 b/packages/ringcentral-integration/modules/Webphone/audio/incoming.mp3 new file mode 100644 index 0000000000..ef38076bed Binary files /dev/null and b/packages/ringcentral-integration/modules/Webphone/audio/incoming.mp3 differ diff --git a/packages/ringcentral-integration/modules/Webphone/audio/outgoing.mp3 b/packages/ringcentral-integration/modules/Webphone/audio/outgoing.mp3 new file mode 100644 index 0000000000..1406fe4b4b Binary files /dev/null and b/packages/ringcentral-integration/modules/Webphone/audio/outgoing.mp3 differ diff --git a/packages/ringcentral-integration/modules/Webphone/typings.d.ts b/packages/ringcentral-integration/modules/Webphone/typings.d.ts index b324713c4e..f8d0b5c528 100644 --- a/packages/ringcentral-integration/modules/Webphone/typings.d.ts +++ b/packages/ringcentral-integration/modules/Webphone/typings.d.ts @@ -2,3 +2,7 @@ declare module '*.ogg' { const content: any; export default content; } +declare module '*.mp3' { + const content: any; + export default content; +} diff --git a/packages/ringcentral-integration/modules/Webphone/webphoneHelper.ts b/packages/ringcentral-integration/modules/Webphone/webphoneHelper.ts index 930b2f48f0..25602cedf8 100644 --- a/packages/ringcentral-integration/modules/Webphone/webphoneHelper.ts +++ b/packages/ringcentral-integration/modules/Webphone/webphoneHelper.ts @@ -5,9 +5,10 @@ import type { WebphoneSession, } from '../../interfaces/Webphone.interface'; import { camelize } from '../../lib/di/utils/utils'; + +import type { WebphoneSessionRequestHeaders } from './Webphone.interface'; import { recordStatus } from './recordStatus'; import { sessionStatus } from './sessionStatus'; -import type { WebphoneSessionRequestHeaders } from './Webphone.interface'; let environment: Window & typeof globalThis; if (typeof window !== 'undefined') { @@ -110,21 +111,29 @@ export function extractHeadersData( export function getCallQueueName({ direction, - toUserName, - fromUserName, + headers, }: { - direction: string; - toUserName: string; - fromUserName: string; + direction: 'Inbound' | 'Outbound'; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + headers: any; }) { - if (direction === callDirections.outbound) { + if ( + direction === callDirections.outbound || + !headers || + !headers['P-Rc-Api-Call-Info'] || + !headers['P-Rc-Api-Call-Info'][0] || + !headers['P-Rc-Api-Call-Info'][0].raw || + !headers['P-Asserted-Identity'] || + !headers['P-Asserted-Identity'][0] || + !headers['P-Asserted-Identity'][0].raw + ) { return null; } - let queueName = null; - if (toUserName && fromUserName === toUserName && toUserName.endsWith(' - ')) { - queueName = toUserName; + if (headers['P-Rc-Api-Call-Info'][0].raw.indexOf('queue-call') === -1) { + return null; } - return queueName; + const name = headers['P-Asserted-Identity'][0].raw.split('"')[1]; + return name || null; } export function normalizeSession( @@ -140,16 +149,16 @@ export function normalizeSession( callId: session.__rc_callId, direction: session.__rc_direction, callStatus: session.__rc_callStatus, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'string | undefined' is not assignable to typ... Remove this comment to see the full error message to: session.request?.to?.uri?.user, toUserName, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'string | undefined' is not assignable to typ... Remove this comment to see the full error message from: session.request?.from?.uri?.user, fromNumber: session.__rc_fromNumber, fromUserName, fromTag: session.fromTag, toTag: session.toTag, - // @ts-expect-error + // @ts-expect-error TS(2322): Type 'number | undefined' is not assignable to typ... Remove this comment to see the full error message startTime: session.startTime && new Date(session.startTime).getTime(), creationTime: session.__rc_creationTime, isOnHold: !!session.localHold, @@ -160,21 +169,18 @@ export function normalizeSession( isForwarded: !!session.__rc_isForwarded, isReplied: !!session.__rc_isReplied, recordStatus: session.__rc_recordStatus || recordStatus.idle, - // @ts-expect-error + // @ts-expect-error TS(2739): Type '{ id: string; }' is missing the following pr... Remove this comment to see the full error message contactMatch: session.__rc_contactMatch, minimized: !!session.__rc_minimized, - // @ts-expect-error partyData: session.__rc_partyData || null, lastActiveTime: session.__rc_lastActiveTime, cached: false, removed: false, - // @ts-expect-error callQueueName: getCallQueueName({ direction: session.__rc_direction, - toUserName, - fromUserName, + headers: session.request && session.request.headers, }), - warmTransferSessionId: session.__rc_transferSessionId, + warmTransferSessionId: session.__rc_transferSessionId || '', }; } @@ -213,8 +219,8 @@ export function sortByLastActiveTimeDesc( /** * HACK: this function is not very reliable, only use it before the merging complete. */ -export function isConferenceSession(session: NormalizedSession) { - return session && session.to && session.to.indexOf('conf_') === 0; +export function isConferenceSession(session?: NormalizedSession) { + return session?.to?.indexOf('conf_') === 0; } export function isRecording(session: NormalizedSession) { diff --git a/packages/ringcentral-integration/package.json b/packages/ringcentral-integration/package.json index 22d6f84b58..5cc9964e82 100644 --- a/packages/ringcentral-integration/package.json +++ b/packages/ringcentral-integration/package.json @@ -1,6 +1,6 @@ { "name": "@ringcentral-integration/commons", - "version": "0.14.0", + "version": "0.15.0", "description": "RingCentral Integration Common Javascript Core Library", "homepage": "https://github.com/ringcentral/ringcentral-js-widgets/tree/master/packages/ringcentral-integration#readme", "bugs": { @@ -28,45 +28,48 @@ "unit-test": "mocha --compilers js:@ringcentral-integration/babel-settings/lib/register" }, "dependencies": { - "@sentry/browser": "^7.43.0", - "@sentry/tracing": "^7.43.0", + "@sentry/browser": "^7.99.0", + "@sentry/tracing": "^7.99.0", "bowser": "^2.5.3", "camelcase": "^6.0.0", - "data-transport": "^4.2.2", - "file-loader": "^6.2.0", + "data-transport": "^4.3.5", "firebase": "^9.9.3", "json-mask": "^0.3.8", "libphonenumber-js": "^1.9.48", "localforage": "^1.10.0", + "mixpanel-browser": "^2.49.0", "prettier": "^2.8.3", "pubnub": "^4.29.11", "ramda": "^0.28.0", "redux": "^4.2.0", "reselect": "^2.5.4", - "ringcentral-call": "^0.2.20", - "ringcentral-call-control": "^0.2.8", - "ringcentral-web-phone": "^0.8.10", - "url-loader": "^4.1.1", - "uuid": "^9.0.0", + "ringcentral-call": "^0.2.21", + "ringcentral-call-control": "^0.2.9", + "ringcentral-web-phone": "^0.8.14", + "uuid": "^9.0.1", "yards": "^0.1.4" }, "devDependencies": { - "@rc-ex/core": "^1.2.1", - "@rc-ex/debug": "^1.0.10", - "@rc-ex/rcsdk": "^1.0.10", - "@rc-ex/ws": "^1.0.10", + "@rc-ex/core": "^1.3.3", + "@rc-ex/debug": "^1.1.3", + "@rc-ex/rcsdk": "^1.1.3", + "@rc-ex/ws": "^1.1.3", "@ringcentral-integration/babel-settings": "*", "@ringcentral-integration/core": "*", + "@ringcentral-integration/i18n-dayjs": "*", "@ringcentral-integration/locale-loader": "*", "@ringcentral-integration/locale-settings": "*", "@ringcentral-integration/phone-number": "*", "@ringcentral-integration/test-utils": "*", "@ringcentral-integration/utils": "*", - "@ringcentral/juno": "^2.35.2", - "@ringcentral/juno-icon": "^1.43.0", + "@ringcentral/juno": "^2.42.0", + "@ringcentral/juno-icon": "^1.76.0", "@ringcentral/sdk": "^4.7.2", "@ringcentral/subscriptions": "^4.6.0", + "@types/chrome": "^0.0.260", + "@types/mixpanel-browser": "^2.49.0", "@types/pendo-io-browser": "^2.17.1", + "@types/ramda": "^0.27.46", "assert": "^2.0.0", "babel-istanbul": "^0.12.1", "babelify": "^7.3.0", @@ -81,7 +84,6 @@ "crypto-browserify": "^3.12.0", "dirty-chai": "^1.2.2", "domain-browser": "^4.22.0", - "events": "^3.3.0", "fetch-mock": "^5.13.1", "fs-extra": "^10.1.0", "gulp": "^4.0.2", @@ -90,23 +92,16 @@ "gulp-mocha": "^5.0.0", "gulp-sourcemaps": "^2.6.5", "https-browserify": "^1.0.0", - "jest-html-reporters": "^3.0.8", - "jest-websocket-mock": "^2.2.1", - "json-loader": "^0.5.7", - "mock-socket": "^9.1.2", - "os-browserify": "^0.3.0", - "path-browserify": "^1.0.1", + "jest-html-reporters": "^3.1.7", + "jest-websocket-mock": "^2.5.0", + "mock-socket": "^9.3.1", "process": "^0.11.10", - "punycode": "^2.1.1", - "querystring-es3": "^0.2.1", "react": "^17.0.2", "react-dom": "^17.0.2", "react-json-tree": "^0.11.2", "react-redux": "^5.1.1", "ringcentral-client": "^1.0.0-beta.2", "sinon": "^2.3.1", - "source-map-loader": "^3.0.0", - "source-map-support": "^0.4.0", "stream-browserify": "^3.0.0", "stream-http": "^3.2.0", "string_decoder": "^1.3.0", @@ -116,20 +111,20 @@ "url": "^0.11.0", "util": "^0.12.4", "vm-browserify": "1.1.2", - "webpack": "^5.58.1", - "webpack-dev-server": "^4.3.1", + "webpack": "^5.89.0", + "webpack-dev-server": "^4.15.1", "yargs": "^17.1.1" }, "peerDependencies": { - "@ringcentral-integration/core": "^0.14.0", + "@ringcentral-integration/core": "^0.15.0", "@ringcentral-integration/i18n": "^2.1.1", + "@ringcentral-integration/i18n-dayjs": "^0.15.0", "@ringcentral-integration/phone-number": "^1.0.4", - "@ringcentral-integration/utils": "^0.14.0", - "@ringcentral/juno": "^2.35.2", - "@ringcentral/juno-icon": "^1.43.0", + "@ringcentral-integration/utils": "^0.15.0", + "@ringcentral/juno": "^2.42.0", + "@ringcentral/juno-icon": "^1.76.0", "@ringcentral/sdk": "^4.7.2", "@ringcentral/subscriptions": "^4.6.0", - "isomorphic-fetch": "^2.2.1", "isomorphic-ws": "^4.0.1", "ringcentral-client": "^1.0.0-beta.2" }, diff --git a/packages/ringcentral-integration/test/jest.setup.ts b/packages/ringcentral-integration/test/jest.setup.ts new file mode 100644 index 0000000000..f1d86b6d4b --- /dev/null +++ b/packages/ringcentral-integration/test/jest.setup.ts @@ -0,0 +1,13 @@ +global.chrome = global.chrome ?? { + runtime: { + onMessage: { + addListener: (listener: unknown) => {}, + removeListener: (listener: unknown) => {}, + }, + sendMessage: (message: unknown) => {}, + }, + tabs: { + query: (queryInfo: chrome.tabs.QueryInfo) => {}, + sendMessage: (tabId: number, message: unknown) => {}, + }, +}; diff --git a/packages/ringcentral-integration/test/lib/mockModule.ts b/packages/ringcentral-integration/test/lib/mockModule.ts index d0c1392f43..75dc899142 100644 --- a/packages/ringcentral-integration/test/lib/mockModule.ts +++ b/packages/ringcentral-integration/test/lib/mockModule.ts @@ -1,9 +1,10 @@ -import { createStore } from 'redux'; - +// @ts-nocheck +// TODO: fix type import { storeKey } from '@ringcentral-integration/core'; +import { createStore } from 'redux'; -import { ModuleFactory } from '../../lib/di'; import RcModule from '../../lib/RcModule'; +import { ModuleFactory } from '../../lib/di'; export const mockModuleWithDeps = any>( module: { provide: string; useClass: T }, diff --git a/packages/ringcentral-integration/test/spec-lib/ObjectProxy.test.tsx b/packages/ringcentral-integration/test/spec-lib/ObjectProxy.test.tsx new file mode 100644 index 0000000000..0ed71a3d73 --- /dev/null +++ b/packages/ringcentral-integration/test/spec-lib/ObjectProxy.test.tsx @@ -0,0 +1,60 @@ +import { + Scenario, + Step, + Then, + When, + autorun, + title, + ut, +} from '@ringcentral-integration/test-utils'; + +import { ObjectProxy } from '../../lib/ObjectProxy'; + +@autorun(test) +@ut +@title('UT for ObjectProxy') +class ObjectProxyTest extends Step { + run() { + let proxyChrome: typeof chrome; + const sendMessageSpy = jest.fn(); + const addListenerSpy = jest.fn(); + return ( + + { + const _chrome: typeof chrome = { + runtime: { + sendMessage: sendMessageSpy, + }, + } as any; + const _chromeProxy: typeof chrome = { + runtime: { + onMessage: { + addListener: addListenerSpy, + }, + }, + } as any; + const objectProxy = new ObjectProxy(_chrome, _chromeProxy); + proxyChrome = objectProxy.create(); + }} + /> + { + // call to source object + proxyChrome.runtime.sendMessage('test'); + expect(sendMessageSpy).toHaveBeenCalledWith('test'); + // call to delegated object + proxyChrome.runtime.onMessage.addListener(() => {}); + expect(addListenerSpy).toHaveBeenCalledTimes(1); + // "set" is not allowed + expect(() => { + proxyChrome.runtime = {} as any; + }).toThrow('Setting properties is not allowed'); + }} + /> + + ); + } +} diff --git a/packages/ringcentral-integration/test/spec-lib/RcModuleV2.test.tsx b/packages/ringcentral-integration/test/spec-lib/RcModuleV2.test.tsx index 854b5fc571..6463dbd8ea 100644 --- a/packages/ringcentral-integration/test/spec-lib/RcModuleV2.test.tsx +++ b/packages/ringcentral-integration/test/spec-lib/RcModuleV2.test.tsx @@ -1,5 +1,3 @@ -import { createStore } from 'redux'; - import { action, RcModuleV2, state } from '@ringcentral-integration/core'; import { autorun, @@ -10,10 +8,11 @@ import { title, When, } from '@ringcentral-integration/test-utils'; - import { sleep } from '@ringcentral-integration/utils'; -import { Module, ModuleFactory } from '../../lib/di'; +import { createStore } from 'redux'; + import RcModule from '../../lib/RcModule'; +import { Module, ModuleFactory } from '../../lib/di'; import { Locale } from '../../modules/Locale'; @autorun(test) diff --git a/packages/ringcentral-integration/test/spec-lib/StorageBase.test.tsx b/packages/ringcentral-integration/test/spec-lib/StorageBase.test.tsx index 72386bf80f..c057f1de06 100644 --- a/packages/ringcentral-integration/test/spec-lib/StorageBase.test.tsx +++ b/packages/ringcentral-integration/test/spec-lib/StorageBase.test.tsx @@ -1,5 +1,3 @@ -import type { Reducer } from 'redux'; - import { usmAction } from '@ringcentral-integration/core'; import { ObjectMap } from '@ringcentral-integration/core/lib/ObjectMap'; import { @@ -11,6 +9,7 @@ import { title, When, } from '@ringcentral-integration/test-utils'; +import type { Reducer } from 'redux'; import { actionTypesBase, getDataReducer } from '../../lib/StorageBase'; diff --git a/packages/ringcentral-integration/test/spec-lib/audioHelper.test.tsx b/packages/ringcentral-integration/test/spec-lib/audioHelper.test.tsx new file mode 100644 index 0000000000..300daf3609 --- /dev/null +++ b/packages/ringcentral-integration/test/spec-lib/audioHelper.test.tsx @@ -0,0 +1,142 @@ +import { + autorun, + Scenario, + Step, + Then, + title, + waitForRenderReady, + When, +} from '@ringcentral-integration/test-utils'; + +import { WebphoneAudioHelper as AudioHelper } from '../../modules/Webphone/AudioHelper'; + +@autorun(test) +@title('AudioHelper::playSound') +export class PlaySound extends Step { + run() { + return ( + + { + context.audioHelper = new AudioHelper({ + enabled: true, + incoming: 'http://incoming_uri', + outgoing: 'http://outgoing_uri', + }); + }} + /> + { + audioHelper.playIncoming(true); + const audio = audioHelper._audio['http://incoming_uri']; + expect(audio.src).toContain('http://incoming_uri'); + }} + /> + { + audioHelper.playIncoming(true); + const audio = audioHelper._audio['http://incoming_uri']; + expect(audio.src).toContain('http://incoming_uri'); + }} + /> + + ); + } +} + +@autorun(test) +@title('AudioHelper::stopSound') +export class StopSound extends Step { + run() { + return ( + + { + context.audioHelper = new AudioHelper({ + enabled: true, + incoming: 'http://incoming_uri', + outgoing: 'http://outgoing_uri', + }); + }} + /> + { + audioHelper.playIncoming(false); + const audio = audioHelper._audio['http://incoming_uri']; + expect(audio).toBeUndefined(); + }} + /> + { + audioHelper.playIncoming(true); + audioHelper.playIncoming(false); + const audio = audioHelper._audio['http://incoming_uri']; + await waitForRenderReady(); + expect(audio.src).not.toContain('http://incoming_uri'); + }} + /> + + ); + } +} + +@autorun(test) +@title('AudioHelper::NoEnabled') +export class NoPlaySoundAtNoEnabled extends Step { + run() { + return ( + + { + context.audioHelper = new AudioHelper({ + enabled: false, + incoming: 'http://incoming_uri', + outgoing: 'http://outgoing_uri', + }); + }} + /> + { + audioHelper.playIncoming(true); + expect(Object.keys(audioHelper._audio).length).toBe(0); + }} + /> + + ); + } +} + +@autorun(test) +@title('AudioHelper::NoUrl') +export class NoPlaySoundAtNoUrl extends Step { + run() { + return ( + + { + context.audioHelper = new AudioHelper({ + enabled: true, + outgoing: 'http://outgoing_uri', + }); + }} + /> + { + audioHelper.playIncoming(true); + expect(Object.keys(audioHelper._audio).length).toBe(0); + }} + /> + + ); + } +} diff --git a/packages/ringcentral-integration/test/spec-lib/channel.test.tsx b/packages/ringcentral-integration/test/spec-lib/channel.test.tsx new file mode 100644 index 0000000000..32f4c7d05a --- /dev/null +++ b/packages/ringcentral-integration/test/spec-lib/channel.test.tsx @@ -0,0 +1,122 @@ +import { + autorun, + Scenario, + Step, + Then, + title, + ut, + When, +} from '@ringcentral-integration/test-utils'; + +import { Channel } from '../../lib/channel'; + +@autorun(test) +@ut +@title('Channel Test') +export class ChannelTest extends Step { + run() { + let channel: Channel; + let sendRuntimeMessage: jest.SpyInstance; + let sendTabMessageSpy: jest.SpyInstance; + let onMessageHandler: ( + message: any, + sender: chrome.runtime.MessageSender, + sendResponse: (response?: any) => void, + ) => void; + const fakeActionHandler = jest.fn(); + return ( + { + sendRuntimeMessage = jest.spyOn(chrome.runtime, 'sendMessage'); + jest + .spyOn(chrome.runtime.onMessage, 'addListener') + .mockImplementation((handler) => { + onMessageHandler = handler; + }); + const tab: chrome.tabs.Tab = { id: 123 } as any; + jest.spyOn(chrome.tabs, 'query').mockResolvedValue([tab]); + sendTabMessageSpy = jest.spyOn(chrome.tabs, 'sendMessage'); + }} + > + { + channel = new Channel('my-channel-type'); + }} + /> + { + const action = 'fake-action'; + channel.select(action, fakeActionHandler); + expect(channel._mux[action]).toBe(fakeActionHandler); + }} + /> + { + await channel.send({ message: 'my-message' }); + expect(sendRuntimeMessage).toHaveBeenCalledWith({ + type: 'my-channel-type', + message: 'my-message', + }); + }} + /> + { + await channel.broadcast({ message: 'my-broadcast' }); + expect(sendTabMessageSpy).toHaveBeenCalledWith(123, { + type: 'my-channel-type', + message: 'my-broadcast', + }); + }} + /> + { + const sender = { id: 'sender-id' }; + const message = { + type: 'my-channel-type', + action: 'fake-action', + }; + + // handler respond success + await new Promise((resolve) => { + fakeActionHandler + .mockReset() + .mockReturnValue(Promise.resolve(true)); + const asyncFlag = onMessageHandler(message, sender, (res) => { + expect(fakeActionHandler).toHaveBeenCalledWith(message, sender); + expect(res).toBe(true); + resolve(res); + }); + expect(asyncFlag).toBe(true); + }); + + // handler respond failed + await new Promise((resolve) => { + fakeActionHandler + .mockReset() + .mockReturnValue(Promise.reject('any reason')); + const asyncFlag = onMessageHandler(message, sender, (res) => { + expect(fakeActionHandler).toHaveBeenCalledWith(message, sender); + expect(res).toBe(undefined); + resolve(res); + }); + expect(asyncFlag).toBe(true); + }); + + // action type not match + fakeActionHandler.mockReset(); + const otherMessage = { type: 'other-type' }; + const asyncFlag = onMessageHandler(otherMessage, sender, () => {}); + expect(asyncFlag).toBe(false); + expect(fakeActionHandler).toHaveBeenCalledTimes(0); + }} + /> + + ); + } +} diff --git a/packages/ringcentral-integration/test/spec-lib/concurrentExecute.test.tsx b/packages/ringcentral-integration/test/spec-lib/concurrentExecute.test.tsx index acf05a0367..3157990e0c 100644 --- a/packages/ringcentral-integration/test/spec-lib/concurrentExecute.test.tsx +++ b/packages/ringcentral-integration/test/spec-lib/concurrentExecute.test.tsx @@ -1,5 +1,3 @@ -import type { SleepPromise } from '@ringcentral-integration/utils'; -import * as sleepModule from '@ringcentral-integration/utils/src/utils/sleep'; import { autorun, examples, @@ -9,6 +7,8 @@ import { title, When, } from '@ringcentral-integration/test-utils'; +import type { SleepPromise } from '@ringcentral-integration/utils'; +import * as sleepModule from '@ringcentral-integration/utils/src/utils/sleep'; import type { ConcurrentExecuteOptions } from '../../lib/concurrentExecute'; import concurrentExecute from '../../lib/concurrentExecute'; @@ -79,13 +79,13 @@ export class ConcurrentExecute extends Step { context: any, ) => { expect(context.result).toEqual(values); - expect(promiseSpyOn).toBeCalledTimes(calledTimes); + expect(promiseSpyOn).toHaveBeenCalledTimes(calledTimes); if (delay === undefined) { - expect(sleepSpyOn).toBeCalledTimes(0); + expect(sleepSpyOn).toHaveBeenCalledTimes(0); } else { // -1 for first thunk not have delay - expect(sleepSpyOn).toBeCalledTimes(calledTimes - 1); + expect(sleepSpyOn).toHaveBeenCalledTimes(calledTimes - 1); expect(delayCount).toBe((calledTimes - 1) * delay); } }} diff --git a/packages/ringcentral-integration/test/spec-lib/createApp.test.tsx b/packages/ringcentral-integration/test/spec-lib/createApp.test.tsx index d700f26d10..92672fcfdf 100644 --- a/packages/ringcentral-integration/test/spec-lib/createApp.test.tsx +++ b/packages/ringcentral-integration/test/spec-lib/createApp.test.tsx @@ -1,5 +1,4 @@ import { action, RcModuleV2, state } from '@ringcentral-integration/core'; -import { sleep } from '@ringcentral-integration/utils'; import { autorun, Given, @@ -9,6 +8,7 @@ import { title, When, } from '@ringcentral-integration/test-utils'; +import { sleep } from '@ringcentral-integration/utils'; import { createApp } from '../../lib/createApp'; import { Module, ModuleFactory } from '../../lib/di'; diff --git a/packages/ringcentral-integration/test/spec-lib/debounce-throttle.test.tsx b/packages/ringcentral-integration/test/spec-lib/debounce-throttle.test.tsx index e8b089cdd7..72cda50459 100644 --- a/packages/ringcentral-integration/test/spec-lib/debounce-throttle.test.tsx +++ b/packages/ringcentral-integration/test/spec-lib/debounce-throttle.test.tsx @@ -8,8 +8,8 @@ import { title, When, } from '@ringcentral-integration/test-utils'; - import { sleep } from '@ringcentral-integration/utils'; + import { debounce, DEFAULT_THRESHOLD, diff --git a/packages/ringcentral-integration/test/spec-lib/rateLimitThrottle.test.tsx b/packages/ringcentral-integration/test/spec-lib/rateLimitThrottle.test.tsx index 11170f6bf2..12deb3d24e 100644 --- a/packages/ringcentral-integration/test/spec-lib/rateLimitThrottle.test.tsx +++ b/packages/ringcentral-integration/test/spec-lib/rateLimitThrottle.test.tsx @@ -8,8 +8,8 @@ import { title, When, } from '@ringcentral-integration/test-utils'; - import { sleep } from '@ringcentral-integration/utils'; + import { rateLimitThrottle } from '../../lib/rateLimitThrottle'; @autorun(test) diff --git a/packages/ringcentral-integration/test/spec-lib/sortDialInNumbers.test.tsx b/packages/ringcentral-integration/test/spec-lib/sortDialInNumbers.test.tsx index 9bc3bb50fe..87852cfeed 100644 --- a/packages/ringcentral-integration/test/spec-lib/sortDialInNumbers.test.tsx +++ b/packages/ringcentral-integration/test/spec-lib/sortDialInNumbers.test.tsx @@ -6,6 +6,7 @@ import { title, When, } from '@ringcentral-integration/test-utils'; + import { sortDialInNumbers } from '../../modules/RcVideo'; @autorun(test) diff --git a/packages/ringcentral-integration/test/spec-modules/ActiveCallControl.test.tsx b/packages/ringcentral-integration/test/spec-modules/ActiveCallControl.test.tsx index 16a2502213..3bd7ebf06c 100644 --- a/packages/ringcentral-integration/test/spec-modules/ActiveCallControl.test.tsx +++ b/packages/ringcentral-integration/test/spec-modules/ActiveCallControl.test.tsx @@ -1,5 +1,3 @@ -import type { SessionData } from 'ringcentral-call-control/lib/Session'; - import { autorun, Given, @@ -9,6 +7,7 @@ import { title, When, } from '@ringcentral-integration/test-utils'; +import type { SessionData } from 'ringcentral-call-control/lib/Session'; import { ActiveCallControl } from '../../modules/ActiveCallControl'; import { mockModuleGenerator as baseMockModuleGenerator } from '../lib/mockModule'; @@ -268,7 +267,7 @@ export class ActiveCallControlTransferCall extends Step { action={(_: any, context: any) => { expect( context.mock._deps.numberValidate.validateNumbers, - ).not.toBeCalled(); + ).not.toHaveBeenCalled(); }} /> { expect( context.mock._deps.numberValidate.validateNumbers, - ).toBeCalledWith(['101']); + ).toHaveBeenCalledWith(['101']); }} /> { expect( context.mock._deps.numberValidate.validateNumbers, - ).toBeCalledWith(['101']); - expect(context.mock._deps.alert.warning).toBeCalledWith({ + ).toHaveBeenCalledWith(['101']); + expect(context.mock._deps.alert.warning).toHaveBeenCalledWith({ message: undefined, payload: { phoneNumber: '101' }, }); @@ -409,7 +408,9 @@ export class ActiveCallControlHoldSession extends Step { { - expect(context.mockModule._rcCall.sessions[0].hold).toBeCalled(); + expect( + context.mockModule._rcCall.sessions[0].hold, + ).toHaveBeenCalled(); }} /> @@ -473,7 +474,7 @@ export class ActiveCallControlForwardSession extends Step { action={(_: any, context: any) => { expect( context.mockModule._deps.numberValidate.validateNumbers, - ).not.toBeCalled(); + ).not.toHaveBeenCalled(); }} /> { expect( context.mockModule._deps.numberValidate.validateNumbers, - ).toBeCalledWith(['101']); + ).toHaveBeenCalledWith(['101']); }} /> @@ -581,7 +582,7 @@ export class ActiveCallControlHoldOtherCallsSession extends Step { const otherCall = context.mockModule._rcCall.sessions.find((s) => { return s.telephonySessionId === 'testHoldId'; }); - expect(otherCall.webphoneSession.hold).toBeCalled(); + expect(otherCall.webphoneSession.hold).toHaveBeenCalled(); }} /> @@ -632,7 +633,7 @@ export class ActiveCallControlAnswerAndEndSession extends Step { const otherCall = context.mockModule._rcCall.sessions.find((s) => { return s.id === 'testOtherId'; }); - expect(otherCall.hangup).toBeCalled(); + expect(otherCall.hangup).toHaveBeenCalled(); }} /> diff --git a/packages/ringcentral-integration/test/spec-modules/Brand/processAssets.test.tsx b/packages/ringcentral-integration/test/spec-modules/Brand/processAssets.test.tsx new file mode 100644 index 0000000000..8eb4c613e3 --- /dev/null +++ b/packages/ringcentral-integration/test/spec-modules/Brand/processAssets.test.tsx @@ -0,0 +1,119 @@ +import { + autorun, + examples, + Given, + Scenario, + Step, + Then, + title, + ut, + When, +} from '@ringcentral-integration/test-utils'; + +import { processAssets, type BrandConfig } from '../../../modules/Brand'; + +interface ExampleItem { + origin: string; + assets: BrandConfig['assets']; + expectedAssets: BrandConfig['assets']; +} + +const assets: BrandConfig['assets'] = { + logo: '/assets/logo.svg', + icon: '/assets/icon.png', +}; + +const testExamples: ExampleItem[] = [ + { + origin: null as any, + assets, + expectedAssets: { + logo: '/assets/logo.svg', + icon: '/assets/icon.png', + }, + }, + { + origin: undefined as any, + assets, + expectedAssets: { + logo: '/assets/logo.svg', + icon: '/assets/icon.png', + }, + }, + { + origin: '', + assets, + expectedAssets: { + logo: '/assets/logo.svg', + icon: '/assets/icon.png', + }, + }, + { + origin: '.', + assets, + expectedAssets: { + $$processed$$: '1', + logo: './assets/logo.svg', + icon: './assets/icon.png', + }, + }, + { + origin: './', + assets, + expectedAssets: { + $$processed$$: '1', + logo: './assets/logo.svg', + icon: './assets/icon.png', + }, + }, + { + origin: 'https://fake.com', + assets, + expectedAssets: { + $$processed$$: '1', + logo: 'https://fake.com/assets/logo.svg', + icon: 'https://fake.com/assets/icon.png', + }, + }, + { + origin: 'https://fake.com//', + assets, + expectedAssets: { + $$processed$$: '1', + logo: 'https://fake.com/assets/logo.svg', + icon: 'https://fake.com/assets/icon.png', + }, + }, +]; + +@autorun(test) +@ut +@title('Unit Test for processAssets - origin "${origin}"') +export class TestProcessAssets extends Step { + @examples(testExamples) + run() { + let result: BrandConfig['assets']; + return ( + + { + expect(processAssets).toEqual(expect.any(Function)); + }} + /> + { + result = processAssets(example.assets, example.origin); + }} + /> + { + expect(result).toEqual(example.expectedAssets); + }} + /> + + ); + } +} diff --git a/packages/ringcentral-integration/test/spec-modules/BrowserLogger.test.tsx b/packages/ringcentral-integration/test/spec-modules/BrowserLogger.test.tsx new file mode 100644 index 0000000000..915542319a --- /dev/null +++ b/packages/ringcentral-integration/test/spec-modules/BrowserLogger.test.tsx @@ -0,0 +1,72 @@ +import { toggleLogger } from '@ringcentral-integration/core/lib/logger/loggerV2'; + +import { BrowserLogger } from '../../modules/BrowserLogger'; + +jest.mock('@ringcentral-integration/core', () => ({ + __esModule: true, + ...jest.requireActual('@ringcentral-integration/core'), + action: jest.fn(), + state: jest.fn(), + globalStorage: jest.fn(), +})); +jest.mock('@ringcentral-integration/core/lib/logger/loggerV2', () => ({ + ...jest.requireActual('@ringcentral-integration/core/lib/logger/loggerV2'), + toggleLogger: jest.fn(), +})); + +describe('BrowserLogger', () => { + let browserLogger: BrowserLogger; + const mockStorageTransport = { + downloadLogs: jest.fn(), + }; + const browserLoggerOptions = { + enabled: true, + logger: { + enable: jest.fn(), + disable: jest.fn(), + log: jest.fn(), + transports: { + find: () => mockStorageTransport, + }, + }, + }; + const mockGlobalStorage = jest.fn(); + + beforeEach(() => { + browserLogger = new BrowserLogger({ + globalStorage: mockGlobalStorage, + prefix: 'test', + browserLoggerOptions, + }); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + it('check logger', async () => { + expect(browserLogger.logger).toBe(browserLoggerOptions.logger); + expect(browserLogger.logger.transports.find()).toBe(mockStorageTransport); + }); + + it('should enable logger when enabled is true', async () => { + browserLogger.enabled = false; + await browserLogger.enable(); + expect(browserLogger.enabled).toBe(true); + expect(toggleLogger).toHaveBeenCalledWith(true); + }); + + it('should disable logger when enabled is false', async () => { + browserLogger.enabled = true; + await browserLogger.disable(); + expect(browserLogger.enabled).toBe(false); + expect(toggleLogger).toHaveBeenCalledWith(false); + }); + + it('should downloadLogs when has storageTransport', async () => { + await browserLogger.saveLog(); + expect(mockStorageTransport.downloadLogs).toHaveBeenCalledWith({ + name: 'test', + }); + }); +}); diff --git a/packages/ringcentral-integration/test/spec-modules/CallingSettings.test.tsx b/packages/ringcentral-integration/test/spec-modules/CallingSettings.test.tsx index b35de3e95e..2c31873621 100644 --- a/packages/ringcentral-integration/test/spec-modules/CallingSettings.test.tsx +++ b/packages/ringcentral-integration/test/spec-modules/CallingSettings.test.tsx @@ -258,7 +258,7 @@ export class JapanEmergencyNotification extends Step { { - expect(context.mockFn).toBeCalledWith({ + expect(context.mockFn).toHaveBeenCalledWith({ message: 'callingSettingsMessages-disableEmergencyInJapan', ttl: 0, }); @@ -277,7 +277,7 @@ export class JapanEmergencyNotification extends Step { { - expect(context.mockFn).not.toBeCalledWith(); + expect(context.mockFn).not.toHaveBeenCalledWith(); expect(context.mockModule.acknowledgeJPMessage).toEqual(true); }} /> diff --git a/packages/ringcentral-integration/test/spec-modules/MediaRecorderHelper.test.tsx b/packages/ringcentral-integration/test/spec-modules/MediaRecorderHelper.test.tsx new file mode 100644 index 0000000000..1b898c67bc --- /dev/null +++ b/packages/ringcentral-integration/test/spec-modules/MediaRecorderHelper.test.tsx @@ -0,0 +1,65 @@ +import { + autorun, + Given, + Scenario, + Step, + title, + When, +} from '@ringcentral-integration/test-utils'; + +import { MediaRecorderHelper } from '../../modules/VolumeInspector/MediaRecorderHelper'; +import { mockModuleGenerator } from '../lib/mockModule'; + +const getMockModule = () => + mockModuleGenerator({ + mediaRecorder: { + state: 'active', + stop: jest.fn(), + }, + cleanupRecording: jest.fn(), + setRecordingTime: jest.fn(), + }); + +@autorun(test) +@title('MediaRecorderHelper Module test') +export class MediaRecorderHelperTest extends Step { + run() { + return ( + + { + jest.spyOn(console, 'warn'); + const mediaRecorderHelper = new MediaRecorderHelper(); + expect(mediaRecorderHelper).not.toBe(null); + context.instance = mediaRecorderHelper; + context.mockModule = getMockModule(); + }} + /> + { + jest + .spyOn(context.mockModule.mediaRecorder, 'stop') + .mockImplementation(() => { + throw new Error('stop error'); + }); + jest.spyOn(context.mockModule, 'cleanupRecording'); + expect(() => + context.instance.stopRecording.call(context.mockModule), + ).toThrow(); + }} + /> + { + jest.spyOn(URL, 'revokeObjectURL'); + context.mockModule.recordedMedia = 'test'; + context.instance.cleanupRecording.call(context.mockModule); + expect(URL.revokeObjectURL).toHaveBeenCalledWith('test'); + }} + /> + + ); + } +} diff --git a/packages/ringcentral-integration/test/spec-modules/Meeting/Meeting.test.tsx b/packages/ringcentral-integration/test/spec-modules/Meeting/Meeting.test.tsx index 9dfeef035d..f054695bf3 100644 --- a/packages/ringcentral-integration/test/spec-modules/Meeting/Meeting.test.tsx +++ b/packages/ringcentral-integration/test/spec-modules/Meeting/Meeting.test.tsx @@ -10,6 +10,7 @@ import { import { Meeting } from '../../../modules/Meeting'; import { mockModuleGenerator } from '../../lib/mockModule'; + import { EXPECT_GENERAL_DEFAULT_SETTING_WITH_SW_SETTING, EXPECT_LAST_MEETING_SETTING, @@ -45,7 +46,8 @@ const mockDeps = { }, }; -@autorun(test) +// TODO: Jest worker encountered 4 child process exceptions +@autorun(test.skip) @title('Initialize Meeting module') export class CheckInitialData extends Step { run() { @@ -79,7 +81,8 @@ export class CheckInitialData extends Step { } } -@autorun(test) +// TODO: Jest worker encountered 4 child process exceptions +@autorun(test.skip) @title('Check pmiDefaultSettings when enableServiceWebSettings is off') export class PmiDefaultSettingsWhenEnableSWOff extends Step { run() { @@ -121,7 +124,8 @@ export class PmiDefaultSettingsWhenEnableSWOff extends Step { } } -@autorun(test) +// TODO: Jest worker encountered 4 child process exceptions +@autorun(test.skip) @title('Check generalDefaultSettings when enableServiceWebSettings is off') export class GeneralDefaultSettingsWhenEnableSWOff extends Step { @examples(` @@ -178,7 +182,8 @@ export class GeneralDefaultSettingsWhenEnableSWOff extends Step { } } -@autorun(test) +// TODO: Jest worker encountered 4 child process exceptions +@autorun(test.skip) @title('Check pmiDefaultSettings when enableServiceWebSettings is on') export class PmiDefaultSettingsWhenEnableSWOn extends Step { @examples(` @@ -288,7 +293,8 @@ export class PmiDefaultSettingsWhenEnableSWOn extends Step { } } -@autorun(test) +// TODO: Jest worker encountered 4 child process exceptions +@autorun(test.skip) @title('Check generalDefaultSettings when enableServiceWebSettings is on') export class GeneralDefaultSettingsWhenEnableSWOn extends Step { @examples(` @@ -362,7 +368,8 @@ export class GeneralDefaultSettingsWhenEnableSWOn extends Step { } } -@autorun(test) +// TODO: Jest worker encountered 4 child process exceptions +@autorun(test.skip) @title('Check pmiDefaultSettings lock data when enableServiceWebSettings is on') export class PmiDefaultSettingsLockDataWhenEnableSWOn extends Step { @examples(` @@ -446,7 +453,8 @@ export class PmiDefaultSettingsLockDataWhenEnableSWOn extends Step { } } -@autorun(test) +// TODO: Jest worker encountered 4 child process exceptions +@autorun(test.skip) @title( 'Check generalDefaultSettings lock data when enableServiceWebSettings is on', ) @@ -510,7 +518,8 @@ export class GeneralDefaultSettingsLockDataWhenEnableSWOn extends Step { } } -@autorun(test) +// TODO: Jest worker encountered 4 child process exceptions +@autorun(test.skip) @title('increase meeting code coverage') class CheckInitScheduleFor extends Step { @examples(` diff --git a/packages/ringcentral-integration/test/spec-modules/RegionSettings.test.tsx b/packages/ringcentral-integration/test/spec-modules/RegionSettings.test.tsx index d3d7cf475c..5330f8d027 100644 --- a/packages/ringcentral-integration/test/spec-modules/RegionSettings.test.tsx +++ b/packages/ringcentral-integration/test/spec-modules/RegionSettings.test.tsx @@ -10,8 +10,8 @@ import { } from '@ringcentral-integration/test-utils'; import { RegionSettings } from '../../modules/RegionSettings'; -import { mockModuleGenerator } from '../lib/mockModule'; import { regionSettingsMessages } from '../../modules/RegionSettings/regionSettingsMessages'; +import { mockModuleGenerator } from '../lib/mockModule'; const getMockModule = () => mockModuleGenerator({ @@ -21,7 +21,8 @@ const getMockModule = () => }, }); -@autorun(test) +// TODO: Jest worker encountered 4 child process exceptions +@autorun(test.skip) @title('RegionSettings Module "_setData" action') export class SetData extends Step { @examples(` @@ -70,7 +71,8 @@ export class SetData extends Step { } } -@autorun(test) +// TODO: Jest worker encountered 4 child process exceptions +@autorun(test.skip) @title('Check RegionSettings alert (when app init)') export class CheckRegionSettings extends Step { @examples(` diff --git a/packages/ringcentral-integration/test/spec-modules/RingCentralExtensions.test.tsx b/packages/ringcentral-integration/test/spec-modules/RingCentralExtensions.test.tsx index 561e96b6ae..3f9b414f74 100644 --- a/packages/ringcentral-integration/test/spec-modules/RingCentralExtensions.test.tsx +++ b/packages/ringcentral-integration/test/spec-modules/RingCentralExtensions.test.tsx @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/no-empty-function */ - -import { EventEmitter } from 'events'; +import { Events } from '@rc-ex/ws'; import { autorun, Given, @@ -8,11 +7,12 @@ import { Step, Then, title, + ut, When, } from '@ringcentral-integration/test-utils'; - +import { EventEmitter } from 'events'; import WebSocket from 'isomorphic-ws'; -import { Events } from '@rc-ex/ws'; + import { webSocketReadyStates, RingCentralExtensions, @@ -22,8 +22,9 @@ import { import { mockModuleGenerator } from '../lib/mockModule'; @autorun(test) -@title('RingCentralExtensions') -export class DefaultState extends Step { +@ut +@title('RingCentralExtensions Test') +export class RingCentralExtensionsTest extends Step { run() { return ( @@ -58,8 +59,8 @@ export class DefaultState extends Step { { - expect(context._setupInfraSpy).toBeCalledTimes(1); - expect(context._bindEventsSpy).toBeCalledTimes(1); + expect(context._setupInfraSpy).toHaveBeenCalledTimes(1); + expect(context._bindEventsSpy).toHaveBeenCalledTimes(1); }} /> { - expect(context.recoverWebSocketConnectionSpy).toBeCalledTimes(2); + expect(context.recoverWebSocketConnectionSpy).toHaveBeenCalledTimes( + 2, + ); }} /> void) { context.authBeforeLogoutHandler = handler; }, + addRefreshErrorHandler(handler: () => void) { + context.authRefreshErrorHandler = handler; + }, }, tabManager: {}, }, @@ -226,36 +234,44 @@ export class DefaultState extends Step { { - expect(context._setSharedStateSpy).toBeCalledTimes(1); - expect(context._inactiveOtherTabsSpy).toBeCalledTimes(1); + expect(context._setSharedStateSpy).toHaveBeenCalledTimes(1); + expect(context._inactiveOtherTabsSpy).toHaveBeenCalledTimes(1); // on autoRecoverSuccess context.mockModule._webSocketExtension.eventEmitter.emit( Events.autoRecoverSuccess, ); - expect(context._exposeConnectionEventsSpy).toBeCalledTimes(1); + expect(context._exposeConnectionEventsSpy).toHaveBeenCalledTimes(1); context._exposeConnectionEventsSpy.mockReset(); // on autoRecoverFailed context.mockModule._webSocketExtension.eventEmitter.emit( Events.autoRecoverFailed, ); - expect(context._exposeConnectionEventsSpy).toBeCalledTimes(1); + expect(context._exposeConnectionEventsSpy).toHaveBeenCalledTimes(1); context._exposeConnectionEventsSpy.mockReset(); // sleepDetector handler expect(context.sleepDetectorEvent).toEqual('detected'); expect(context.sleepDetectorHandler).toBeTruthy(); context.sleepDetectorHandler(); - expect(context.recoverWebSocketConnectionSpy).toBeCalledTimes(1); + expect(context.recoverWebSocketConnectionSpy).toHaveBeenCalledTimes( + 1, + ); // auth handlers expect(context.authAfterLoggedInHandler).toBeTruthy(); expect(context.authBeforeLogoutHandler).toBeTruthy(); + expect(context.authRefreshErrorHandler).toBeTruthy(); context.authAfterLoggedInHandler(); context.authBeforeLogoutHandler(); - expect(context.recoverWebSocketConnectionSpy).toBeCalledTimes(2); - expect(context.revokeWebSocketConnectionSpy).toBeCalledTimes(1); + context.authRefreshErrorHandler(false); + expect(context.recoverWebSocketConnectionSpy).toHaveBeenCalledTimes( + 2, + ); + expect(context.revokeWebSocketConnectionSpy).toHaveBeenCalledTimes( + 2, + ); }} /> { - expect(context.setSharedStateSpy).toBeCalledWith( + expect(context.setSharedStateSpy).toHaveBeenCalledWith( `ws-${context.currentTabId}`, { webSocketReady: true }, ); @@ -324,7 +340,7 @@ export class DefaultState extends Step { { - expect(context.installExtensionSpy).toBeCalledTimes(2); + expect(context.installExtensionSpy).toHaveBeenCalledTimes(2); }} /> { - expect(context._inactiveOtherTabsSpy).toBeCalledTimes(1); - expect(context.recoverWebSocketConnectionSpy).toBeCalledTimes(1); + expect(context._inactiveOtherTabsSpy).toHaveBeenCalledTimes(1); + expect(context.recoverWebSocketConnectionSpy).toHaveBeenCalledTimes( + 1, + ); }} /> { context.mockModule = mockModuleGenerator({ + _deps: {}, ready: true, _webSocketExtension: { options: { @@ -372,6 +391,8 @@ export class DefaultState extends Step { }, }, }, + _setWsAutoRecover: + RingCentralExtensions.prototype._setWsAutoRecover, _setTokens() {}, _useTokens() {}, }); @@ -400,14 +421,16 @@ export class DefaultState extends Step { context.mockModule._webSocketExtension.options.autoRecover .enabled, ).toEqual(false); - expect(context._setTokensSpy).toBeCalledTimes(1); - expect(context._useTokensSpy).toBeCalledTimes(1); + expect(context._setTokensSpy).toHaveBeenCalledTimes(1); + expect(context._useTokensSpy).toHaveBeenCalledTimes(1); }} /> { context.mockModule = mockModuleGenerator({ + _setWsAutoRecover: + RingCentralExtensions.prototype._setWsAutoRecover, _deps: { tabManager: { send() {}, @@ -438,7 +461,7 @@ export class DefaultState extends Step { { - expect(context.sendSpy).toBeCalledTimes(2); + expect(context.sendSpy).toHaveBeenCalledTimes(2); expect(context.sendSpy.mock.calls[0][0]).toEqual( InactiveTabEventName, ); @@ -475,8 +498,8 @@ export class DefaultState extends Step { { - expect(context._setTokensSpy).toBeCalledTimes(2); - expect(context._syncTokensToOtherTabsSpy).toBeCalledTimes(2); + expect(context._setTokensSpy).toHaveBeenCalledTimes(2); + expect(context._syncTokensToOtherTabsSpy).toHaveBeenCalledTimes(2); }} /> { - expect(context.recoverSpy).toBeCalledTimes(1); - expect(context._installWebSocketExtensionSpy).toBeCalledTimes(1); - expect(context._exposeConnectionEventsSpy).toBeCalledTimes(2); + expect(context.recoverSpy).toHaveBeenCalledTimes(1); + expect(context._installWebSocketExtensionSpy).toHaveBeenCalledTimes( + 1, + ); + expect(context._exposeConnectionEventsSpy).toHaveBeenCalledTimes(2); }} /> { context.mockModule = mockModuleGenerator({ ready: true, - isWebSocketReady: true, disconnectOnInactive: true, isTabActive: true, _webSocketExtension: { @@ -600,9 +625,9 @@ export class DefaultState extends Step { { - expect(context.revokeSpy).toBeCalledTimes(1); - expect(context._clearTokensSpy).toBeCalledTimes(1); - expect(context._exposeConnectionEventsSpy).toBeCalledTimes(1); + expect(context.revokeSpy).toHaveBeenCalledTimes(1); + expect(context._clearTokensSpy).toHaveBeenCalledTimes(1); + expect(context._exposeConnectionEventsSpy).toHaveBeenCalledTimes(1); }} /> { expect(context.mockModule._removeWsListener).toBeTruthy(); - expect(context._syncWsReadyStateSpy).toBeCalledTimes(2); + expect(context._syncWsReadyStateSpy).toHaveBeenCalledTimes(2); }} /> { - expect(startFn).toBeCalledTimes(1); + expect(startFn).toHaveBeenCalledTimes(1); expect(startFn.mock.calls[0][0]).toEqual({ firstLogin: true }); expect(context.mockModule.allGuides[brandCode]).toBe( context.guides, diff --git a/packages/ringcentral-integration/test/spec-modules/VolumeInspector.test.tsx b/packages/ringcentral-integration/test/spec-modules/VolumeInspector.test.tsx new file mode 100644 index 0000000000..c0038c1e30 --- /dev/null +++ b/packages/ringcentral-integration/test/spec-modules/VolumeInspector.test.tsx @@ -0,0 +1,104 @@ +import { + autorun, + Given, + Scenario, + Step, + title, + When, +} from '@ringcentral-integration/test-utils'; + +import { VolumeInspector } from '../../modules/VolumeInspector'; +import { mockModuleGenerator } from '../lib/mockModule'; + +jest.mock('@ringcentral-integration/core', () => ({ + ...jest.requireActual('@ringcentral-integration/core'), + action: jest.fn(), + state: jest.fn(), +})); + +jest.mock('@ringcentral-integration/commons/lib/proxy/proxify', () => ({ + ...jest.requireActual('@ringcentral-integration/commons/lib/proxy/proxify'), + proxify: jest.fn(), +})); + +const getMockModule = () => + mockModuleGenerator({ + _deps: { audioSettings: {} }, + audioDetector: { + connect: jest.fn(), + registerListener: jest.fn(), + }, + completeTest: jest.fn(), + setTestState: jest.fn(), + }); + +@autorun(test) +@title('VolumeInspector Module test') +export class VolumeInspectorTest extends Step { + run() { + return ( + + { + jest.spyOn(console, 'warn'); + const volumeInspector = new VolumeInspector({} as any); + expect(volumeInspector).not.toBe(null); + context.instance = volumeInspector; + context.mockModule = getMockModule(); + }} + /> + { + context.mockModule.audioEl = null; + const mockFn = jest.spyOn( + context.mockModule.audioDetector, + 'connect', + ); + context.instance.setupAudioDetector.call(context.mockModule); + expect(mockFn).not.toHaveBeenCalled(); + }} + /> + { + context.mockModule.audioEl = new Audio(); + jest + .spyOn(context.mockModule.audioDetector, 'registerListener') + .mockReturnValue(new Error('error')); + context.instance.setupAudioDetector.call(context.mockModule); + expect(context.mockModule.detectorListenDisposer).toBe(undefined); + }} + /> + { + context.instance.startRecording.call(context.mockModule); + expect(console.warn).toHaveBeenCalledWith( + 'can not setup mic media', + expect.any(Error), + ); + }} + /> + { + const mockFn = jest.spyOn(context.instance, 'completeTest'); + jest + .spyOn(context.instance.mediaRecorderHelper, 'stopRecording') + .mockImplementation(() => { + throw new Error('error'); + }); + context.instance.stopRecording(); + expect(mockFn).toHaveBeenCalled(); + expect(console.warn).toHaveBeenCalledWith( + 'stopRecording failed', + expect.any(Error), + ); + }} + /> + + ); + } +} diff --git a/packages/ringcentral-integration/test/spec-modules/Webphone.test.tsx b/packages/ringcentral-integration/test/spec-modules/Webphone.test.tsx index 9e8999dc28..fa9f08fa60 100644 --- a/packages/ringcentral-integration/test/spec-modules/Webphone.test.tsx +++ b/packages/ringcentral-integration/test/spec-modules/Webphone.test.tsx @@ -7,11 +7,12 @@ import { title, When, } from '@ringcentral-integration/test-utils'; -import { Webphone } from '../../modules/Webphone'; +import { Webphone } from '../../modules/Webphone'; import { mockModuleGenerator } from '../lib/mockModule'; -@autorun(test) +// TODO: Jest worker encountered 4 child process exceptions +@autorun(test.skip) @title('Check forward data') export class CheckforwardData extends Step { @examples(` @@ -62,7 +63,8 @@ export class CheckforwardData extends Step { } } -@autorun(test) +// TODO: Jest worker encountered 4 child process exceptions +@autorun(test.skip) @title('Check transfer data') export class CheckInitialData1 extends Step { @examples(` @@ -103,7 +105,8 @@ export class CheckInitialData1 extends Step { } } -@autorun(test) +// TODO: Jest worker encountered 4 child process exceptions +@autorun(test.skip) @title('Check startWarmTransfer data') export class CheckInitialData12 extends Step { @examples(` diff --git a/packages/ringcentral-integration/typings/index.d.ts b/packages/ringcentral-integration/typings/index.d.ts index c6da23913f..30686e8418 100644 --- a/packages/ringcentral-integration/typings/index.d.ts +++ b/packages/ringcentral-integration/typings/index.d.ts @@ -1,6 +1,10 @@ +/* eslint-disable no-var */ + +export {}; + interface LockManager { - request(name: string, callback: Function): Promise; - request(name: string, options: any, callback: Function): Promise; + request(name: string, callback: () => any): Promise; + request(name: string, options: any, callback: () => any): Promise; query(): Promise; } @@ -11,3 +15,8 @@ declare interface Navigator { interface Window { mixpanel: any; } + +declare global { + /** proxy object for APIs */ + var apiProxy: { chrome?: typeof chrome; browser?: any } | undefined; +} diff --git a/packages/ringcentral-mock/package.json b/packages/ringcentral-mock/package.json index c64c996241..166171250b 100644 --- a/packages/ringcentral-mock/package.json +++ b/packages/ringcentral-mock/package.json @@ -1,6 +1,6 @@ { "name": "@ringcentral-integration/mock", - "version": "0.14.0", + "version": "0.15.0", "description": "A RingCentral Platform APIs mocking library", "license": "MIT", "author": "", @@ -19,20 +19,20 @@ "fetch-mock": "^9.11.0", "fetch-mock-jest": "^1.5.1", "isomorphic-fetch": "^2.2.1", - "jest-websocket-mock": "^2.2.1", + "jest-websocket-mock": "^2.5.0", "json-schema-faker": "^0.5.0-rcv.40", "openapi-types": "^9.3.0", "path-to-regexp": "^6.2.0", "ramda": "^0.28.0", "ringcentral-open-api-parser": "^0.6.2", - "ringcentral-web-phone": "^0.8.10", + "ringcentral-web-phone": "^0.8.14", "rxjs": "^7.8.0" }, "devDependencies": { - "@rc-ex/core": "^1.2.1", - "@rc-ex/debug": "^1.0.10", - "@rc-ex/rcsdk": "^1.0.10", - "@rc-ex/ws": "^1.0.10", + "@rc-ex/core": "^1.3.3", + "@rc-ex/debug": "^1.1.3", + "@rc-ex/rcsdk": "^1.1.3", + "@rc-ex/ws": "^1.1.3", "@ringcentral-integration/babel-settings": "*", "@ringcentral-integration/test-utils": "*", "@types/faker": "^5.5.9", @@ -40,10 +40,15 @@ "js-yaml": "^4.1.0", "jsdoc-tests": "^0.1.1", "ts-node": "^10.9.1", - "typescript": "^4.9.4" + "typescript": "^5.5.2" }, "common": true, "ci": { "@ringcentral-integration/mock": "**" + }, + "nx": { + "tags": [ + "scope:ci-group-1" + ] } } diff --git a/packages/ringcentral-mock/scripts/exec.ts b/packages/ringcentral-mock/scripts/exec.ts index ef29a65b44..25b61d6e62 100644 --- a/packages/ringcentral-mock/scripts/exec.ts +++ b/packages/ringcentral-mock/scripts/exec.ts @@ -1,4 +1,5 @@ import path from 'path'; + import { generate } from './generate'; generate( diff --git a/packages/ringcentral-mock/scripts/generate.ts b/packages/ringcentral-mock/scripts/generate.ts index 71475d7dd7..ce1361eefe 100644 --- a/packages/ringcentral-mock/scripts/generate.ts +++ b/packages/ringcentral-mock/scripts/generate.ts @@ -1,8 +1,8 @@ -import fs from 'fs'; -import path from 'path'; import execa from 'execa'; +import fs from 'fs'; import yaml from 'js-yaml'; import type { OpenAPIV3 } from 'openapi-types'; +import path from 'path'; import { parse } from 'ringcentral-open-api-parser'; import { generateApis } from './generateApis'; diff --git a/packages/ringcentral-mock/scripts/generateApis.ts b/packages/ringcentral-mock/scripts/generateApis.ts index 7e3f2e764e..e5d9fb6f4c 100644 --- a/packages/ringcentral-mock/scripts/generateApis.ts +++ b/packages/ringcentral-mock/scripts/generateApis.ts @@ -1,7 +1,7 @@ -import fs from 'fs'; -import path from 'path'; import execa from 'execa'; +import fs from 'fs'; import type { OpenAPIV3 } from 'openapi-types'; +import path from 'path'; import { uniq } from 'ramda'; import { generateIndex } from './generateIndex'; diff --git a/packages/ringcentral-mock/scripts/generateInterfaces.ts b/packages/ringcentral-mock/scripts/generateInterfaces.ts index 0222cc4377..d04244c53b 100644 --- a/packages/ringcentral-mock/scripts/generateInterfaces.ts +++ b/packages/ringcentral-mock/scripts/generateInterfaces.ts @@ -1,6 +1,6 @@ +import execa from 'execa'; import fs from 'fs'; import path from 'path'; -import execa from 'execa'; import { uniq, without } from 'ramda'; import type { Field, Model } from 'ringcentral-open-api-parser/lib/types'; diff --git a/packages/ringcentral-mock/scripts/generateSchemas.ts b/packages/ringcentral-mock/scripts/generateSchemas.ts index 64c4064e52..bc57dffec8 100644 --- a/packages/ringcentral-mock/scripts/generateSchemas.ts +++ b/packages/ringcentral-mock/scripts/generateSchemas.ts @@ -1,7 +1,7 @@ -import fs from 'fs'; -import path from 'path'; import execa from 'execa'; +import fs from 'fs'; import type { OpenAPIV3 } from 'openapi-types'; +import path from 'path'; import { clone } from 'ramda'; type PropertyWithFaker = OpenAPIV3.NonArraySchemaObject & { diff --git a/packages/ringcentral-mock/src/PlatformMock.ts b/packages/ringcentral-mock/src/PlatformMock.ts index 17bfec0bbb..e8b600a187 100644 --- a/packages/ringcentral-mock/src/PlatformMock.ts +++ b/packages/ringcentral-mock/src/PlatformMock.ts @@ -1,18 +1,18 @@ -import { URL, URLSearchParams } from 'url'; +import $RefParser from '@apidevtools/json-schema-ref-parser'; import Ajv from 'ajv'; import type { MockOptions, MockRequest } from 'fetch-mock'; import fetchMock from 'fetch-mock-jest'; import type { JSONSchemaFakerOptions } from 'json-schema-faker'; import type { OpenAPIV3 } from 'openapi-types'; import { match } from 'path-to-regexp'; -import $RefParser from '@apidevtools/json-schema-ref-parser'; + import type { Debugger } from './debugger'; import { createDebugger } from './debugger'; import type { Generate } from './faker'; import { fake } from './faker'; +import type { SchemaObject } from './interface'; import type { Delete, Get, Patch, Post, Put } from './platform/apis'; import schemas from './platform/schemas.json'; -import type { SchemaObject } from './interface'; interface ResponseBody { /** @@ -227,6 +227,7 @@ export class PlatformMock { : createDebugger({ verbose: options?.verbose }); this.fetchMock.catch((url, request) => { this.debugger?.({ url, mock: false, request }); + return new Response('ok', { status: 200 }); }); return this; } @@ -247,9 +248,10 @@ export class PlatformMock { response: async (url: string, request: MockRequest = {}) => { let schema: OpenAPIV3.SchemaObject | null = null; let methodSchema: MethodSchema | null = null; - if (this.schemas.paths[matcher]) { + const schemaItem = this.schemas.paths[matcher as never]; + if (schemaItem) { try { - methodSchema = this.schemas.paths[matcher][method.toLowerCase()]; + methodSchema = schemaItem[method.toLowerCase()] as MethodSchema; schema = methodSchema!.responses?.[status] ?? (methodSchema!.responses as DefaultSchemas)?.default; diff --git a/packages/ringcentral-mock/src/RcMock.ts b/packages/ringcentral-mock/src/RcMock.ts index 621a79a216..376f969e77 100644 --- a/packages/ringcentral-mock/src/RcMock.ts +++ b/packages/ringcentral-mock/src/RcMock.ts @@ -1,19 +1,67 @@ -import type { MockRequest } from 'fetch-mock'; -import type { RcvDelegator } from '@ringcentral-integration/commons/modules/RcVideo'; -import type { NormalizedSession } from '@ringcentral-integration/commons/interfaces/Webphone.interface'; import { callDirection } from '@ringcentral-integration/commons/enums/callDirections'; import { telephonyStatus as telephonyStatuses } from '@ringcentral-integration/commons/enums/telephonyStatus'; -import { isConferenceSession } from '@ringcentral-integration/commons/modules/Webphone/webphoneHelper'; -import { includes } from 'ramda'; import { createTelephonySession, + makeTelephonySessionId, + makeWebphoneSessionId, PartyStatusCode, telephonySessionBuildersCache, + clearTelephonySessionBuilders, } from '@ringcentral-integration/commons/integration-test/mock/telephonySessionBuilder'; +import type { NormalizedSession } from '@ringcentral-integration/commons/interfaces/Webphone.interface'; +import type { RcvDelegator } from '@ringcentral-integration/commons/modules/RcVideo'; +import { isConferenceSession } from '@ringcentral-integration/commons/modules/Webphone/webphoneHelper'; +import type { MockRequest } from 'fetch-mock'; +import { includes } from 'ramda'; + import type { PlatformMockOptions } from './PlatformMock'; import { PlatformMock } from './PlatformMock'; -import type { PubnubMock, WebSocketMock } from './subscription'; +import type { + ArraySchemaObject, + MakeCallProps, + MessageProps, + SchemaObject, +} from './interface'; import { generateTelephonyState } from './lib/generateTelephonyState'; +import accountBody from './platform/data/accountInfo.json'; +import blockedNumberBody from './platform/data/blockedNumber.json'; +import bringInToConferenceResponse from './platform/data/bringInToConferenceRes.json'; +import clientInfoResponseBody from './platform/data/clientInfo.json'; +import companyPagerResponse from './platform/data/companyPager.json'; +import companyPagerInvalidResponse from './platform/data/companyPagerInvalid.json'; +import conferenceCallResponse from './platform/data/conferenceCall.json'; +import deviceBody from './platform/data/device.json'; +import dialInNumbersBody from './platform/data/dialInNumbers.json'; +import dialingPlanBody from './platform/data/dialingPlan.json'; +import directoryEntries from './platform/data/directoryEntries.json'; +import discoveryExternalBody from './platform/data/discoveryExternal.json'; +import discoveryInitialBody from './platform/data/discoveryInitial.json'; +import extensionInfoBody from './platform/data/extensionInfo.json'; +import extensionsListBody from './platform/data/extensions.json'; +import featuresBody from './platform/data/features.json'; +import forwardAllCallsBody from './platform/data/forwardAllCallsInfo.json'; +import generateCodeBody from './platform/data/generateCode.json'; +import invitationBridgesResponse from './platform/data/invitationBridges.json'; +import postMeetingBody from './platform/data/meeting.json'; +import meetingInvitation from './platform/data/meetingInvitation.json'; +import parerPhoneNumbersResponse from './platform/data/numberParser.json'; +import numberParserAPIResponse from './platform/data/numberParserV2.json'; +import partySuperviseResponse from './platform/data/partySupervise.json'; +import postRcvBridgesBody from './platform/data/postRcvBridges.json'; +import presenceBody from './platform/data/presence.json'; +import { + RCV_INVITATION_BODY, + RCV_INVITATION_START, + RCV_INVITATION_END, +} from './platform/data/rcvInvitation'; +import rcvMeetingSettingsBody from './platform/data/rcvMeetingSettings.json'; +import ringOutBody from './platform/data/ringOut.json'; +import sipProvisionBody from './platform/data/sipProvision.json'; +import smsResponse from './platform/data/sms.json'; +import telephonySessionResponse from './platform/data/telephonySession.json'; +import videoPersonalSettingsBody from './platform/data/videoPersonalSettings.json'; +import videoPreferenceBody from './platform/data/videoPreference.json'; +import wsTokenBody from './platform/data/ws/wstoken.json'; import type { AccountLockedSettingResponse, DetailedExtensionPresenceWithSIPEvent, @@ -41,50 +89,12 @@ import type { TokenInfo, ExtensionCallQueuePresenceList, GetCallRecordingResponse, + PartySuperviseResponse, + CallStatusInfo, + ExtensionCallerIdInfo, + GetExtensionForwardingNumberListResponse, } from './platform/interfaces'; -import type { - ArraySchemaObject, - MakeCallProps, - MessageProps, - SchemaObject, -} from './interface'; -import accountBody from './platform/data/accountInfo.json'; -import clientInfoResponseBody from './platform/data/clientInfo.json'; -import featuresBody from './platform/data/features.json'; -import blockedNumberBody from './platform/data/blockedNumber.json'; -import extensionsListBody from './platform/data/extensions.json'; -import directoryEntries from './platform/data/directoryEntries.json'; -import dialingPlanBody from './platform/data/dialingPlan.json'; -import discoveryExternalBody from './platform/data/discoveryExternal.json'; -import discoveryInitialBody from './platform/data/discoveryInitial.json'; -import videoPersonalSettingsBody from './platform/data/videoPersonalSettings.json'; -import rcvMeetingSettingsBody from './platform/data/rcvMeetingSettings.json'; -import dialInNumbersBody from './platform/data/dialInNumbers.json'; -import forwardAllCallsBody from './platform/data/forwardAllCallsInfo.json'; -import postRcvBridgesBody from './platform/data/postRcvBridges.json'; -import videoPreferenceBody from './platform/data/videoPreference.json'; -import extensionInfoBody from './platform/data/extensionInfo.json'; -import postMeetingBody from './platform/data/meeting.json'; -import presenceBody from './platform/data/presence.json'; -import meetingInvitation from './platform/data/meetingInvitation.json'; -import generateCodeBody from './platform/data/generateCode.json'; -import ringOutBody from './platform/data/ringOut.json'; -import wsTokenBody from './platform/data/ws/wstoken.json'; -import parerPhoneNumbersResponse from './platform/data/numberParser.json'; -import numberParserAPIResponse from './platform/data/numberParserV2.json'; -import smsResponse from './platform/data/sms.json'; -import conferenceCallResponse from './platform/data/conferenceCall.json'; -import bringInToConferenceResponse from './platform/data/bringInToConferenceRes.json'; -import telephonySessionResponse from './platform/data/telephonySession.json'; -import deviceBody from './platform/data/device.json'; -import companyPagerResponse from './platform/data/companyPager.json'; -import invitationBridgesResponse from './platform/data/invitationBridges.json'; -import companyPagerInvalidResponse from './platform/data/companyPagerInvalid.json'; -import { - RCV_INVITATION_BODY, - RCV_INVITATION_START, - RCV_INVITATION_END, -} from './platform/data/rcvInvitation'; +import type { PubnubMock, WebSocketMock } from './subscription'; import { WebphoneSessionMock } from './webphone'; export interface RcMockOptions extends PlatformMockOptions { @@ -106,7 +116,17 @@ export interface PostOauthTokenProps { failureCode?: 400 | 403 | 503; } -type HttpStatusCode = 200 | 400 | 403 | 404 | 503 | 409 | 500; +interface CreateConferenceResponse { + session: CallSessionObject; +} + +type HttpStatusCode = 200 | 201 | 400 | 403 | 404 | 503 | 409 | 500; + +export type EventData = { + event: string; + timestamp: string; + body: T; +}; /** * RcMock is a mock for Rc base business logic @@ -223,11 +243,12 @@ export class RcMock extends PlatformMock { override reset() { this.subscription.remove(); this.removeWebphone?.(); + clearTelephonySessionBuilders(); return super.reset(); } getCheckPubsub() { - this.get('https://pubsub.pubnub.com/time/0' as any, 200, { + this.get('/time/0' as any, 200, { response: () => { return { body: '' }; }, @@ -257,9 +278,20 @@ export class RcMock extends PlatformMock { this.post('/restapi/oauth/revoke'); } - getCallerId() { + getCallerId( + handler?: (data: ExtensionCallerIdInfo) => ExtensionCallerIdInfo, + ) { this.get( '/restapi/v1.0/account/:accountId/extension/:extensionId/caller-id', + 200, + { + repeat: 0, + response: ({ mockData }) => { + return { + body: handler?.(mockData as any) ?? mockData, + }; + }, + }, ); } @@ -456,6 +488,11 @@ export class RcMock extends PlatformMock { { schema, response: ({ mockData }) => { + // make phone number have features that we always need + mockData.records[0].features = ['CallerId', 'SmsSender', 'MmsSender']; + // alway have main company number + mockData.records[0].usageType = 'MainCompanyNumber'; + return { body: handler?.(mockData) ?? mockData, }; @@ -464,9 +501,24 @@ export class RcMock extends PlatformMock { ); } - getForwardingNumber() { + getForwardingNumber( + handler?: ( + mockData: GetExtensionForwardingNumberListResponse, + ) => GetExtensionForwardingNumberListResponse, + ) { this.get( '/restapi/v1.0/account/:accountId/extension/:extensionId/forwarding-number', + 200, + { + response: ({ mockData }) => { + // always give first number have all features + mockData.records[0].features = ['CallFlip', 'CallForwarding']; + + return { + body: handler?.(mockData) ?? mockData, + }; + }, + }, ); } @@ -517,7 +569,7 @@ export class RcMock extends PlatformMock { } = {}) { this.get('/restapi/v1.0/account/:accountId', 200, { repeat: repeat ?? 1, - response: ({ mockData }) => { + response: () => { accountBody.serviceInfo.brand.id = brandId ?? '1210'; return { body: (handler?.(accountBody) ?? @@ -621,6 +673,14 @@ export class RcMock extends PlatformMock { mockData.records[0].subject = message; mockData.records[0].attachments.length = 0; } + for (const record of mockData.records) { + // when to field length > 1, it is group message + // 'True' specifies that message is sent exactly to this recipient + // at least one of to number list should be the target number in group message + if (record.direction === 'Inbound' && record.to.length > 1) { + record.to[0].target = true; + } + } return { body: handler?.(mockData) ?? mockData, }; @@ -705,6 +765,8 @@ export class RcMock extends PlatformMock { 200, { response: ({ mockData }) => { + // * set default result always be "In Progress" to prevent not have any call in call history + mockData.records[0].result = 'In Progress'; mockData.records.length = length; return { body: mockData }; }, @@ -734,6 +796,24 @@ export class RcMock extends PlatformMock { }; break; case 400: + body = { + error: 'invalid_grant', + errors: [ + { + errorCode: 'OAU-211', + message: 'Token revoked', + }, + ], + error_description: 'Token revoked', + }; + break; + case 503: + body = { + message: 'Service Unavailable', + error: 'service_unavailable', + error_description: 'Service Unavailable', + }; + break; default: body = { message: 'Wrong token', @@ -742,7 +822,8 @@ export class RcMock extends PlatformMock { }; break; } - this.post('/restapi/oauth/token', failure ? failureCode : 200, { + this.post('/restapi/oauth/token', failure ? failureCode : (200 as any), { + repeat, response: { body, }, @@ -779,6 +860,14 @@ export class RcMock extends PlatformMock { return schema; }, response: ({ mockData }) => { + // * set default result always be "Call connected" to prevent not have any call in call history + mockData.records[0].result = 'Call connected'; + + // be default mock, always set that be Voice to make normal open call log can be opened(fax could not be opened) + mockData.records.forEach((x) => { + x.type = 'Voice'; + }); + const body = handler?.(mockData) ?? mockData; return { body, @@ -798,7 +887,11 @@ export class RcMock extends PlatformMock { { repeat, response: ({ mockData }) => { + // * set default result always be "In Progress" to prevent not have any call in call history + mockData.records[0].result = 'In Progress'; + const body = (handler?.(mockData) ?? mockData) as UserCallLogResponse; + // log('🐞 ~ body:', body); return { body, }; @@ -842,6 +935,14 @@ export class RcMock extends PlatformMock { { repeat, response: ({ mockData }) => { + for (const record of mockData.records) { + // when to field length > 1, it is group message + // 'True' specifies that message is sent exactly to this recipient + // at least one of to number list should be the target number in group message + if (record.direction === 'Inbound' && record.to.length > 1) { + record.to[0].target = true; + } + } return { body: handler?.(mockData) ?? mockData, }; @@ -868,8 +969,23 @@ export class RcMock extends PlatformMock { ); } - postSipProvision() { - this.post('/restapi/v1.0/client-info/sip-provision'); + postSipProvision( + handler?: (mockData: typeof sipProvisionBody) => typeof sipProvisionBody, + repeat?: number, + status = 200, + ) { + this.post('/restapi/v1.0/client-info/sip-provision', status as any, { + repeat, + response: ({ mockData }) => { + const data = { + ...sipProvisionBody, + ...mockData, + }; + return { + body: handler?.(data) ?? data, + }; + }, + }); } getClientInfo(handler?: () => any, repeat = 0) { @@ -1075,12 +1191,13 @@ export class RcMock extends PlatformMock { } postBridges( + repeat = 0, handler?: ( mockData: typeof postRcvBridgesBody, ) => typeof postRcvBridgesBody, ) { this.post('/rcvideo/v1/bridges' as any, 200, { - repeat: 0, + repeat, response: ({ body }) => { const responseData = { ...postRcvBridgesBody, @@ -1405,28 +1522,34 @@ export class RcMock extends PlatformMock { } postConferenceCall( - handler?: (res: CallSessionObject) => CallSessionObject, + handler?: (res: CreateConferenceResponse) => CreateConferenceResponse, repeat?: number, ) { - const conferenceCallRes: CallSessionObject = { - ...conferenceCallResponse, - creationTime: new Date().getTime().toString(), - } as any; this.post('/restapi/v1.0/account/:accountId/telephony/conference', 201, { - response: { - body: handler?.(conferenceCallRes) ?? conferenceCallRes, - }, repeat, + response: () => { + const conferenceCallRes: CreateConferenceResponse = { + ...conferenceCallResponse, + session: { + ...conferenceCallResponse.session, + creationTime: new Date().getTime().toString(), + } as any, + }; + return { + body: handler?.(conferenceCallRes) ?? conferenceCallRes, + } as any; + }, }); } bringInToConference( handler?: (res: CallParty) => CallParty, repeat?: number, + status: HttpStatusCode = 201, ) { this.post( '/restapi/v1.0/account/:accountId/telephony/sessions/:telephonySessionId/parties/bring-in', - 201, + status as any, { response: { body: @@ -1438,6 +1561,19 @@ export class RcMock extends PlatformMock { ); } + removePartyFromConference(repeat?: number, status: HttpStatusCode = 201) { + this.post( + '/restapi/v1.0/account/:accountId/telephony/sessions/:telephonySessionId/parties/:partyId' as any, + status, + { + response: { + body: {}, + }, + repeat, + }, + ); + } + async triggerPresenceChanged( handler?: (eventData: GetPresenceInfo) => GetPresenceInfo, ) { @@ -1468,12 +1604,14 @@ export class RcMock extends PlatformMock { sessions, }: { sessions?: NormalizedSession[]; - handler?: (eventData: GetPresenceInfo) => GetPresenceInfo; + handler?: ( + eventData: EventData, + ) => EventData; }) { const activeCalls = sessions ? this.generateActiveCalls(sessions, [], sessions.map((i) => i.id) as any) : []; - const event = { + const event: EventData = { event: '/restapi/v1.0/account/~/extension/~/presence?detailedTelephonyState=true&sipData=true&totalActiveCalls', timestamp: new Date().toISOString(), @@ -1481,6 +1619,7 @@ export class RcMock extends PlatformMock { activeCalls, allowSeeMyPresence: true, dndStatus: 'TakeAllCalls', + // @ts-ignore extensionId: 160751006, meetingsStatus: 'Disconnected', pickUpCallsOnHold: false, @@ -1491,9 +1630,9 @@ export class RcMock extends PlatformMock { totalActiveCalls: activeCalls.length, userStatus: 'Available', // TODO: fix type for miss `uri, extension, message, meetingStatus` - } as any as GetPresenceInfo, + }, }; - await this.subscription.trigger(handler?.(event as any) ?? event); + await this.subscription.trigger(handler?.(event) ?? event); } async receiveCall({ @@ -1524,8 +1663,8 @@ export class RcMock extends PlatformMock { isWebRTC = true, useUserAgentSession = false, direction = callDirection.outbound, - telephonySessionId = new Date().getTime().toString(), - sessionId = new Date().getTime().toString(), + telephonySessionId = makeTelephonySessionId(), + sessionId = makeWebphoneSessionId(), fromNumberData, toNumberData, startTime, @@ -1544,14 +1683,16 @@ export class RcMock extends PlatformMock { ...props, }); - const telephoneSessionId = telephonySessionBuilder.telephoneSessionId; const event = telephonySessionBuilder.done(); await this.subscription.trigger(event); if (isWebRTC) { - const webSession = new WebphoneSessionMock(telephoneSessionId); - const callEvent = - direction === callDirection.inbound ? 'invite' : 'inviteSent'; - const { webphone } = webSession; + const webSession = new WebphoneSessionMock( + telephonySessionBuilder.getTelephonySessionId(), + telephonySessionBuilder.getPartyId(), + telephonySessionBuilder.getSessionId(), + ); + const callEvent = 'invite'; + const webphone = webSession.webphone!; telephonySessionBuilder.setRelatedWebphoneSession(webSession); if (useUserAgentSession && direction === callDirection.inbound) { @@ -1570,9 +1711,9 @@ export class RcMock extends PlatformMock { webphone.userAgent.trigger(callEvent, session); this.removeWebphone = () => webSession.remove(); - await new Promise((r) => setTimeout(r, 1000)); } - return telephoneSessionId; + + return telephonySessionBuilder.getTelephonySessionId(); } async connectLatestCall() { @@ -1584,15 +1725,15 @@ export class RcMock extends PlatformMock { async disConnectLatestCall() { const [telephonySessionInstance] = telephonySessionBuildersCache.slice(-1); - telephonySessionInstance.setDisconnected(); - const event = telephonySessionInstance.done(); + telephonySessionInstance?.setDisconnected(); + const event = telephonySessionInstance?.done(); telephonySessionInstance?.relatedWebphoneSession?.terminate(); await this.subscription.trigger(event); } async hangUp(telephonySessionId: string) { const telephonySessionInstance = telephonySessionBuildersCache.find( - (s: any) => s.telephoneSessionId === telephonySessionId, + (s) => s.getTelephonySessionId() === telephonySessionId, ); if (telephonySessionInstance) { @@ -1603,9 +1744,20 @@ export class RcMock extends PlatformMock { } } + async reject(telephonySessionId: string) { + const telephonySessionInstance = telephonySessionBuildersCache.find( + (s) => s.getTelephonySessionId() === telephonySessionId, + ); + if (telephonySessionInstance) { + telephonySessionInstance.setDisconnected(); + const event = telephonySessionInstance.done(); + await this.subscription.trigger(event); + } + } + async goneCall(telephonySessionId: string) { const telephonySessionInstance = telephonySessionBuildersCache.find( - (s: any) => s.telephoneSessionId === telephonySessionId, + (s) => s.getTelephonySessionId() === telephonySessionId, ); if (telephonySessionInstance) { @@ -1618,34 +1770,36 @@ export class RcMock extends PlatformMock { async answer(telephonySessionId: string) { const telephonySessionBuilder = telephonySessionBuildersCache.find( - (s: any) => s.telephoneSessionId === telephonySessionId, + (s) => s.getTelephonySessionId() === telephonySessionId, ); if (telephonySessionBuilder) { + await this.holdOtherCalls(telephonySessionId); telephonySessionBuilder.setConnected(); const event = telephonySessionBuilder.done(); await this.subscription.trigger(event); } } - async unholdCall(telephonySessionId: string, otherIds: string[]) { - const telephonySessionBuilder = telephonySessionBuildersCache.find( - (s: any) => s.telephoneSessionId === telephonySessionId, - ); - - const otherSessions = telephonySessionBuildersCache.filter((s: any) => - otherIds.includes(s.telephoneSessionId), - ); - - if (otherSessions.length) { - for (const item of otherSessions) { - item?.setHoldCall(); - const event = item.done(); + async holdOtherCalls(currentTelephonySessionId: string) { + for (const builder of telephonySessionBuildersCache) { + if ( + builder.getTelephonySessionId() !== currentTelephonySessionId && + builder.getStatus() !== PartyStatusCode.gone + ) { + builder.setHoldCall(); + const event = builder.done(); await this.subscription.trigger(event); } } + } + async unholdCall(telephonySessionId: string) { + const telephonySessionBuilder = telephonySessionBuildersCache.find( + (s) => s.getTelephonySessionId() === telephonySessionId, + ); if (telephonySessionBuilder) { + this.holdOtherCalls(telephonySessionId); telephonySessionBuilder.setConnected(); const event = telephonySessionBuilder.done(); await this.subscription.trigger(event); @@ -1654,7 +1808,7 @@ export class RcMock extends PlatformMock { async holdCall(telephonySessionId: string) { const telephonySessionBuilder = telephonySessionBuildersCache.find( - (s: any) => s.telephoneSessionId === telephonySessionId, + (s) => s.getTelephonySessionId() === telephonySessionId, ); if (telephonySessionBuilder) { telephonySessionBuilder.setHoldCall(); @@ -1665,7 +1819,7 @@ export class RcMock extends PlatformMock { async muteCall(telephonySessionId: string) { const telephonySessionBuilder = telephonySessionBuildersCache.find( - (s: any) => s.telephoneSessionId === telephonySessionId, + (s) => s.getTelephonySessionId() === telephonySessionId, ); if (telephonySessionBuilder) { telephonySessionBuilder.setMuteCall(); @@ -1676,7 +1830,7 @@ export class RcMock extends PlatformMock { async startRecord(telephonySessionId: string) { const telephonySessionBuilder = telephonySessionBuildersCache.find( - (s: any) => s.telephoneSessionId === telephonySessionId, + (s) => s.getTelephonySessionId() === telephonySessionId, ); if (telephonySessionBuilder) { telephonySessionBuilder.startRecord(); @@ -1690,7 +1844,7 @@ export class RcMock extends PlatformMock { '/restapi/v1.0/account/:accountId/extension/:extensionId/presence', 200, { - response: ({ mockData }) => { + response: () => { const activeCalls = this.generateActiveCalls(sessions); return { body: { @@ -1714,7 +1868,7 @@ export class RcMock extends PlatformMock { ); } - completeWarmTransfer(status: 200 | 409 | 503 = 200) { + completeWarmTransfer(status: 200 | 409 | 503 = 200, repeat = 0) { let body = {}; switch (status) { case 200: @@ -1742,7 +1896,7 @@ export class RcMock extends PlatformMock { response: { body, }, - repeat: 0, + repeat, }, ); } @@ -1849,7 +2003,7 @@ export class RcMock extends PlatformMock { } this.patch( '/restapi/v1.0/account/:accountId/telephony/sessions/:telephonySessionId/parties/:partyId/recordings/:recordingId', - status, + status as any, { response: { body, @@ -1924,7 +2078,7 @@ export class RcMock extends PlatformMock { mute(status: HttpStatusCode = 200) { this.patch( '/restapi/v1.0/account/:accountId/telephony/sessions/:telephonySessionId/parties/:partyId', - status, + status as any, { body: { muted: true, @@ -1936,7 +2090,7 @@ export class RcMock extends PlatformMock { unmute(status: HttpStatusCode = 200) { this.patch( '/restapi/v1.0/account/:accountId/telephony/sessions/:telephonySessionId/parties/:partyId', - status, + status as any, { body: { muted: false, @@ -1982,6 +2136,68 @@ export class RcMock extends PlatformMock { ); } + partySupervise( + supervise: 'hold' | 'unhold', + status: HttpStatusCode = 200, + handler?: ( + params: { partyId: string; telephonySessionId: string }, + partyInfo: PartySuperviseResponse, + ) => PartySuperviseResponse, + repeat = 0, + ) { + let callStatus: Partial; + if (supervise === 'hold') { + callStatus = { + code: 'Hold', + }; + } else if (supervise === 'unhold') { + callStatus = { + code: 'Answered', + }; + } else { + throw new Error(`Unsupported supervise ${supervise}`); + } + this.post( + `/restapi/v1.0/account/~/telephony/sessions/:telephonySessionId/parties/:partyId/${supervise}` as any, + status, + { + repeat, + response: ({ params }) => { + const body: PartySuperviseResponse = { + ...partySuperviseResponse, + id: params.partyId, + status: callStatus, + } as any; + return { + body: handler?.(params, body) ?? body, + }; + }, + }, + ); + } + + holdParty( + status: HttpStatusCode = 200, + handler?: ( + params: { partyId: string; telephonySessionId: string }, + partyInfo: PartySuperviseResponse, + ) => PartySuperviseResponse, + repeat = 0, + ) { + this.partySupervise('hold', status, handler, repeat); + } + + unholdParty( + status: HttpStatusCode = 200, + handler?: ( + params: { partyId: string; telephonySessionId: string }, + partyInfo: PartySuperviseResponse, + ) => PartySuperviseResponse, + repeat = 0, + ) { + this.partySupervise('unhold', status, handler, repeat); + } + getCallRecordingData( handler?: ( recordingData: GetCallRecordingResponse, diff --git a/packages/ringcentral-mock/src/brands.ts b/packages/ringcentral-mock/src/brands.ts new file mode 100644 index 0000000000..3a87b3db88 --- /dev/null +++ b/packages/ringcentral-mock/src/brands.ts @@ -0,0 +1,25 @@ +// TODO: refactor for internal rc mock +export const brands = { + 2020: 'atos', + 3420: 'att', + 3460: 'attub', + 6010: 'avaya', + 7710: 'bt', + 4610: 'eastlink', + 4210: 'ecotel', + 4810: 'mcm', + 2110: 'rainbow', + 1210: 'rc', + 5010: 'rcau', + 3610: 'rcca', + 2010: 'rceu', + 3710: 'rcuk', + 7310: 'telus', + 2210: 'verizon', + 7010: 'vodafone', + 4710: 'versatel', + 4910: 'frontier', + 2030: 'dttelekom', + 2040: 'dtatos', + 2050: 'sunrise', +}; diff --git a/packages/ringcentral-mock/src/faker.ts b/packages/ringcentral-mock/src/faker.ts index 4fba0005b1..5b65bd74e2 100644 --- a/packages/ringcentral-mock/src/faker.ts +++ b/packages/ringcentral-mock/src/faker.ts @@ -1,7 +1,7 @@ /* eslint-disable global-require */ +import faker from '@faker-js/faker'; import type { JSONSchemaFakerOptions } from 'json-schema-faker'; import jsf from 'json-schema-faker'; -import faker from '@faker-js/faker'; export type Generate = typeof jsf.generate; diff --git a/packages/ringcentral-mock/src/interface.ts b/packages/ringcentral-mock/src/interface.ts index 1b954c7b59..93e332315f 100644 --- a/packages/ringcentral-mock/src/interface.ts +++ b/packages/ringcentral-mock/src/interface.ts @@ -1,9 +1,11 @@ -import type callDirections from '@ringcentral-integration/commons/enums/callDirections'; +import type { CallDirection } from '@ringcentral-integration/commons/enums/callDirections'; import type { NumberData, PartyStatusCode, + Party, } from '@ringcentral-integration/commons/integration-test/mock/telephonySessionBuilder'; import type { OpenAPIV3 } from 'openapi-types'; + import type { GetMessageInfoResponse } from './platform/interfaces'; interface RedefinedSchema { @@ -39,15 +41,11 @@ interface NonArraySchemaObject extends BaseSchemaObject { export type SchemaObject = ArraySchemaObject | NonArraySchemaObject; -type CallDirectionsKeys = keyof typeof callDirections; - -type CallDirections = typeof callDirections[CallDirectionsKeys]; - export interface MakeCallProps { phoneNumber?: string; isWebRTC?: boolean; useUserAgentSession?: boolean; - direction?: CallDirections; + direction?: CallDirection; telephonySessionId?: string; sessionId?: string; fromNumberData?: NumberData; @@ -56,6 +54,9 @@ export interface MakeCallProps { isRecording?: boolean; status?: PartyStatusCode; queueCall?: boolean; + originType?: string; + peerId?: Party['status']['peerId']; + reason?: string; } export interface MessageProps { diff --git a/packages/ringcentral-mock/src/lib/generateFeatures.ts b/packages/ringcentral-mock/src/lib/generateFeatures.ts index 3eafe35e8c..46926b4d60 100644 --- a/packages/ringcentral-mock/src/lib/generateFeatures.ts +++ b/packages/ringcentral-mock/src/lib/generateFeatures.ts @@ -1,4 +1,5 @@ import { clone } from 'ramda'; + import featuresBody from '../platform/data/features.json'; type IIds = @@ -250,7 +251,9 @@ type IIds = | 'EditMeetingsProvider' | 'EmergencyCallNotification' | 'RCMeetingApps' - | 'IMS'; + | 'IMS' + | 'RingSense' + | 'VoiceCallsRecordingTranscriptions'; export type IGenerateFeaturesDataProps = Partial>; diff --git a/packages/ringcentral-mock/src/lib/generateTelephonyState.ts b/packages/ringcentral-mock/src/lib/generateTelephonyState.ts index 8f34fc3cc7..8bf68ad33b 100644 --- a/packages/ringcentral-mock/src/lib/generateTelephonyState.ts +++ b/packages/ringcentral-mock/src/lib/generateTelephonyState.ts @@ -1,3 +1,5 @@ +// @ts-nocheck +// TODO: fix type import presenceBody from '../platform/data/presence.json'; import type { DetailedExtensionPresenceEventBody, @@ -7,9 +9,9 @@ import type { DetailedExtensionPresenceWithSIPEvent } from '../platform/interfac export interface IGenerateTelephonyState { hasActiveCall: boolean; - direction?: string; - phoneNumber?: string; - eventData?: DetailedExtensionPresenceWithSIPEvent; + direction?: string | null; + phoneNumber?: string | null; + eventData?: DetailedExtensionPresenceWithSIPEvent | null; } export const generateTelephonyState = ({ diff --git a/packages/ringcentral-mock/src/platform/apis/Put.ts b/packages/ringcentral-mock/src/platform/apis/Put.ts index 5b3235e59a..fe3a299bbf 100644 --- a/packages/ringcentral-mock/src/platform/apis/Put.ts +++ b/packages/ringcentral-mock/src/platform/apis/Put.ts @@ -96,6 +96,7 @@ export interface Put { * OK */ 200: PresenceInfoResponse; + 503: any; }; }; '/restapi/v1.0/account/:accountId/call-queues/:groupId/presence': { diff --git a/packages/ringcentral-mock/src/platform/data/clientInfo.json b/packages/ringcentral-mock/src/platform/data/clientInfo.json index 83c4f61f2a..16ef391f00 100644 --- a/packages/ringcentral-mock/src/platform/data/clientInfo.json +++ b/packages/ringcentral-mock/src/platform/data/clientInfo.json @@ -1,65 +1,71 @@ { - "uri": "https://api-xmnup.lab.nordigy.ru/restapi/v1.0/client-info", - "client": { - "detected": true, - "userAgent": "PostmanRuntime/7.29.2", - "appId": "7gufiGT3T3CCuCP37hMDaQ~LW0n-yIkSYWWsLqeWA2Syg", - "appName": "Microsoft Teams: RingCentral", - "appVersion": "7.29.2", - "locale": "en-US" - }, + "uri": "https://api-rcapps-labs_domain/restapi/v1.0/client-info", + "client": {}, "provisioning": { "interopClientIds": { - "serviceWeb": "DE0Af3c63a68092734D333990A0401D349EEd9bdc46e80ec158238C3D857A92A", - "expressSetup": "a85da13c1fd991f09A9EB97FB4301c1ebf9e3f840431877921151c06660858bf", - "qosReports": "BG3JfKMgSUaSjZvEIT1KeA", - "analyticsPortal": "YCzy6r9qTyCpyMPN1rUgVA", - "carPlay": "obeAu02zQyWALRwW9R2avw", - "rcVideo": "AJdKBzHLRkie9peuDaZLLw", - "rcMeetings": "KI38mp4Ns033cEC681b94677b9B3AD0bbeFC2Ab1E" + "serviceWeb": "xxx", + "expressSetup": "xxx", + "qosReports": "xxx", + "analyticsPortal": "xxx", + "carPlay": "xxx", + "rcVideo": "xxx", + "rcMeetings": "xxx", + "chc": "xxx", + "ringSense": "xxx" }, "webUris": { - "expressSetupMobile": "https://service-xmnup-us.secure.lab.nordigy.ru/rc-setup/mobile/?appUrlScheme={appUrlScheme}&code={authCode}&hash=", - "signUp": "http://service-xmnup-us.lab.nordigy.ru/office/plansandpricing.html", - "support": "http://service-xmnup-us.lab.nordigy.ru/support.html", - "mobileWebUsers": "https://service-xmnup.lab.nordigy.ru/mobileweb/settings.html?session={random}&mid=1080138004&code={authCode}#extensions?tab=%22users%22&localeId=en_US&appUrlScheme={appUrlScheme}", - "mobileWebBilling": "https://service-xmnup.lab.nordigy.ru/mobileweb/settings.html?session={random}&mid=1080138004&code={authCode}#billing/main?appUrlScheme={appUrlScheme}&localeId=en_US", - "mobileWebPhoneSystem": "https://service-xmnup.lab.nordigy.ru/mobileweb/settings.html?session={random}&mid=1080138004&code={authCode}#main?screen=phoneSystem&appUrlScheme={appUrlScheme}&localeId=en_US", - "mobileWebUserSettings": "https://service-xmnup.lab.nordigy.ru/mobileweb/settings.html?session={random}&mid=1080138004&code={authCode}#main?screen=myInbound&appUrlScheme={appUrlScheme}&localeId=en_US", - "mobileWebTellAFriend": "https://service-xmnup.lab.nordigy.ru/mobileweb/settings.html?session={random}&mid=1080138004&code={authCode}#tellAFriend/main?appUrlScheme={appUrlScheme}&localeId=en_US", - "mobileWebChangePassword": "https://service-xmnup.lab.nordigy.ru/mobileweb/settings.html?session={random}&mid=1080138004&code={authCode}#changePwd?brandId=1210&mid=1080138004&appUrlScheme={appUrlScheme}&localeId=en_US", - "mobileWebInternationalCalling": "https://service-xmnup.lab.nordigy.ru/mobileweb/settings.html?session={random}&mid=1080138004&code={authCode}#billing/internationalCalling?appUrlScheme={appUrlScheme}&localeId=en_US", - "mobileWebCallHandling": "https://service-xmnup.lab.nordigy.ru/mobileweb/settings.html?session={random}&mid=1080138004&code={authCode}#userCallForwarding/rules?mid=1080138004&appUrlScheme={appUrlScheme}&localeId=en_US", - "mobileWebNotifications": "https://service-xmnup.lab.nordigy.ru/mobileweb/settings.html?session={random}&mid=1080138004&code={authCode}#notificationSettings?mid=1080138004&appUrlScheme={appUrlScheme}&localeId=en_US", - "mobileWebReporting": "https://service-xmnup.lab.nordigy.ru/mobileweb/reports.html?session={random}&mid=1080138004&code={authCode}&appUrlScheme={appUrlScheme}&localeId=en_US", - "mobileWebTrialUpgrade": "https://service-xmnup.lab.nordigy.ru/mobileweb/settings.html?session={random}&mid=1080138004&code={authCode}#billing/upgrade?brandId=1210&appUrlScheme={appUrlScheme}&localeId=en_US", - "mobileWebCallQueueManagement": "https://service-xmnup.lab.nordigy.ru/mobileweb/settings.html?session={random}&mid=1080138004&code={authCode}#callQueueManagement?mid=1080138004&appUrlScheme={appUrlScheme}&localeId=en_US", - "serviceWebHome": "https://service-xmnup.lab.nordigy.ru/login/main.asp?mid=1080138004&code={authCode}&rdr=", - "serviceWebPhoneSystem": "https://service-xmnup.lab.nordigy.ru/login/main.asp?mid=1080138004&code={authCode}&rdr=/company/index.html", - "serviceWebUserSettings": "https://service-xmnup.lab.nordigy.ru/login/main.asp?mid=1080138004&code={authCode}&rdr=/settings/index.html", - "serviceWebBilling": "https://service-xmnup.lab.nordigy.ru/login/main.asp?mid=1080138004&code={authCode}&rdr=%2Fsettings%2Fbilling.html", + "expressSetupMobile": "https://service-xmrupxmn-us-secure.intlabs_domain/rc-setup/mobile/?appUrlScheme={appUrlScheme}&code={authCode}&hash=", + "signUp": "http://service-xmrupxmn-us.intlabs_domain/office/plansandpricing.html", + "support": "https://support.ringcentral.com", + "mobileWebUsers": "https://service-labs_domain/mobileweb/settings.html?session={random}&mid=334153004&code={authCode}#extensions?tab=%22users%22&localeId=en_US&appUrlScheme={appUrlScheme}", + "mobileWebBilling": "https://service-labs_domain/mobileweb/settings.html?session={random}&mid=334153004&code={authCode}#billing/main?appUrlScheme={appUrlScheme}&localeId=en_US", + "mobileWebPhoneSystem": "https://service-labs_domain/mobileweb/settings.html?session={random}&mid=334153004&code={authCode}#main?screen=phoneSystem&appUrlScheme={appUrlScheme}&localeId=en_US", + "mobileWebUserSettings": "https://service-labs_domain/mobileweb/settings.html?session={random}&mid=334153004&code={authCode}#main?screen=myInbound&appUrlScheme={appUrlScheme}&localeId=en_US", + "mobileWebTellAFriend": "https://service-labs_domain/mobileweb/settings.html?session={random}&mid=334153004&code={authCode}#tellAFriend/main?appUrlScheme={appUrlScheme}&localeId=en_US", + "mobileWebChangePassword": "https://service-labs_domain/mobileweb/settings.html?session={random}&mid=334153004&code={authCode}#changePwd?brandId=1210&mid=334153004&appUrlScheme={appUrlScheme}&localeId=en_US", + "mobileWebInternationalCalling": "https://service-labs_domain/mobileweb/settings.html?session={random}&mid=334153004&code={authCode}#billing/internationalCalling?appUrlScheme={appUrlScheme}&localeId=en_US", + "mobileWebCallHandling": "https://service-labs_domain/mobileweb/settings.html?session={random}&mid=334153004&code={authCode}#userCallForwarding/rules?mid=334153004&appUrlScheme={appUrlScheme}&localeId=en_US", + "mobileWebNotifications": "https://service-labs_domain/mobileweb/settings.html?session={random}&mid=334153004&code={authCode}#notificationSettings?mid=334153004&appUrlScheme={appUrlScheme}&localeId=en_US", + "mobileWebReporting": "https://service-labs_domain/mobileweb/reports.html?session={random}&mid=334153004&code={authCode}&appUrlScheme={appUrlScheme}&localeId=en_US", + "mobileWebTrialUpgrade": "https://service-labs_domain/mobileweb/settings.html?session={random}&mid=334153004&code={authCode}#billing/upgrade?brandId=1210&appUrlScheme={appUrlScheme}&localeId=en_US", + "mobileWebCallQueueManagement": "https://service-labs_domain/mobileweb/settings.html?session={random}&mid=334153004&code={authCode}#callQueueManagement?mid=334153004&appUrlScheme={appUrlScheme}&localeId=en_US", + "serviceWebHome": "https://service-labs_domain/login/main.asp?mid=334153004&code={authCode}&rdr=", + "serviceWebPhoneSystem": "https://service-labs_domain/login/main.asp?mid=334153004&code={authCode}&rdr=/company/index.html", + "serviceWebUserSettings": "https://service-labs_domain/login/main.asp?mid=334153004&code={authCode}&rdr=/settings/index.html", + "serviceWebBilling": "https://service-labs_domain/login/main.asp?mid=334153004&code={authCode}&rdr=%2Fsettings%2Fbilling.html", "serviceWebTellAFriend": "https://www.ringcentral.com/buylove/buylove.html?BMID=REFTIER&PID=6969", - "serviceWebChangePassword": "https://service-xmnup.lab.nordigy.ru/login/changePwd.html?localeId=en_US&mid=1080138004&number=12054061927", - "serviceWebCallHandling": "https://service-xmnup.lab.nordigy.ru/login/main.asp?mid=1080138004&code={authCode}&rdr=%2Fsettings%2Findex.html%23%2Fsettings%2FcallHandling", - "serviceWebBlockedCalls": "https://service-xmnup.lab.nordigy.ru/login/main.asp?mid=1080138004&code={authCode}&rdr=%2Fsettings%2Findex.html%23%2Fsettings%2FscreeningGreetingHoldMusic%2FblockedCalls", - "serviceWebUserPhones": "https://service-xmnup.lab.nordigy.ru/login/main.asp?mid=1080138004&code={authCode}&rdr=%2Fsettings%2Findex.html%23%2Fsettings%2FphonesAndNumbers%2Fphone", - "serviceWebBillingPayment": "https://service-xmnup.lab.nordigy.ru/billing/payment.html", + "serviceWebChangePassword": "https://service-labs_domain/login/changePwd.html?localeId=en_US&mid=334153004&number=14012223521", + "serviceWebCallHandling": "https://service-labs_domain/login/main.asp?mid=334153004&code={authCode}&rdr=%2Fsettings%2Findex.html%23%2Fsettings%2FcallHandling", + "serviceWebBlockedCalls": "https://service-labs_domain/login/main.asp?mid=334153004&code={authCode}&rdr=%2Fsettings%2Findex.html%23%2Fsettings%2FscreeningGreetingHoldMusic%2FblockedCalls", + "serviceWebUserPhones": "https://service-labs_domain/login/main.asp?mid=334153004&code={authCode}&rdr=%2Fsettings%2Findex.html%23%2Fsettings%2FphonesAndNumbers%2Fphone", + "serviceWebBillingPayment": "https://service-labs_domain/billing/payment.html", "expiresIn": 1800, "liveReports": "https://do-not-use-deprecated.net", - "mobileWebQosReports": "https://analytics-xmnup.ponylab.ringcentral.com/qos", + "mobileWebQosReports": "https://dev.internal.rcdevops.com/qos", "mobileAppDownload": "https://www.ringcentral.com/apps/?apps=ringcentral-glip,ringcentral-phone", "meetings": "https://rcm.rcdev.ringcentral.com", "webinar": "https://webinar.ringcentral.com", - "meetingsRecordings": "https://service-xmnup.lab.nordigy.ru", - "analyticsPortal": "https://analytics-xmnup.ponylab.ringcentral.com/login?code={authCode}", - "expressSetup": "https://service-xmnup-us.secure.lab.nordigy.ru/rc-setup/web/index.html?code={authCode}", - "resetPassword": "https://login-xmnup.rclabenv.com/api/resetPassword?client_id={clientId}&login_hint={username}&redirect_uri={appUrlScheme}&display={display}&brand_id=1210&ui_locales=en_US", - "smbCompanySettings": "https://service-xmnup.lab.nordigy.ru/smb/entry/companySettings?mid=1080138004&code={authCode}&localeId=en_US&theme={themeId}&appId=7gufiGT3T3CCuCP37hMDaQ~LW0n-yIkSYWWsLqeWA2Syg", - "smbVoicemailPin": "https://service-xmnup.lab.nordigy.ru/smb/entry/mobile/userSetup/pin?mid=1080138004&code={authCode}&localeId=en_US&theme={themeId}&appId=7gufiGT3T3CCuCP37hMDaQ~LW0n-yIkSYWWsLqeWA2Syg", - "smbUserCallForwarding": "https://service-xmnup.lab.nordigy.ru/smb/entry/userSetup/callForwarding?mid=1080138004&code={authCode}&localeId=en_US&theme={themeId}&appId=7gufiGT3T3CCuCP37hMDaQ~LW0n-yIkSYWWsLqeWA2Syg", - "smbUserVoicemail": "https://service-xmnup.lab.nordigy.ru/smb/entry/userSetup/voicemail?mid=1080138004&code={authCode}&localeId=en_US&theme={themeId}&appId=7gufiGT3T3CCuCP37hMDaQ~LW0n-yIkSYWWsLqeWA2Syg", - "smbFacSettings": "https://service-xmnup.lab.nordigy.ru/smb/entry/userSetup/forwardAllCalls?mid=1080138004&code={authCode}&localeId=en_US&theme={themeId}&appId=7gufiGT3T3CCuCP37hMDaQ~LW0n-yIkSYWWsLqeWA2Syg", - "smbMobileFacSettings": "https://service-xmnup.lab.nordigy.ru/smb/entry/mobile/userSetup/forwardAllCalls?mid=1080138004&code={authCode}&localeId=en_US&theme={themeId}&appId=7gufiGT3T3CCuCP37hMDaQ~LW0n-yIkSYWWsLqeWA2Syg" + "meetingsRecordings": "https://service-labs_domain", + "analyticsPortal": "https://analytics-xmrupxmn.ponylab.ringcentral.com/login?code={authCode}", + "expressSetup": "https://service-xmrupxmn-us-secure.intlabs_domain/rc-setup/web/index.html?code={authCode}", + "resetPassword": "https://login-labs_domain/api/resetPassword?client_id={clientId}&login_hint={username}&redirect_uri={appUrlScheme}&display={display}&brand_id=1210&ui_locales=en_US", + "smbCompanySettings": "https://service-labs_domain/smb/entry/companySettings?mid=334153004&code={authCode}&localeId=en_US&theme={themeId}&appId=7gufiGT3T3CCuCP37hMDaQ~LW0n-yIkSYWWsLqeWA2Syg", + "smbAdminSettings": "https://service-labs_domain/smb/entry/adminSettings?mid=334153004&code={authCode}&localeId=en_US&theme={themeId}&appId=7gufiGT3T3CCuCP37hMDaQ~LW0n-yIkSYWWsLqeWA2Syg", + "smbVoicemailPin": "https://service-labs_domain/smb/entry/mobile/userSetup/pin?mid=334153004&code={authCode}&localeId=en_US&theme={themeId}&appId=7gufiGT3T3CCuCP37hMDaQ~LW0n-yIkSYWWsLqeWA2Syg", + "smbUserCallForwarding": "https://service-labs_domain/smb/entry/userSetup/callForwarding?mid=334153004&code={authCode}&localeId=en_US&theme={themeId}&appId=7gufiGT3T3CCuCP37hMDaQ~LW0n-yIkSYWWsLqeWA2Syg", + "smbUserVoicemail": "https://service-labs_domain/smb/entry/userSetup/voicemail?mid=334153004&code={authCode}&localeId=en_US&theme={themeId}&appId=7gufiGT3T3CCuCP37hMDaQ~LW0n-yIkSYWWsLqeWA2Syg", + "smbFacSettings": "https://service-labs_domain/smb/entry/userSetup/forwardAllCalls?mid=334153004&code={authCode}&localeId=en_US&theme={themeId}&appId=7gufiGT3T3CCuCP37hMDaQ~LW0n-yIkSYWWsLqeWA2Syg", + "smbMobileFacSettings": "https://service-labs_domain/smb/entry/mobile/userSetup/forwardAllCalls?mid=334153004&code={authCode}&localeId=en_US&theme={themeId}&appId=7gufiGT3T3CCuCP37hMDaQ~LW0n-yIkSYWWsLqeWA2Syg", + "smbFaccSettings": "https://service-labs_domain/smb/entry/adminSettings/forwardAllCompanyCalls?mid=334153004&code={authCode}&localeId=en_US&theme={themeId}&appId=7gufiGT3T3CCuCP37hMDaQ~LW0n-yIkSYWWsLqeWA2Syg", + "smbMobileFaccSettings": "https://service-labs_domain/smb/entry/mobile/adminSettings/forwardAllCompanyCalls?mid=334153004&code={authCode}&localeId=en_US&theme={themeId}&appId=7gufiGT3T3CCuCP37hMDaQ~LW0n-yIkSYWWsLqeWA2Syg", + "smbMobileCompanyInfo": "https://service-labs_domain/smb/entry/mobile/adminSettings/companyInfo?mid=334153004&code={authCode}&localeId=en_US&theme={themeId}&appId=7gufiGT3T3CCuCP37hMDaQ~LW0n-yIkSYWWsLqeWA2Syg", + "smbMobileUser": "https://service-labs_domain/smb/entry/mobile/adminSettings/team?mid=334153004&code={authCode}&localeId=en_US&theme={themeId}&appId=7gufiGT3T3CCuCP37hMDaQ~LW0n-yIkSYWWsLqeWA2Syg", + "smbMobilePhone": "https://service-labs_domain/smb/entry/mobile/adminSettings/phoneSystem?mid=334153004&code={authCode}&localeId=en_US&theme={themeId}&appId=7gufiGT3T3CCuCP37hMDaQ~LW0n-yIkSYWWsLqeWA2Syg", + "smbPTTChannelsMobile": "https://service-labs_domain/smb/entry/pttChannels/mobile?mid=334153004&code={authCode}&localeId=en_US&theme={themeId}&appId=7gufiGT3T3CCuCP37hMDaQ~LW0n-yIkSYWWsLqeWA2Syg", + "appGalleryHome": "https://www.ringcentral.com/apps/", + "ringSensePortalBaseUri": "https://xmrupxmn.ringsenselabs_domain", + "chcSettings": "https://service-labs_domain/chc/{path}?mid=334153004&code={authCode}&localeId=en_US&theme={themeId}&appId=7gufiGT3T3CCuCP37hMDaQ~LW0n-yIkSYWWsLqeWA2Syg", + "ringSenseAuthUri": "https://xmrupxmn.ringsenselabs_domain/oauthlogin?code={authCode}&appType={appType}&sourceCallId={sourceCallId}" }, "hints": { "forceUnifiedMobileApp": { @@ -70,7 +76,7 @@ } }, "assetService": { - "baseUri": "https://platform.devtest.ringcentral.com", + "baseUri": "https://brand-assets-labs_domain", "assetId": "f7cade80b7cc92b991cf4d2806d6bd78" } } diff --git a/packages/ringcentral-mock/src/platform/data/directoryEntries.json b/packages/ringcentral-mock/src/platform/data/directoryEntries.json index a36f53cebf..f4684d32a1 100644 --- a/packages/ringcentral-mock/src/platform/data/directoryEntries.json +++ b/packages/ringcentral-mock/src/platform/data/directoryEntries.json @@ -85,6 +85,38 @@ "usageType": "DirectNumber" } ] + }, + { + "id": "1737838004", + "type": "Department", + "status": "Enabled", + "name": "Tech Support", + "extensionNumber": "10092", + "account": { + "id": "1737819004" + }, + "phoneNumbers": [ + { + "phoneNumber": "+17204084360", + "type": "VoiceFax", + "formattedPhoneNumber": "+1(720)408-4360", + "usageType": "DirectNumber" + }, + { + "phoneNumber": "+18553242201", + "type": "VoiceFax", + "formattedPhoneNumber": "+1(855)324-2201", + "usageType": "DirectNumber" + } + ] + }, + { + "id": "2279689004", + "type": "Department", + "status": "Enabled", + "name": "callQueue2", + "extensionNumber": "10093", + "account": { "id": "1737819004" } } ], "paging": { diff --git a/packages/ringcentral-mock/src/platform/data/extensions.json b/packages/ringcentral-mock/src/platform/data/extensions.json index 9e3967e9dc..cd08813806 100644 --- a/packages/ringcentral-mock/src/platform/data/extensions.json +++ b/packages/ringcentral-mock/src/platform/data/extensions.json @@ -6,7 +6,7 @@ "status": "Enabled", "firstName": "Something1", "lastName": "New1", - "email": "dean.chen@ringcentral.com", + "email": "dean11@rc.com", "extensionNumber": "101", "account": { "id": "129832006" @@ -55,7 +55,7 @@ "firstName": "Something111", "lastName": "Unactived111", "department": "TEST", - "email": "bruce.li+111@ringcentral.com", + "email": "bruce111@rc.com", "extensionNumber": "102", "account": { "id": "129832006" diff --git a/packages/ringcentral-mock/src/platform/data/features.json b/packages/ringcentral-mock/src/platform/data/features.json index 904ba31c57..db3e3aa1ab 100644 --- a/packages/ringcentral-mock/src/platform/data/features.json +++ b/packages/ringcentral-mock/src/platform/data/features.json @@ -1593,6 +1593,14 @@ "id": "RCMeetingApps", "available": true }, + { + "id": "RingSense", + "available": true + }, + { + "id": "VoiceCallsRecordingTranscriptions", + "available": true + }, { "id": "IMS", "available": false, diff --git a/packages/ringcentral-mock/src/platform/data/partySupervise.json b/packages/ringcentral-mock/src/platform/data/partySupervise.json new file mode 100644 index 0000000000..e20c6cd75b --- /dev/null +++ b/packages/ringcentral-mock/src/platform/data/partySupervise.json @@ -0,0 +1,29 @@ +{ + "accountId": "1028960004", + "attributes": {}, + "brandId": "1210", + "direction": "Inbound", + "extensionId": "1028960004", + "from": { + "deviceId": "624886004", + "extensionId": "1394474004", + "name": "test klay", + "phoneNumber": "102" + }, + "id": "p-a4a0d810f4c5cz18bada3b816z5d487a0000-2", + "muted": false, + "owner": { + "accountId": "1028960004", + "brandId": "1210", + "extensionId": "1028960004" + }, + "standAlone": false, + "status": { + "code": "Hold" + }, + "to": { + "extensionId": "1028960004", + "name": "admin user", + "phoneNumber": "101" + } +} diff --git a/packages/ringcentral-mock/src/platform/data/sipProvision.json b/packages/ringcentral-mock/src/platform/data/sipProvision.json new file mode 100644 index 0000000000..ea4fafb624 --- /dev/null +++ b/packages/ringcentral-mock/src/platform/data/sipProvision.json @@ -0,0 +1,37 @@ +{ + "device": { + "uri": "https://platform.devtest.ringcentral.com/restapi/v1.0/account/129832006/device/17737006", + "id": "17737006", + "type": "WebPhone", + "status": "Online", + "phoneLines": [], + "linePooling": "None" + }, + "sipInfo": [ + { + "transport": "WSS", + "username": "18442085446*101", + "password": "s0aO1", + "authorizationId": "17737006", + "domain": "platform.devtest.ringcentral.com", + "outboundProxy": "webphone-platform.devtest.ringcentral.com:8083", + "outboundProxyBackup": "webphone-platform.devtest.ringcentral.com:8083", + "switchBackInterval": 3600 + } + ], + "sipFlags": { + "voipFeatureEnabled": true, + "voipCountryBlocked": false, + "outboundCallsEnabled": true, + "dscpEnabled": true, + "dscpSignaling": 26, + "dscpVoice": 46, + "dscpVideo": 34 + }, + "sipErrorCodes": [ + "503", + "502", + "504" + ], + "pollingInterval": 7200 +} diff --git a/packages/ringcentral-mock/src/platform/interfaces/AccountCallLogResponse.ts b/packages/ringcentral-mock/src/platform/interfaces/AccountCallLogResponse.ts index 89f871bf7a..0668af4c35 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/AccountCallLogResponse.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/AccountCallLogResponse.ts @@ -1,6 +1,6 @@ -import type { CompanyCallLogRecord } from './CompanyCallLogRecord'; import type { CallLogNavigationInfo } from './CallLogNavigationInfo'; import type { CallLogPagingInfo } from './CallLogPagingInfo'; +import type { CompanyCallLogRecord } from './CompanyCallLogRecord'; export interface AccountCallLogResponse { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/AccountDeviceUpdate.ts b/packages/ringcentral-mock/src/platform/interfaces/AccountDeviceUpdate.ts index 7b880c707a..d9bde88edb 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/AccountDeviceUpdate.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/AccountDeviceUpdate.ts @@ -1,7 +1,7 @@ -import type { EmergencyServiceAddressResourceRequest } from './EmergencyServiceAddressResourceRequest'; import type { DeviceEmergencyInfo } from './DeviceEmergencyInfo'; import type { DeviceUpdateExtensionInfo } from './DeviceUpdateExtensionInfo'; import type { DeviceUpdatePhoneLinesInfo } from './DeviceUpdatePhoneLinesInfo'; +import type { EmergencyServiceAddressResourceRequest } from './EmergencyServiceAddressResourceRequest'; export interface AccountDeviceUpdate { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/AccountLockedSettingResponse.ts b/packages/ringcentral-mock/src/platform/interfaces/AccountLockedSettingResponse.ts index 0bc55baa19..930b4a309b 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/AccountLockedSettingResponse.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/AccountLockedSettingResponse.ts @@ -1,6 +1,6 @@ import type { ScheduleUserMeetingInfo } from './ScheduleUserMeetingInfo'; -import type { UserMeetingRecordingSetting } from './UserMeetingRecordingSetting'; import type { TelephonyLockedSettings } from './TelephonyLockedSettings'; +import type { UserMeetingRecordingSetting } from './UserMeetingRecordingSetting'; export interface AccountLockedSettingResponse { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/AccountRegionalSettings.ts b/packages/ringcentral-mock/src/platform/interfaces/AccountRegionalSettings.ts index 5fc8be4778..7ce369337d 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/AccountRegionalSettings.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/AccountRegionalSettings.ts @@ -1,9 +1,9 @@ import type { CountryInfo } from './CountryInfo'; -import type { TimezoneInfo } from './TimezoneInfo'; -import type { RegionalLanguageInfo } from './RegionalLanguageInfo'; -import type { GreetingLanguageInfo } from './GreetingLanguageInfo'; -import type { FormattingLocaleInfo } from './FormattingLocaleInfo'; import type { CurrencyInfo } from './CurrencyInfo'; +import type { FormattingLocaleInfo } from './FormattingLocaleInfo'; +import type { GreetingLanguageInfo } from './GreetingLanguageInfo'; +import type { RegionalLanguageInfo } from './RegionalLanguageInfo'; +import type { TimezoneInfo } from './TimezoneInfo'; // Account level region data (web service Auto-Receptionist settings) export interface AccountRegionalSettings { diff --git a/packages/ringcentral-mock/src/platform/interfaces/ActiveCallInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/ActiveCallInfo.ts index fbe57b2b5e..3a6c5c7c0e 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/ActiveCallInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/ActiveCallInfo.ts @@ -1,5 +1,5 @@ -import type { DetailedCallInfo } from './DetailedCallInfo'; import type { CallInfoCQ } from './CallInfoCQ'; +import type { DetailedCallInfo } from './DetailedCallInfo'; export interface ActiveCallInfo { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/AddPartyRequest.ts b/packages/ringcentral-mock/src/platform/interfaces/AddPartyRequest.ts index c7a887b090..2c3cfbc4ca 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/AddPartyRequest.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/AddPartyRequest.ts @@ -3,7 +3,7 @@ export interface AddPartyRequest { * Internal identifier of a call session * Required */ - telephonySessionId: string; + sessionId: string; /** * Internal identifier of a party that should be added to the call session * Required diff --git a/packages/ringcentral-mock/src/platform/interfaces/AnsweringRuleInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/AnsweringRuleInfo.ts index 42d925f5e3..93ac34ff72 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/AnsweringRuleInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/AnsweringRuleInfo.ts @@ -1,14 +1,14 @@ -import type { ScheduleInfo } from './ScheduleInfo'; import type { CalledNumberInfo } from './CalledNumberInfo'; import type { CallersInfo } from './CallersInfo'; import type { ForwardingInfo } from './ForwardingInfo'; -import type { UnconditionalForwardingInfo } from './UnconditionalForwardingInfo'; +import type { GreetingInfo } from './GreetingInfo'; +import type { MissedCallInfo } from './MissedCallInfo'; import type { QueueInfo } from './QueueInfo'; +import type { ScheduleInfo } from './ScheduleInfo'; +import type { SharedLinesInfo } from './SharedLinesInfo'; import type { TransferredExtensionInfo } from './TransferredExtensionInfo'; +import type { UnconditionalForwardingInfo } from './UnconditionalForwardingInfo'; import type { VoicemailInfo } from './VoicemailInfo'; -import type { GreetingInfo } from './GreetingInfo'; -import type { SharedLinesInfo } from './SharedLinesInfo'; -import type { MissedCallInfo } from './MissedCallInfo'; export interface AnsweringRuleInfo { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/AutomaticLocationUpdatesDeviceInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/AutomaticLocationUpdatesDeviceInfo.ts index 9afd1f02f6..4c3681c29e 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/AutomaticLocationUpdatesDeviceInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/AutomaticLocationUpdatesDeviceInfo.ts @@ -1,6 +1,6 @@ import type { AutomaticLocationUpdatesModelInfo } from './AutomaticLocationUpdatesModelInfo'; -import type { AutomaticLocationUpdatesSiteInfo } from './AutomaticLocationUpdatesSiteInfo'; import type { AutomaticLocationUpdatesPhoneLine } from './AutomaticLocationUpdatesPhoneLine'; +import type { AutomaticLocationUpdatesSiteInfo } from './AutomaticLocationUpdatesSiteInfo'; export interface AutomaticLocationUpdatesDeviceInfo { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/CallInfoCQ.ts b/packages/ringcentral-mock/src/platform/interfaces/CallInfoCQ.ts index a6dcf94249..5087ba84a1 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CallInfoCQ.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CallInfoCQ.ts @@ -1,5 +1,5 @@ -import type { PrimaryCQInfo } from './PrimaryCQInfo'; import type { AdditionalCQInfo } from './AdditionalCQInfo'; +import type { PrimaryCQInfo } from './PrimaryCQInfo'; // Primary/additional CQ information export interface CallInfoCQ { diff --git a/packages/ringcentral-mock/src/platform/interfaces/CallLogRecordLegInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/CallLogRecordLegInfo.ts index 65bc3639a8..80c05fe036 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CallLogRecordLegInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CallLogRecordLegInfo.ts @@ -1,9 +1,9 @@ import type { BillingInfo } from './BillingInfo'; -import type { CallLogDelegateInfo } from './CallLogDelegateInfo'; -import type { ExtensionInfoCallLog } from './ExtensionInfoCallLog'; import type { CallLogCallerInfo } from './CallLogCallerInfo'; -import type { CallLogRecordingInfo } from './CallLogRecordingInfo'; +import type { CallLogDelegateInfo } from './CallLogDelegateInfo'; import type { CallLogRecordMessage } from './CallLogRecordMessage'; +import type { CallLogRecordingInfo } from './CallLogRecordingInfo'; +import type { ExtensionInfoCallLog } from './ExtensionInfoCallLog'; export interface CallLogRecordLegInfo { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/CallLogSync.ts b/packages/ringcentral-mock/src/platform/interfaces/CallLogSync.ts index e04c6500f6..0d757d8c71 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CallLogSync.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CallLogSync.ts @@ -1,5 +1,5 @@ -import type { UserCallLogRecord } from './UserCallLogRecord'; import type { SyncInfoCallLog } from './SyncInfoCallLog'; +import type { UserCallLogRecord } from './UserCallLogRecord'; export interface CallLogSync { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/CallParty.ts b/packages/ringcentral-mock/src/platform/interfaces/CallParty.ts index deb3c8c2a2..a9662f7041 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CallParty.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CallParty.ts @@ -1,7 +1,7 @@ import type { CallStatusInfo } from './CallStatusInfo'; +import type { OwnerInfo } from './OwnerInfo'; import type { ParkInfo } from './ParkInfo'; import type { PartyInfo } from './PartyInfo'; -import type { OwnerInfo } from './OwnerInfo'; import type { RecordingInfo } from './RecordingInfo'; // Information on a party of a call session diff --git a/packages/ringcentral-mock/src/platform/interfaces/CallRecordingExtensions.ts b/packages/ringcentral-mock/src/platform/interfaces/CallRecordingExtensions.ts index 35e32927c8..6fba934dae 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CallRecordingExtensions.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CallRecordingExtensions.ts @@ -1,6 +1,6 @@ -import type { CallRecordingExtensionInfo } from './CallRecordingExtensionInfo'; import type { CallHandlingNavigationInfo } from './CallHandlingNavigationInfo'; import type { CallHandlingPagingInfo } from './CallHandlingPagingInfo'; +import type { CallRecordingExtensionInfo } from './CallRecordingExtensionInfo'; export interface CallRecordingExtensions { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/CallRecordingSettingsResource.ts b/packages/ringcentral-mock/src/platform/interfaces/CallRecordingSettingsResource.ts index 005b0ec4d5..22e7c4b0f6 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CallRecordingSettingsResource.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CallRecordingSettingsResource.ts @@ -1,6 +1,6 @@ -import type { OnDemandResource } from './OnDemandResource'; import type { AutomaticRecordingResource } from './AutomaticRecordingResource'; import type { GreetingResource } from './GreetingResource'; +import type { OnDemandResource } from './OnDemandResource'; export interface CallRecordingSettingsResource { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/CallSessionObject.ts b/packages/ringcentral-mock/src/platform/interfaces/CallSessionObject.ts index 8ee7afcd78..c41d21d92d 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CallSessionObject.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CallSessionObject.ts @@ -1,5 +1,5 @@ -import type { OriginInfo } from './OriginInfo'; import type { CallParty } from './CallParty'; +import type { OriginInfo } from './OriginInfo'; // Call session information export interface CallSessionObject { diff --git a/packages/ringcentral-mock/src/platform/interfaces/CallSessionStatusInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/CallSessionStatusInfo.ts index 4eca82c2d6..af97f39143 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CallSessionStatusInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CallSessionStatusInfo.ts @@ -1,5 +1,5 @@ -import type { PeerInfo } from './PeerInfo'; import type { MobilePickupData } from './MobilePickupData'; +import type { PeerInfo } from './PeerInfo'; export interface CallSessionStatusInfo { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/CallerIdByDevice.ts b/packages/ringcentral-mock/src/platform/interfaces/CallerIdByDevice.ts index 8bbee05ae4..2e481ac984 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CallerIdByDevice.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CallerIdByDevice.ts @@ -1,5 +1,5 @@ -import type { CallerIdDeviceInfo } from './CallerIdDeviceInfo'; import type { CallerIdByDeviceInfo } from './CallerIdByDeviceInfo'; +import type { CallerIdDeviceInfo } from './CallerIdDeviceInfo'; // Caller ID settings by device export interface CallerIdByDevice { diff --git a/packages/ringcentral-mock/src/platform/interfaces/CallerIdByDeviceRequest.ts b/packages/ringcentral-mock/src/platform/interfaces/CallerIdByDeviceRequest.ts index 0003f38a1e..c1d4205a99 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CallerIdByDeviceRequest.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CallerIdByDeviceRequest.ts @@ -1,5 +1,5 @@ -import type { CallerIdDeviceInfoRequest } from './CallerIdDeviceInfoRequest'; import type { CallerIdByDeviceInfoRequest } from './CallerIdByDeviceInfoRequest'; +import type { CallerIdDeviceInfoRequest } from './CallerIdDeviceInfoRequest'; // Caller ID settings by device export interface CallerIdByDeviceRequest { diff --git a/packages/ringcentral-mock/src/platform/interfaces/CompanyActiveCallsResponse.ts b/packages/ringcentral-mock/src/platform/interfaces/CompanyActiveCallsResponse.ts index a0fd09df14..caf1ff0bd0 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CompanyActiveCallsResponse.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CompanyActiveCallsResponse.ts @@ -1,6 +1,6 @@ -import type { CompanyCallLogRecord } from './CompanyCallLogRecord'; import type { CallLogNavigationInfo } from './CallLogNavigationInfo'; import type { CallLogPagingInfo } from './CallLogPagingInfo'; +import type { CompanyCallLogRecord } from './CompanyCallLogRecord'; export interface CompanyActiveCallsResponse { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/CompanyAnsweringRuleInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/CompanyAnsweringRuleInfo.ts index 288582edc9..5455064b9c 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CompanyAnsweringRuleInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CompanyAnsweringRuleInfo.ts @@ -1,7 +1,7 @@ -import type { CompanyAnsweringRuleCallersInfoRequest } from './CompanyAnsweringRuleCallersInfoRequest'; import type { CompanyAnsweringRuleCalledNumberInfoRequest } from './CompanyAnsweringRuleCalledNumberInfoRequest'; -import type { CompanyAnsweringRuleScheduleInfo } from './CompanyAnsweringRuleScheduleInfo'; +import type { CompanyAnsweringRuleCallersInfoRequest } from './CompanyAnsweringRuleCallersInfoRequest'; import type { CompanyAnsweringRuleExtensionInfoRequest } from './CompanyAnsweringRuleExtensionInfoRequest'; +import type { CompanyAnsweringRuleScheduleInfo } from './CompanyAnsweringRuleScheduleInfo'; import type { GreetingInfo } from './GreetingInfo'; export interface CompanyAnsweringRuleInfo { diff --git a/packages/ringcentral-mock/src/platform/interfaces/CompanyAnsweringRuleList.ts b/packages/ringcentral-mock/src/platform/interfaces/CompanyAnsweringRuleList.ts index 109af6a017..f27886c7eb 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CompanyAnsweringRuleList.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CompanyAnsweringRuleList.ts @@ -1,6 +1,6 @@ -import type { ListCompanyAnsweringRuleInfo } from './ListCompanyAnsweringRuleInfo'; -import type { CallHandlingPagingInfo } from './CallHandlingPagingInfo'; import type { CallHandlingNavigationInfo } from './CallHandlingNavigationInfo'; +import type { CallHandlingPagingInfo } from './CallHandlingPagingInfo'; +import type { ListCompanyAnsweringRuleInfo } from './ListCompanyAnsweringRuleInfo'; export interface CompanyAnsweringRuleList { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/CompanyAnsweringRuleRequest.ts b/packages/ringcentral-mock/src/platform/interfaces/CompanyAnsweringRuleRequest.ts index 0857f6cbb6..59b6617f64 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CompanyAnsweringRuleRequest.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CompanyAnsweringRuleRequest.ts @@ -1,5 +1,5 @@ -import type { CompanyAnsweringRuleCallersInfoRequest } from './CompanyAnsweringRuleCallersInfoRequest'; import type { CompanyAnsweringRuleCalledNumberInfo } from './CompanyAnsweringRuleCalledNumberInfo'; +import type { CompanyAnsweringRuleCallersInfoRequest } from './CompanyAnsweringRuleCallersInfoRequest'; import type { CompanyAnsweringRuleScheduleInfoRequest } from './CompanyAnsweringRuleScheduleInfoRequest'; import type { GreetingInfo } from './GreetingInfo'; diff --git a/packages/ringcentral-mock/src/platform/interfaces/CompanyAnsweringRuleUpdate.ts b/packages/ringcentral-mock/src/platform/interfaces/CompanyAnsweringRuleUpdate.ts index 7c7a22750a..8d63bc0081 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CompanyAnsweringRuleUpdate.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CompanyAnsweringRuleUpdate.ts @@ -1,5 +1,5 @@ -import type { CompanyAnsweringRuleCallersInfoRequest } from './CompanyAnsweringRuleCallersInfoRequest'; import type { CompanyAnsweringRuleCalledNumberInfo } from './CompanyAnsweringRuleCalledNumberInfo'; +import type { CompanyAnsweringRuleCallersInfoRequest } from './CompanyAnsweringRuleCallersInfoRequest'; import type { CompanyAnsweringRuleScheduleInfoRequest } from './CompanyAnsweringRuleScheduleInfoRequest'; import type { GreetingInfo } from './GreetingInfo'; diff --git a/packages/ringcentral-mock/src/platform/interfaces/CompanyCallLogRecord.ts b/packages/ringcentral-mock/src/platform/interfaces/CompanyCallLogRecord.ts index 1efe02eead..bf232ef100 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CompanyCallLogRecord.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CompanyCallLogRecord.ts @@ -1,10 +1,10 @@ -import type { ExtensionInfoCallLog } from './ExtensionInfoCallLog'; +import type { BillingInfo } from './BillingInfo'; import type { CallLogCallerInfo } from './CallLogCallerInfo'; -import type { CallLogRecordMessage } from './CallLogRecordMessage'; import type { CallLogDelegateInfo } from './CallLogDelegateInfo'; -import type { CallLogRecordingInfo } from './CallLogRecordingInfo'; import type { CallLogRecordLegInfo } from './CallLogRecordLegInfo'; -import type { BillingInfo } from './BillingInfo'; +import type { CallLogRecordMessage } from './CallLogRecordMessage'; +import type { CallLogRecordingInfo } from './CallLogRecordingInfo'; +import type { ExtensionInfoCallLog } from './ExtensionInfoCallLog'; export interface CompanyCallLogRecord { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/CompanyDirectoryEventBody.ts b/packages/ringcentral-mock/src/platform/interfaces/CompanyDirectoryEventBody.ts index d22691a8dc..5c3a01e421 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CompanyDirectoryEventBody.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CompanyDirectoryEventBody.ts @@ -1,7 +1,7 @@ import type { CompanyDirectoryAccountInfo } from './CompanyDirectoryAccountInfo'; import type { CompanyDirectoryPhoneNumberInfo } from './CompanyDirectoryPhoneNumberInfo'; -import type { ContactDirectorySiteInfo } from './ContactDirectorySiteInfo'; import type { CompanyDirectoryProfileImageInfo } from './CompanyDirectoryProfileImageInfo'; +import type { ContactDirectorySiteInfo } from './ContactDirectorySiteInfo'; // Notification payload body export interface CompanyDirectoryEventBody { diff --git a/packages/ringcentral-mock/src/platform/interfaces/CompanyPhoneNumberInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/CompanyPhoneNumberInfo.ts index ee44b3f28e..66a918e00a 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CompanyPhoneNumberInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CompanyPhoneNumberInfo.ts @@ -1,7 +1,7 @@ +import type { ContactCenterProvider } from './ContactCenterProvider'; import type { CountryInfo } from './CountryInfo'; import type { ExtensionInfo } from './ExtensionInfo'; import type { TemporaryNumberInfo } from './TemporaryNumberInfo'; -import type { ContactCenterProvider } from './ContactCenterProvider'; export interface CompanyPhoneNumberInfo { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/ContactDirectoryEvent.ts b/packages/ringcentral-mock/src/platform/interfaces/ContactDirectoryEvent.ts index b085cdc990..fc222195a1 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/ContactDirectoryEvent.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/ContactDirectoryEvent.ts @@ -1,7 +1,7 @@ import type { CompanyDirectoryAccountInfo } from './CompanyDirectoryAccountInfo'; import type { CompanyDirectoryPhoneNumberInfo } from './CompanyDirectoryPhoneNumberInfo'; -import type { ContactDirectorySiteInfo } from './ContactDirectorySiteInfo'; import type { CompanyDirectoryProfileImageInfo } from './CompanyDirectoryProfileImageInfo'; +import type { ContactDirectorySiteInfo } from './ContactDirectorySiteInfo'; export interface ContactDirectoryEvent { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/ContactList.ts b/packages/ringcentral-mock/src/platform/interfaces/ContactList.ts index a171f55dae..4aff92b562 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/ContactList.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/ContactList.ts @@ -1,7 +1,7 @@ import type { PersonalContactResource } from './PersonalContactResource'; +import type { UserContactsGroupsInfo } from './UserContactsGroupsInfo'; import type { UserContactsNavigationInfo } from './UserContactsNavigationInfo'; import type { UserContactsPagingInfo } from './UserContactsPagingInfo'; -import type { UserContactsGroupsInfo } from './UserContactsGroupsInfo'; export interface ContactList { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/ContactResource.ts b/packages/ringcentral-mock/src/platform/interfaces/ContactResource.ts index d5af30282c..64d02538e8 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/ContactResource.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/ContactResource.ts @@ -1,7 +1,7 @@ -import type { AccountResource } from './AccountResource'; -import type { PhoneNumberResource } from './PhoneNumberResource'; import type { AccountDirectoryProfileImageResource } from './AccountDirectoryProfileImageResource'; +import type { AccountResource } from './AccountResource'; import type { BusinessSiteResource } from './BusinessSiteResource'; +import type { PhoneNumberResource } from './PhoneNumberResource'; import type { UserCustomFieldResource } from './UserCustomFieldResource'; export interface ContactResource { diff --git a/packages/ringcentral-mock/src/platform/interfaces/CreateAnsweringRuleRequest.ts b/packages/ringcentral-mock/src/platform/interfaces/CreateAnsweringRuleRequest.ts index a1e5216899..897f087d76 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CreateAnsweringRuleRequest.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CreateAnsweringRuleRequest.ts @@ -1,13 +1,13 @@ -import type { CallersInfoRequest } from './CallersInfoRequest'; import type { CalledNumberInfo } from './CalledNumberInfo'; -import type { ScheduleInfo } from './ScheduleInfo'; +import type { CallersInfoRequest } from './CallersInfoRequest'; import type { ForwardingInfo } from './ForwardingInfo'; -import type { UnconditionalForwardingInfo } from './UnconditionalForwardingInfo'; +import type { GreetingInfo } from './GreetingInfo'; +import type { MissedCallInfo } from './MissedCallInfo'; import type { QueueInfo } from './QueueInfo'; +import type { ScheduleInfo } from './ScheduleInfo'; import type { TransferredExtensionInfo } from './TransferredExtensionInfo'; +import type { UnconditionalForwardingInfo } from './UnconditionalForwardingInfo'; import type { VoicemailInfo } from './VoicemailInfo'; -import type { MissedCallInfo } from './MissedCallInfo'; -import type { GreetingInfo } from './GreetingInfo'; export interface CreateAnsweringRuleRequest { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/CreateFaxMessageRequest.ts b/packages/ringcentral-mock/src/platform/interfaces/CreateFaxMessageRequest.ts index 632fc0b9a7..314daf242d 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CreateFaxMessageRequest.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CreateFaxMessageRequest.ts @@ -1,5 +1,5 @@ -import type { MessageStoreCalleeInfoRequest } from './MessageStoreCalleeInfoRequest'; import type { Attachment } from './Attachment'; +import type { MessageStoreCalleeInfoRequest } from './MessageStoreCalleeInfoRequest'; // Request body for operation createFaxMessage export interface CreateFaxMessageRequest { diff --git a/packages/ringcentral-mock/src/platform/interfaces/CreateMMSMessage.ts b/packages/ringcentral-mock/src/platform/interfaces/CreateMMSMessage.ts index dc9b7e7b37..d0578f07e0 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CreateMMSMessage.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CreateMMSMessage.ts @@ -1,6 +1,6 @@ -import type { MessageStoreCallerInfoRequest } from './MessageStoreCallerInfoRequest'; -import type { MessageCountryInfo } from './MessageCountryInfo'; import type { Attachment } from './Attachment'; +import type { MessageCountryInfo } from './MessageCountryInfo'; +import type { MessageStoreCallerInfoRequest } from './MessageStoreCallerInfoRequest'; export interface CreateMMSMessage { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/CreateNetworkRequest.ts b/packages/ringcentral-mock/src/platform/interfaces/CreateNetworkRequest.ts index e5a116ec99..2ae5087806 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CreateNetworkRequest.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CreateNetworkRequest.ts @@ -1,7 +1,7 @@ import type { AutomaticLocationUpdatesSiteInfo } from './AutomaticLocationUpdatesSiteInfo'; -import type { PublicIpRangeInfo } from './PublicIpRangeInfo'; -import type { PrivateIpRangeInfoRequest } from './PrivateIpRangeInfoRequest'; import type { ERLLocationInfo } from './ERLLocationInfo'; +import type { PrivateIpRangeInfoRequest } from './PrivateIpRangeInfoRequest'; +import type { PublicIpRangeInfo } from './PublicIpRangeInfo'; export interface CreateNetworkRequest { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/CreateSMSMessage.ts b/packages/ringcentral-mock/src/platform/interfaces/CreateSMSMessage.ts index 9dbae0efed..feb02ec52a 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CreateSMSMessage.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CreateSMSMessage.ts @@ -1,5 +1,5 @@ -import type { MessageStoreCallerInfoRequest } from './MessageStoreCallerInfoRequest'; import type { MessageCountryInfo } from './MessageCountryInfo'; +import type { MessageStoreCallerInfoRequest } from './MessageStoreCallerInfoRequest'; export interface CreateSMSMessage { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/CreateSipRegistrationResponse.ts b/packages/ringcentral-mock/src/platform/interfaces/CreateSipRegistrationResponse.ts index f887ef73be..b2e025e90d 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CreateSipRegistrationResponse.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CreateSipRegistrationResponse.ts @@ -1,6 +1,6 @@ -import type { SipRegistrationDeviceInfo } from './SipRegistrationDeviceInfo'; -import type { SIPInfoResponse } from './SIPInfoResponse'; import type { SIPFlagsResponse } from './SIPFlagsResponse'; +import type { SIPInfoResponse } from './SIPInfoResponse'; +import type { SipRegistrationDeviceInfo } from './SipRegistrationDeviceInfo'; export interface CreateSipRegistrationResponse { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/CreateSwitchInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/CreateSwitchInfo.ts index 12384a19be..a84e410f53 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CreateSwitchInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CreateSwitchInfo.ts @@ -1,6 +1,6 @@ -import type { SwitchSiteInfo } from './SwitchSiteInfo'; -import type { LocationUpdatesEmergencyAddressInfoRequest } from './LocationUpdatesEmergencyAddressInfoRequest'; import type { ERLLocationInfo } from './ERLLocationInfo'; +import type { LocationUpdatesEmergencyAddressInfoRequest } from './LocationUpdatesEmergencyAddressInfoRequest'; +import type { SwitchSiteInfo } from './SwitchSiteInfo'; export interface CreateSwitchInfo { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/CreateUser.ts b/packages/ringcentral-mock/src/platform/interfaces/CreateUser.ts index f3d3962914..c0fab73039 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CreateUser.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CreateUser.ts @@ -1,9 +1,9 @@ -import type { UserAddress } from './UserAddress'; import type { Email } from './Email'; +import type { EnterpriseUser } from './EnterpriseUser'; import type { Name } from './Name'; import type { PhoneNumber } from './PhoneNumber'; import type { Photo } from './Photo'; -import type { EnterpriseUser } from './EnterpriseUser'; +import type { UserAddress } from './UserAddress'; export interface CreateUser { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/CreateWirelessPoint.ts b/packages/ringcentral-mock/src/platform/interfaces/CreateWirelessPoint.ts index 564bc07397..80fa84bbe7 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CreateWirelessPoint.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CreateWirelessPoint.ts @@ -1,6 +1,6 @@ import type { AutomaticLocationUpdatesSiteInfo } from './AutomaticLocationUpdatesSiteInfo'; -import type { LocationUpdatesEmergencyAddressInfoRequest } from './LocationUpdatesEmergencyAddressInfoRequest'; import type { ERLLocationInfo } from './ERLLocationInfo'; +import type { LocationUpdatesEmergencyAddressInfoRequest } from './LocationUpdatesEmergencyAddressInfoRequest'; export interface CreateWirelessPoint { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/CustomAnsweringRuleInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/CustomAnsweringRuleInfo.ts index ba45764559..c564a650a6 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CustomAnsweringRuleInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CustomAnsweringRuleInfo.ts @@ -1,13 +1,13 @@ -import type { ScheduleInfo } from './ScheduleInfo'; import type { CalledNumberInfo } from './CalledNumberInfo'; import type { CallersInfo } from './CallersInfo'; import type { ForwardingInfo } from './ForwardingInfo'; -import type { UnconditionalForwardingInfo } from './UnconditionalForwardingInfo'; +import type { GreetingInfo } from './GreetingInfo'; import type { QueueInfo } from './QueueInfo'; +import type { ScheduleInfo } from './ScheduleInfo'; +import type { SharedLinesInfo } from './SharedLinesInfo'; import type { TransferredExtensionInfo } from './TransferredExtensionInfo'; +import type { UnconditionalForwardingInfo } from './UnconditionalForwardingInfo'; import type { VoicemailInfo } from './VoicemailInfo'; -import type { GreetingInfo } from './GreetingInfo'; -import type { SharedLinesInfo } from './SharedLinesInfo'; export interface CustomAnsweringRuleInfo { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/CustomCompanyGreetingInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/CustomCompanyGreetingInfo.ts index 422c7390a4..bfd7823156 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/CustomCompanyGreetingInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/CustomCompanyGreetingInfo.ts @@ -1,5 +1,5 @@ -import type { CustomGreetingAnsweringRuleInfo } from './CustomGreetingAnsweringRuleInfo'; import type { CustomCompanyGreetingLanguageInfo } from './CustomCompanyGreetingLanguageInfo'; +import type { CustomGreetingAnsweringRuleInfo } from './CustomGreetingAnsweringRuleInfo'; export interface CustomCompanyGreetingInfo { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/DataExportTask.ts b/packages/ringcentral-mock/src/platform/interfaces/DataExportTask.ts index d96e929191..67a9a4f55f 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/DataExportTask.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/DataExportTask.ts @@ -1,6 +1,6 @@ import type { CreatorInfo } from './CreatorInfo'; -import type { SpecificInfo } from './SpecificInfo'; import type { ExportTaskResultInfo } from './ExportTaskResultInfo'; +import type { SpecificInfo } from './SpecificInfo'; export interface DataExportTask { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/DictionaryGreetingList.ts b/packages/ringcentral-mock/src/platform/interfaces/DictionaryGreetingList.ts index 4e4acf248d..95ca395e18 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/DictionaryGreetingList.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/DictionaryGreetingList.ts @@ -1,6 +1,6 @@ -import type { DictionaryGreetingInfo } from './DictionaryGreetingInfo'; import type { CallHandlingNavigationInfo } from './CallHandlingNavigationInfo'; import type { CallHandlingPagingInfo } from './CallHandlingPagingInfo'; +import type { DictionaryGreetingInfo } from './DictionaryGreetingInfo'; export interface DictionaryGreetingList { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/EmergencyLocationInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/EmergencyLocationInfo.ts index 9800a75792..6e95ea9b14 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/EmergencyLocationInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/EmergencyLocationInfo.ts @@ -1,6 +1,6 @@ import type { EmergencyLocationAddressInfo } from './EmergencyLocationAddressInfo'; -import type { ShortSiteInfo } from './ShortSiteInfo'; import type { LocationOwnerInfo } from './LocationOwnerInfo'; +import type { ShortSiteInfo } from './ShortSiteInfo'; // Company emergency response location details export interface EmergencyLocationInfo { diff --git a/packages/ringcentral-mock/src/platform/interfaces/ExtensionBulkUpdateInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/ExtensionBulkUpdateInfo.ts index 26c3592469..5bdf086cdd 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/ExtensionBulkUpdateInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/ExtensionBulkUpdateInfo.ts @@ -1,11 +1,11 @@ -import type { ExtensionStatusInfo } from './ExtensionStatusInfo'; -import type { ContactInfoUpdateRequest } from './ContactInfoUpdateRequest'; -import type { ExtensionRegionalSettingRequest } from './ExtensionRegionalSettingRequest'; +import type { AutomaticLocationUpdatesSiteInfo } from './AutomaticLocationUpdatesSiteInfo'; import type { CallQueueInfoRequest } from './CallQueueInfoRequest'; -import type { UserTransitionInfo } from './UserTransitionInfo'; +import type { ContactInfoUpdateRequest } from './ContactInfoUpdateRequest'; import type { CustomFieldInfo } from './CustomFieldInfo'; -import type { AutomaticLocationUpdatesSiteInfo } from './AutomaticLocationUpdatesSiteInfo'; +import type { ExtensionRegionalSettingRequest } from './ExtensionRegionalSettingRequest'; +import type { ExtensionStatusInfo } from './ExtensionStatusInfo'; import type { ReferenceInfo } from './ReferenceInfo'; +import type { UserTransitionInfo } from './UserTransitionInfo'; export interface ExtensionBulkUpdateInfo { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/ExtensionBulkUpdateTaskResult.ts b/packages/ringcentral-mock/src/platform/interfaces/ExtensionBulkUpdateTaskResult.ts index 7c888d8e0b..7655880bde 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/ExtensionBulkUpdateTaskResult.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/ExtensionBulkUpdateTaskResult.ts @@ -1,5 +1,5 @@ -import type { ExtensionUpdateShortResult } from './ExtensionUpdateShortResult'; import type { ErrorEntity } from './ErrorEntity'; +import type { ExtensionUpdateShortResult } from './ExtensionUpdateShortResult'; // Result record on multiple extension update task export interface ExtensionBulkUpdateTaskResult { diff --git a/packages/ringcentral-mock/src/platform/interfaces/ExtensionCreationRequest.ts b/packages/ringcentral-mock/src/platform/interfaces/ExtensionCreationRequest.ts index 1eabd8f8a1..52517b74b7 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/ExtensionCreationRequest.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/ExtensionCreationRequest.ts @@ -1,9 +1,9 @@ import type { ContactInfoCreationRequest } from './ContactInfoCreationRequest'; import type { CustomFieldInfo } from './CustomFieldInfo'; +import type { ExtensionStatusInfo } from './ExtensionStatusInfo'; import type { ReferenceInfo } from './ReferenceInfo'; import type { RegionalSettings } from './RegionalSettings'; import type { SiteInfo } from './SiteInfo'; -import type { ExtensionStatusInfo } from './ExtensionStatusInfo'; export interface ExtensionCreationRequest { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/ExtensionCreationResponse.ts b/packages/ringcentral-mock/src/platform/interfaces/ExtensionCreationResponse.ts index 610f85c625..7c8eb45e03 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/ExtensionCreationResponse.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/ExtensionCreationResponse.ts @@ -1,12 +1,12 @@ +import type { AutomaticLocationUpdatesSiteInfo } from './AutomaticLocationUpdatesSiteInfo'; import type { ContactInfo } from './ContactInfo'; import type { CustomFieldInfo } from './CustomFieldInfo'; import type { ExtensionPermissions } from './ExtensionPermissions'; +import type { ExtensionServiceFeatureInfo } from './ExtensionServiceFeatureInfo'; +import type { ExtensionStatusInfo } from './ExtensionStatusInfo'; import type { ProfileImageInfo } from './ProfileImageInfo'; import type { ReferenceInfo } from './ReferenceInfo'; import type { RegionalSettings } from './RegionalSettings'; -import type { ExtensionServiceFeatureInfo } from './ExtensionServiceFeatureInfo'; -import type { AutomaticLocationUpdatesSiteInfo } from './AutomaticLocationUpdatesSiteInfo'; -import type { ExtensionStatusInfo } from './ExtensionStatusInfo'; export interface ExtensionCreationResponse { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/ExtensionDeviceResponse.ts b/packages/ringcentral-mock/src/platform/interfaces/ExtensionDeviceResponse.ts index 665c0659dc..e00b8c750b 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/ExtensionDeviceResponse.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/ExtensionDeviceResponse.ts @@ -1,10 +1,10 @@ -import type { ModelInfo } from './ModelInfo'; -import type { ExtensionInfoIntId } from './ExtensionInfoIntId'; -import type { EmergencyServiceAddressResource } from './EmergencyServiceAddressResource'; import type { DeviceEmergencyInfo } from './DeviceEmergencyInfo'; +import type { DeviceSiteInfo } from './DeviceSiteInfo'; +import type { EmergencyServiceAddressResource } from './EmergencyServiceAddressResource'; +import type { ExtensionInfoIntId } from './ExtensionInfoIntId'; +import type { ModelInfo } from './ModelInfo'; import type { PhoneLinesInfo } from './PhoneLinesInfo'; import type { ShippingInfo } from './ShippingInfo'; -import type { DeviceSiteInfo } from './DeviceSiteInfo'; export interface ExtensionDeviceResponse { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/ExtensionRegionalSettingRequest.ts b/packages/ringcentral-mock/src/platform/interfaces/ExtensionRegionalSettingRequest.ts index eff6779426..fee5bf1470 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/ExtensionRegionalSettingRequest.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/ExtensionRegionalSettingRequest.ts @@ -1,8 +1,8 @@ import type { ExtensionCountryInfoRequest } from './ExtensionCountryInfoRequest'; -import type { ExtensionTimezoneInfoRequest } from './ExtensionTimezoneInfoRequest'; -import type { ExtensionLanguageInfoRequest } from './ExtensionLanguageInfoRequest'; -import type { ExtensionGreetingLanguageInfoRequest } from './ExtensionGreetingLanguageInfoRequest'; import type { ExtensionFormattingLocaleInfoRequest } from './ExtensionFormattingLocaleInfoRequest'; +import type { ExtensionGreetingLanguageInfoRequest } from './ExtensionGreetingLanguageInfoRequest'; +import type { ExtensionLanguageInfoRequest } from './ExtensionLanguageInfoRequest'; +import type { ExtensionTimezoneInfoRequest } from './ExtensionTimezoneInfoRequest'; export interface ExtensionRegionalSettingRequest { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/ExtensionUpdateRequest.ts b/packages/ringcentral-mock/src/platform/interfaces/ExtensionUpdateRequest.ts index d46460a4b5..cf2cab5def 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/ExtensionUpdateRequest.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/ExtensionUpdateRequest.ts @@ -1,11 +1,11 @@ -import type { ExtensionStatusInfo } from './ExtensionStatusInfo'; -import type { ContactInfoUpdateRequest } from './ContactInfoUpdateRequest'; -import type { ExtensionRegionalSettingRequest } from './ExtensionRegionalSettingRequest'; +import type { AutomaticLocationUpdatesSiteInfoRequest } from './AutomaticLocationUpdatesSiteInfoRequest'; import type { CallQueueInfoRequest } from './CallQueueInfoRequest'; -import type { UserTransitionInfo } from './UserTransitionInfo'; +import type { ContactInfoUpdateRequest } from './ContactInfoUpdateRequest'; import type { CustomFieldInfo } from './CustomFieldInfo'; -import type { AutomaticLocationUpdatesSiteInfoRequest } from './AutomaticLocationUpdatesSiteInfoRequest'; +import type { ExtensionRegionalSettingRequest } from './ExtensionRegionalSettingRequest'; +import type { ExtensionStatusInfo } from './ExtensionStatusInfo'; import type { ReferenceInfo } from './ReferenceInfo'; +import type { UserTransitionInfo } from './UserTransitionInfo'; export interface ExtensionUpdateRequest { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/FaxMessageEventBody.ts b/packages/ringcentral-mock/src/platform/interfaces/FaxMessageEventBody.ts index adee18e642..c1d95ec224 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/FaxMessageEventBody.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/FaxMessageEventBody.ts @@ -1,6 +1,6 @@ +import type { FaxMessageAttachment } from './FaxMessageAttachment'; import type { NotificationRecipientInfo } from './NotificationRecipientInfo'; import type { SenderInfo } from './SenderInfo'; -import type { FaxMessageAttachment } from './FaxMessageAttachment'; // Notification payload body export interface FaxMessageEventBody { diff --git a/packages/ringcentral-mock/src/platform/interfaces/GetAccountInfoResponse.ts b/packages/ringcentral-mock/src/platform/interfaces/GetAccountInfoResponse.ts index 8284c5e64e..686626f701 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/GetAccountInfoResponse.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/GetAccountInfoResponse.ts @@ -1,9 +1,9 @@ +import type { AccountLimits } from './AccountLimits'; import type { AccountOperatorInfo } from './AccountOperatorInfo'; +import type { AccountRegionalSettings } from './AccountRegionalSettings'; +import type { AccountStatusInfo } from './AccountStatusInfo'; import type { ServiceInfo } from './ServiceInfo'; import type { SignupInfoResource } from './SignupInfoResource'; -import type { AccountStatusInfo } from './AccountStatusInfo'; -import type { AccountRegionalSettings } from './AccountRegionalSettings'; -import type { AccountLimits } from './AccountLimits'; export interface GetAccountInfoResponse { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/GetDeviceInfoResponse.ts b/packages/ringcentral-mock/src/platform/interfaces/GetDeviceInfoResponse.ts index 8a6cf2087b..4e339b35fa 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/GetDeviceInfoResponse.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/GetDeviceInfoResponse.ts @@ -1,11 +1,11 @@ -import type { ModelInfo } from './ModelInfo'; -import type { ExtensionInfoIntId } from './ExtensionInfoIntId'; +import type { BillingStatementInfo } from './BillingStatementInfo'; import type { DeviceEmergencyInfo } from './DeviceEmergencyInfo'; +import type { DeviceSiteInfo } from './DeviceSiteInfo'; import type { EmergencyServiceAddressResource } from './EmergencyServiceAddressResource'; +import type { ExtensionInfoIntId } from './ExtensionInfoIntId'; +import type { ModelInfo } from './ModelInfo'; import type { PhoneLinesInfo } from './PhoneLinesInfo'; import type { ShippingInfo } from './ShippingInfo'; -import type { DeviceSiteInfo } from './DeviceSiteInfo'; -import type { BillingStatementInfo } from './BillingStatementInfo'; export interface GetDeviceInfoResponse { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/GetExtensionDevicesResponse.ts b/packages/ringcentral-mock/src/platform/interfaces/GetExtensionDevicesResponse.ts index 073bda67ee..af811864ea 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/GetExtensionDevicesResponse.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/GetExtensionDevicesResponse.ts @@ -1,6 +1,6 @@ -import type { ExtensionDeviceResponse } from './ExtensionDeviceResponse'; import type { DeviceProvisioningNavigationInfo } from './DeviceProvisioningNavigationInfo'; import type { DeviceProvisioningPagingInfo } from './DeviceProvisioningPagingInfo'; +import type { ExtensionDeviceResponse } from './ExtensionDeviceResponse'; export interface GetExtensionDevicesResponse { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/GetExtensionForwardingNumberListResponse.ts b/packages/ringcentral-mock/src/platform/interfaces/GetExtensionForwardingNumberListResponse.ts index 3ad8850ae6..b806cb6b8e 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/GetExtensionForwardingNumberListResponse.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/GetExtensionForwardingNumberListResponse.ts @@ -1,6 +1,6 @@ -import type { ForwardingNumberInfo } from './ForwardingNumberInfo'; import type { CallHandlingNavigationInfo } from './CallHandlingNavigationInfo'; import type { CallHandlingPagingInfo } from './CallHandlingPagingInfo'; +import type { ForwardingNumberInfo } from './ForwardingNumberInfo'; export interface GetExtensionForwardingNumberListResponse { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/GetExtensionInfoResponse.ts b/packages/ringcentral-mock/src/platform/interfaces/GetExtensionInfoResponse.ts index ee61b2c93e..13717c5ba1 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/GetExtensionInfoResponse.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/GetExtensionInfoResponse.ts @@ -1,16 +1,16 @@ -import type { GetExtensionAccountInfo } from './GetExtensionAccountInfo'; +import type { AutomaticLocationUpdatesSiteInfo } from './AutomaticLocationUpdatesSiteInfo'; +import type { CallQueueExtensionInfo } from './CallQueueExtensionInfo'; import type { ContactInfo } from './ContactInfo'; import type { CustomFieldInfo } from './CustomFieldInfo'; import type { DepartmentInfo } from './DepartmentInfo'; import type { ExtensionPermissions } from './ExtensionPermissions'; +import type { ExtensionServiceFeatureInfo } from './ExtensionServiceFeatureInfo'; +import type { ExtensionStatusInfo } from './ExtensionStatusInfo'; +import type { GetExtensionAccountInfo } from './GetExtensionAccountInfo'; import type { ProfileImageInfo } from './ProfileImageInfo'; import type { ReferenceInfo } from './ReferenceInfo'; -import type { Roles } from './Roles'; import type { RegionalSettings } from './RegionalSettings'; -import type { ExtensionServiceFeatureInfo } from './ExtensionServiceFeatureInfo'; -import type { ExtensionStatusInfo } from './ExtensionStatusInfo'; -import type { CallQueueExtensionInfo } from './CallQueueExtensionInfo'; -import type { AutomaticLocationUpdatesSiteInfo } from './AutomaticLocationUpdatesSiteInfo'; +import type { Roles } from './Roles'; export interface GetExtensionInfoResponse { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/GetExtensionListInfoResponse.ts b/packages/ringcentral-mock/src/platform/interfaces/GetExtensionListInfoResponse.ts index 15482cbff5..f7e15f6f01 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/GetExtensionListInfoResponse.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/GetExtensionListInfoResponse.ts @@ -1,8 +1,8 @@ +import type { AutomaticLocationUpdatesSiteInfo } from './AutomaticLocationUpdatesSiteInfo'; +import type { CallQueueExtensionInfo } from './CallQueueExtensionInfo'; import type { ContactInfo } from './ContactInfo'; import type { ExtensionPermissions } from './ExtensionPermissions'; import type { ProfileImageInfo } from './ProfileImageInfo'; -import type { CallQueueExtensionInfo } from './CallQueueExtensionInfo'; -import type { AutomaticLocationUpdatesSiteInfo } from './AutomaticLocationUpdatesSiteInfo'; export interface GetExtensionListInfoResponse { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/GetExtensionPhoneNumbersResponse.ts b/packages/ringcentral-mock/src/platform/interfaces/GetExtensionPhoneNumbersResponse.ts index c9df2731ec..d339ab3865 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/GetExtensionPhoneNumbersResponse.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/GetExtensionPhoneNumbersResponse.ts @@ -1,6 +1,6 @@ -import type { UserPhoneNumberInfo } from './UserPhoneNumberInfo'; import type { ProvisioningNavigationInfo } from './ProvisioningNavigationInfo'; import type { ProvisioningPagingInfo } from './ProvisioningPagingInfo'; +import type { UserPhoneNumberInfo } from './UserPhoneNumberInfo'; export interface GetExtensionPhoneNumbersResponse { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/GetInternalTextMessageInfoResponse.ts b/packages/ringcentral-mock/src/platform/interfaces/GetInternalTextMessageInfoResponse.ts index f3b3c51d4a..a47b238b54 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/GetInternalTextMessageInfoResponse.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/GetInternalTextMessageInfoResponse.ts @@ -1,5 +1,5 @@ -import type { MessageAttachmentInfo } from './MessageAttachmentInfo'; import type { ConversationInfo } from './ConversationInfo'; +import type { MessageAttachmentInfo } from './MessageAttachmentInfo'; import type { MessageStoreCallerInfoResponseFrom } from './MessageStoreCallerInfoResponseFrom'; import type { MessageStoreCallerInfoResponseTo } from './MessageStoreCallerInfoResponseTo'; diff --git a/packages/ringcentral-mock/src/platform/interfaces/GetMessageInfoResponse.ts b/packages/ringcentral-mock/src/platform/interfaces/GetMessageInfoResponse.ts index 8b46436e6a..4f84407ce1 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/GetMessageInfoResponse.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/GetMessageInfoResponse.ts @@ -1,5 +1,5 @@ -import type { MessageAttachmentInfo } from './MessageAttachmentInfo'; import type { ConversationInfo } from './ConversationInfo'; +import type { MessageAttachmentInfo } from './MessageAttachmentInfo'; import type { MessageStoreCallerInfoResponseFrom } from './MessageStoreCallerInfoResponseFrom'; import type { MessageStoreCallerInfoResponseTo } from './MessageStoreCallerInfoResponseTo'; diff --git a/packages/ringcentral-mock/src/platform/interfaces/GetPresenceInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/GetPresenceInfo.ts index 0e903bfdeb..e769d665dc 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/GetPresenceInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/GetPresenceInfo.ts @@ -1,5 +1,5 @@ -import type { GetPresenceExtensionInfo } from './GetPresenceExtensionInfo'; import type { ActiveCallInfo } from './ActiveCallInfo'; +import type { GetPresenceExtensionInfo } from './GetPresenceExtensionInfo'; export interface GetPresenceInfo { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/GetSMSMessageInfoResponse.ts b/packages/ringcentral-mock/src/platform/interfaces/GetSMSMessageInfoResponse.ts index 3f946ed065..f3436ad0c1 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/GetSMSMessageInfoResponse.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/GetSMSMessageInfoResponse.ts @@ -1,5 +1,5 @@ -import type { MessageAttachmentInfo } from './MessageAttachmentInfo'; import type { ConversationInfo } from './ConversationInfo'; +import type { MessageAttachmentInfo } from './MessageAttachmentInfo'; import type { MessageStoreCallerInfoResponseFrom } from './MessageStoreCallerInfoResponseFrom'; import type { MessageStoreCallerInfoResponseTo } from './MessageStoreCallerInfoResponseTo'; diff --git a/packages/ringcentral-mock/src/platform/interfaces/GetServiceInfoResponse.ts b/packages/ringcentral-mock/src/platform/interfaces/GetServiceInfoResponse.ts index b751d4edb0..4b5d7fa812 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/GetServiceInfoResponse.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/GetServiceInfoResponse.ts @@ -1,11 +1,11 @@ +import type { AccountLimits } from './AccountLimits'; +import type { BillingPlanInfo } from './BillingPlanInfo'; import type { BrandInfo } from './BrandInfo'; import type { ContractedCountryInfo } from './ContractedCountryInfo'; +import type { PackageInfo } from './PackageInfo'; +import type { ServiceFeatureInfo } from './ServiceFeatureInfo'; import type { ServicePlanInfo } from './ServicePlanInfo'; import type { TargetServicePlanInfo } from './TargetServicePlanInfo'; -import type { BillingPlanInfo } from './BillingPlanInfo'; -import type { ServiceFeatureInfo } from './ServiceFeatureInfo'; -import type { AccountLimits } from './AccountLimits'; -import type { PackageInfo } from './PackageInfo'; export interface GetServiceInfoResponse { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/GlipAdaptiveCardInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/GlipAdaptiveCardInfo.ts index 1b15fc5711..3d0fbd1285 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/GlipAdaptiveCardInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/GlipAdaptiveCardInfo.ts @@ -1,6 +1,6 @@ +import type { AdaptiveCardAction } from './AdaptiveCardAction'; import type { AdaptiveCardCreator } from './AdaptiveCardCreator'; import type { AdaptiveCardInfoRequest } from './AdaptiveCardInfoRequest'; -import type { AdaptiveCardAction } from './AdaptiveCardAction'; import type { AdaptiveCardSelectAction } from './AdaptiveCardSelectAction'; import type { BackgroundImage } from './BackgroundImage'; diff --git a/packages/ringcentral-mock/src/platform/interfaces/GlipAdaptiveCardRequest.ts b/packages/ringcentral-mock/src/platform/interfaces/GlipAdaptiveCardRequest.ts index 7a9d7f8b32..6e3d828c55 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/GlipAdaptiveCardRequest.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/GlipAdaptiveCardRequest.ts @@ -1,5 +1,5 @@ -import type { AdaptiveCardInfoRequest } from './AdaptiveCardInfoRequest'; import type { AdaptiveCardAction } from './AdaptiveCardAction'; +import type { AdaptiveCardInfoRequest } from './AdaptiveCardInfoRequest'; import type { AdaptiveCardSelectAction } from './AdaptiveCardSelectAction'; import type { BackgroundImage } from './BackgroundImage'; diff --git a/packages/ringcentral-mock/src/platform/interfaces/GlipCreateTask.ts b/packages/ringcentral-mock/src/platform/interfaces/GlipCreateTask.ts index 25dc69e353..1893f9cdbc 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/GlipCreateTask.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/GlipCreateTask.ts @@ -1,6 +1,6 @@ import type { AssigneeInfo } from './AssigneeInfo'; -import type { GlipTaskRecurrenceInfo } from './GlipTaskRecurrenceInfo'; import type { GlipAttachmentInfoRequest } from './GlipAttachmentInfoRequest'; +import type { GlipTaskRecurrenceInfo } from './GlipTaskRecurrenceInfo'; export interface GlipCreateTask { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/GlipNotesInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/GlipNotesInfo.ts index cd3d08b507..e4527b6cda 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/GlipNotesInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/GlipNotesInfo.ts @@ -1,5 +1,5 @@ -import type { GlipNoteInfo } from './GlipNoteInfo'; import type { GlipNavigationInfo } from './GlipNavigationInfo'; +import type { GlipNoteInfo } from './GlipNoteInfo'; export interface GlipNotesInfo { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/GlipPostInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/GlipPostInfo.ts index ddfc73fd7d..bcdfb12f1b 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/GlipPostInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/GlipPostInfo.ts @@ -1,5 +1,5 @@ -import type { GlipMessageAttachmentInfo } from './GlipMessageAttachmentInfo'; import type { GlipMentionsInfo } from './GlipMentionsInfo'; +import type { GlipMessageAttachmentInfo } from './GlipMessageAttachmentInfo'; export interface GlipPostInfo { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/GlipPostsList.ts b/packages/ringcentral-mock/src/platform/interfaces/GlipPostsList.ts index aa63b1c9d6..dd229476ae 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/GlipPostsList.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/GlipPostsList.ts @@ -1,5 +1,5 @@ -import type { GlipPostInfo } from './GlipPostInfo'; import type { GlipNavigationInfo } from './GlipNavigationInfo'; +import type { GlipPostInfo } from './GlipPostInfo'; export interface GlipPostsList { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/GlipTaskInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/GlipTaskInfo.ts index d0d9b8b838..ddde97dad8 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/GlipTaskInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/GlipTaskInfo.ts @@ -1,6 +1,6 @@ import type { GlipCreatorInfo } from './GlipCreatorInfo'; -import type { TaskAssigneeInfo } from './TaskAssigneeInfo'; import type { GlipTaskRecurrenceInfo } from './GlipTaskRecurrenceInfo'; +import type { TaskAssigneeInfo } from './TaskAssigneeInfo'; import type { TaskAttachment } from './TaskAttachment'; export interface GlipTaskInfo { diff --git a/packages/ringcentral-mock/src/platform/interfaces/GlipTeamsList.ts b/packages/ringcentral-mock/src/platform/interfaces/GlipTeamsList.ts index db1bfc0442..870d6c848d 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/GlipTeamsList.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/GlipTeamsList.ts @@ -1,5 +1,5 @@ -import type { GlipTeamInfo } from './GlipTeamInfo'; import type { GlipNavigationInfo } from './GlipNavigationInfo'; +import type { GlipTeamInfo } from './GlipTeamInfo'; export interface GlipTeamsList { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/GlipUnreadMessageCountEvent.ts b/packages/ringcentral-mock/src/platform/interfaces/GlipUnreadMessageCountEvent.ts index 68993d2ebd..f580dbb315 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/GlipUnreadMessageCountEvent.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/GlipUnreadMessageCountEvent.ts @@ -1,5 +1,5 @@ -import type { GlipAPNSInfo } from './GlipAPNSInfo'; import type { GCMInfo } from './GCMInfo'; +import type { GlipAPNSInfo } from './GlipAPNSInfo'; export interface GlipUnreadMessageCountEvent { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/GlipUpdateTask.ts b/packages/ringcentral-mock/src/platform/interfaces/GlipUpdateTask.ts index 53a8ce62c6..25da93bb8c 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/GlipUpdateTask.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/GlipUpdateTask.ts @@ -1,6 +1,6 @@ import type { AssigneeInfo } from './AssigneeInfo'; -import type { GlipTaskRecurrenceInfo } from './GlipTaskRecurrenceInfo'; import type { GlipAttachmentInfoRequest } from './GlipAttachmentInfoRequest'; +import type { GlipTaskRecurrenceInfo } from './GlipTaskRecurrenceInfo'; export interface GlipUpdateTask { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/GreetingInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/GreetingInfo.ts index 30beb7c051..ce407d7b5c 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/GreetingInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/GreetingInfo.ts @@ -1,5 +1,5 @@ -import type { PresetInfo } from './PresetInfo'; import type { CustomGreetingInfoRequest } from './CustomGreetingInfoRequest'; +import type { PresetInfo } from './PresetInfo'; export interface GreetingInfo { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/IVRMenuInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/IVRMenuInfo.ts index 4f35802aef..dbfd529f68 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/IVRMenuInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/IVRMenuInfo.ts @@ -1,6 +1,6 @@ -import type { IVRMenuSiteInfo } from './IVRMenuSiteInfo'; -import type { IVRMenuPromptInfo } from './IVRMenuPromptInfo'; import type { IVRMenuActionsInfo } from './IVRMenuActionsInfo'; +import type { IVRMenuPromptInfo } from './IVRMenuPromptInfo'; +import type { IVRMenuSiteInfo } from './IVRMenuSiteInfo'; export interface IVRMenuInfo { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/IVRPrompts.ts b/packages/ringcentral-mock/src/platform/interfaces/IVRPrompts.ts index b42e07e07a..1b2b79af40 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/IVRPrompts.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/IVRPrompts.ts @@ -1,6 +1,6 @@ -import type { PromptInfo } from './PromptInfo'; import type { CallHandlingNavigationInfo } from './CallHandlingNavigationInfo'; import type { CallHandlingPagingInfo } from './CallHandlingPagingInfo'; +import type { PromptInfo } from './PromptInfo'; export interface IVRPrompts { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/InstantMessageEventBody.ts b/packages/ringcentral-mock/src/platform/interfaces/InstantMessageEventBody.ts index 790ea74fa2..9ffd3ef379 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/InstantMessageEventBody.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/InstantMessageEventBody.ts @@ -1,7 +1,7 @@ +import type { ConversationInfo } from './ConversationInfo'; +import type { MessageAttachmentInfo } from './MessageAttachmentInfo'; import type { NotificationRecipientInfo } from './NotificationRecipientInfo'; import type { SenderInfo } from './SenderInfo'; -import type { MessageAttachmentInfo } from './MessageAttachmentInfo'; -import type { ConversationInfo } from './ConversationInfo'; // Notification payload body export interface InstantMessageEventBody { diff --git a/packages/ringcentral-mock/src/platform/interfaces/ListMeetingRecordingsResponse.ts b/packages/ringcentral-mock/src/platform/interfaces/ListMeetingRecordingsResponse.ts index 0f2d26094a..c85560bfd1 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/ListMeetingRecordingsResponse.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/ListMeetingRecordingsResponse.ts @@ -1,6 +1,6 @@ import type { MeetingRecording } from './MeetingRecording'; -import type { MeetingRecordingsPagingInfo } from './MeetingRecordingsPagingInfo'; import type { MeetingRecordingsNavigationInfo } from './MeetingRecordingsNavigationInfo'; +import type { MeetingRecordingsPagingInfo } from './MeetingRecordingsPagingInfo'; export interface ListMeetingRecordingsResponse { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/MakeRingOutRequest.ts b/packages/ringcentral-mock/src/platform/interfaces/MakeRingOutRequest.ts index 7b7e1e904c..602a4a2769 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/MakeRingOutRequest.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/MakeRingOutRequest.ts @@ -1,6 +1,6 @@ +import type { MakeRingOutCallerIdInfo } from './MakeRingOutCallerIdInfo'; import type { MakeRingOutCallerInfoRequestFrom } from './MakeRingOutCallerInfoRequestFrom'; import type { MakeRingOutCallerInfoRequestTo } from './MakeRingOutCallerInfoRequestTo'; -import type { MakeRingOutCallerIdInfo } from './MakeRingOutCallerIdInfo'; import type { MakeRingOutCoutryInfo } from './MakeRingOutCoutryInfo'; export interface MakeRingOutRequest { diff --git a/packages/ringcentral-mock/src/platform/interfaces/MeetingRequestResource.ts b/packages/ringcentral-mock/src/platform/interfaces/MeetingRequestResource.ts index be27c08f6f..97eb0ad558 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/MeetingRequestResource.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/MeetingRequestResource.ts @@ -1,5 +1,5 @@ -import type { MeetingScheduleResource } from './MeetingScheduleResource'; import type { HostInfoRequest } from './HostInfoRequest'; +import type { MeetingScheduleResource } from './MeetingScheduleResource'; import type { RecurrenceInfo } from './RecurrenceInfo'; export interface MeetingRequestResource { diff --git a/packages/ringcentral-mock/src/platform/interfaces/MeetingResponseResource.ts b/packages/ringcentral-mock/src/platform/interfaces/MeetingResponseResource.ts index f03dfe1543..d00de43ae5 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/MeetingResponseResource.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/MeetingResponseResource.ts @@ -1,6 +1,6 @@ +import type { HostInfoRequest } from './HostInfoRequest'; import type { MeetingLinks } from './MeetingLinks'; import type { MeetingScheduleResource } from './MeetingScheduleResource'; -import type { HostInfoRequest } from './HostInfoRequest'; import type { RecurrenceInfo } from './RecurrenceInfo'; export interface MeetingResponseResource { diff --git a/packages/ringcentral-mock/src/platform/interfaces/MeetingServiceInfoResource.ts b/packages/ringcentral-mock/src/platform/interfaces/MeetingServiceInfoResource.ts index faae6d26be..b86855ae5f 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/MeetingServiceInfoResource.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/MeetingServiceInfoResource.ts @@ -1,5 +1,5 @@ -import type { MeetingExternalUserInfoResource } from './MeetingExternalUserInfoResource'; import type { DialInNumberResource } from './DialInNumberResource'; +import type { MeetingExternalUserInfoResource } from './MeetingExternalUserInfoResource'; export interface MeetingServiceInfoResource { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/MeetingUserSettingsResponse.ts b/packages/ringcentral-mock/src/platform/interfaces/MeetingUserSettingsResponse.ts index 0fd5f768e8..e0e8604b38 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/MeetingUserSettingsResponse.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/MeetingUserSettingsResponse.ts @@ -1,6 +1,6 @@ -import type { UserMeetingRecordingSetting } from './UserMeetingRecordingSetting'; import type { ScheduleUserMeetingInfo } from './ScheduleUserMeetingInfo'; import type { TelephonyUserMeetingSettings } from './TelephonyUserMeetingSettings'; +import type { UserMeetingRecordingSetting } from './UserMeetingRecordingSetting'; export interface MeetingUserSettingsResponse { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/MeetingsResource.ts b/packages/ringcentral-mock/src/platform/interfaces/MeetingsResource.ts index c3a62ec3db..98fa417601 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/MeetingsResource.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/MeetingsResource.ts @@ -1,6 +1,6 @@ import type { MeetingResponseResource } from './MeetingResponseResource'; -import type { MeetingsPagingInfo } from './MeetingsPagingInfo'; import type { MeetingsNavigationInfo } from './MeetingsNavigationInfo'; +import type { MeetingsPagingInfo } from './MeetingsPagingInfo'; export interface MeetingsResource { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/MessageBody.ts b/packages/ringcentral-mock/src/platform/interfaces/MessageBody.ts index e919bd3119..e591f6b29f 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/MessageBody.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/MessageBody.ts @@ -1,7 +1,7 @@ -import type { MessageAttachmentInfo } from './MessageAttachmentInfo'; import type { ConversationInfo } from './ConversationInfo'; -import type { MessageSenderInfo } from './MessageSenderInfo'; +import type { MessageAttachmentInfo } from './MessageAttachmentInfo'; import type { MessageRecipientInfo } from './MessageRecipientInfo'; +import type { MessageSenderInfo } from './MessageSenderInfo'; export interface MessageBody { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/NetworkInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/NetworkInfo.ts index 3cd08c708e..5c0cc5a849 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/NetworkInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/NetworkInfo.ts @@ -1,7 +1,7 @@ import type { AutomaticLocationUpdatesSiteInfo } from './AutomaticLocationUpdatesSiteInfo'; -import type { PublicIpRangeInfo } from './PublicIpRangeInfo'; -import type { PrivateIpRangeInfo } from './PrivateIpRangeInfo'; import type { ERLLocationInfo } from './ERLLocationInfo'; +import type { PrivateIpRangeInfo } from './PrivateIpRangeInfo'; +import type { PublicIpRangeInfo } from './PublicIpRangeInfo'; export interface NetworkInfo { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/NotificationSettings.ts b/packages/ringcentral-mock/src/platform/interfaces/NotificationSettings.ts index 4688847245..0f602a6d4a 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/NotificationSettings.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/NotificationSettings.ts @@ -1,9 +1,9 @@ import type { EmailRecipientInfo } from './EmailRecipientInfo'; -import type { VoicemailsInfo } from './VoicemailsInfo'; import type { InboundFaxesInfo } from './InboundFaxesInfo'; -import type { OutboundFaxesInfo } from './OutboundFaxesInfo'; import type { InboundTextsInfo } from './InboundTextsInfo'; import type { MissedCallsInfo } from './MissedCallsInfo'; +import type { OutboundFaxesInfo } from './OutboundFaxesInfo'; +import type { VoicemailsInfo } from './VoicemailsInfo'; export interface NotificationSettings { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/NotificationSettingsUpdateRequest.ts b/packages/ringcentral-mock/src/platform/interfaces/NotificationSettingsUpdateRequest.ts index 1d285fe6ed..e7cd17c927 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/NotificationSettingsUpdateRequest.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/NotificationSettingsUpdateRequest.ts @@ -1,8 +1,8 @@ -import type { VoicemailsInfo } from './VoicemailsInfo'; import type { InboundFaxesInfo } from './InboundFaxesInfo'; -import type { OutboundFaxesInfo } from './OutboundFaxesInfo'; import type { InboundTextsInfo } from './InboundTextsInfo'; import type { MissedCallsInfo } from './MissedCallsInfo'; +import type { OutboundFaxesInfo } from './OutboundFaxesInfo'; +import type { VoicemailsInfo } from './VoicemailsInfo'; export interface NotificationSettingsUpdateRequest { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/OptOutBulkAssignResponse.ts b/packages/ringcentral-mock/src/platform/interfaces/OptOutBulkAssignResponse.ts index a45883b448..1aee3b46d0 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/OptOutBulkAssignResponse.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/OptOutBulkAssignResponse.ts @@ -1,5 +1,5 @@ -import type { BulkAssignOptOuts } from './BulkAssignOptOuts'; import type { BulkAssignOptIns } from './BulkAssignOptIns'; +import type { BulkAssignOptOuts } from './BulkAssignOptOuts'; // The results of adding opt-outs and opt-ins export interface OptOutBulkAssignResponse { diff --git a/packages/ringcentral-mock/src/platform/interfaces/PartySuperviseResponse.ts b/packages/ringcentral-mock/src/platform/interfaces/PartySuperviseResponse.ts index 1fd6409fe8..e9114bc33f 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/PartySuperviseResponse.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/PartySuperviseResponse.ts @@ -1,6 +1,6 @@ -import type { PartyInfo } from './PartyInfo'; -import type { OwnerInfo } from './OwnerInfo'; import type { CallStatusInfo } from './CallStatusInfo'; +import type { OwnerInfo } from './OwnerInfo'; +import type { PartyInfo } from './PartyInfo'; export interface PartySuperviseResponse { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsAggregatesRequest.ts b/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsAggregatesRequest.ts index 9ba169cdac..d9a33e5894 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsAggregatesRequest.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsAggregatesRequest.ts @@ -1,7 +1,7 @@ import type { AggregatePerformanceCallsGrouping } from './AggregatePerformanceCallsGrouping'; -import type { PerformanceCallsTimeSettings } from './PerformanceCallsTimeSettings'; import type { PerformanceCallsFilters } from './PerformanceCallsFilters'; import type { PerformanceCallsResponseDataOptions } from './PerformanceCallsResponseDataOptions'; +import type { PerformanceCallsTimeSettings } from './PerformanceCallsTimeSettings'; export interface PerformanceCallsAggregatesRequest { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsAggregatesResponse.ts b/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsAggregatesResponse.ts index 73c1f7b7eb..52d2bc8623 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsAggregatesResponse.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsAggregatesResponse.ts @@ -1,5 +1,5 @@ -import type { ResponsePaging } from './ResponsePaging'; import type { PerformanceCallsData } from './PerformanceCallsData'; +import type { ResponsePaging } from './ResponsePaging'; export interface PerformanceCallsAggregatesResponse { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsCounters.ts b/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsCounters.ts index 665aabc806..c88f8eddf6 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsCounters.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsCounters.ts @@ -1,13 +1,13 @@ -import type { PerformanceCallsTotal } from './PerformanceCallsTotal'; +import type { PerformanceCallsActions } from './PerformanceCallsActions'; +import type { PerformanceCallsByCompanyHours } from './PerformanceCallsByCompanyHours'; import type { PerformanceCallsByDirection } from './PerformanceCallsByDirection'; import type { PerformanceCallsByOrigin } from './PerformanceCallsByOrigin'; +import type { PerformanceCallsByQueueSla } from './PerformanceCallsByQueueSla'; import type { PerformanceCallsByResponse } from './PerformanceCallsByResponse'; -import type { PerformanceCallsSegments } from './PerformanceCallsSegments'; import type { PerformanceCallsByResult } from './PerformanceCallsByResult'; -import type { PerformanceCallsActions } from './PerformanceCallsActions'; -import type { PerformanceCallsByCompanyHours } from './PerformanceCallsByCompanyHours'; -import type { PerformanceCallsByQueueSla } from './PerformanceCallsByQueueSla'; import type { PerformanceCallsByType } from './PerformanceCallsByType'; +import type { PerformanceCallsSegments } from './PerformanceCallsSegments'; +import type { PerformanceCallsTotal } from './PerformanceCallsTotal'; // Call volume data for the specified grouping export interface PerformanceCallsCounters { diff --git a/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsCountersResponseOptions.ts b/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsCountersResponseOptions.ts index 3866d19898..7f22165f62 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsCountersResponseOptions.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsCountersResponseOptions.ts @@ -1,13 +1,13 @@ import type { PerformanceCallsCountersResponseOptionsAllCalls } from './PerformanceCallsCountersResponseOptionsAllCalls'; +import type { PerformanceCallsCountersResponseOptionsCallsByActions } from './PerformanceCallsCountersResponseOptionsCallsByActions'; +import type { PerformanceCallsCountersResponseOptionsCallsByCompanyHours } from './PerformanceCallsCountersResponseOptionsCallsByCompanyHours'; import type { PerformanceCallsCountersResponseOptionsCallsByDirection } from './PerformanceCallsCountersResponseOptionsCallsByDirection'; import type { PerformanceCallsCountersResponseOptionsCallsByOrigin } from './PerformanceCallsCountersResponseOptionsCallsByOrigin'; +import type { PerformanceCallsCountersResponseOptionsCallsByQueueSla } from './PerformanceCallsCountersResponseOptionsCallsByQueueSla'; import type { PerformanceCallsCountersResponseOptionsCallsByResponse } from './PerformanceCallsCountersResponseOptionsCallsByResponse'; -import type { PerformanceCallsCountersResponseOptionsCallsSegments } from './PerformanceCallsCountersResponseOptionsCallsSegments'; import type { PerformanceCallsCountersResponseOptionsCallsByResult } from './PerformanceCallsCountersResponseOptionsCallsByResult'; -import type { PerformanceCallsCountersResponseOptionsCallsByCompanyHours } from './PerformanceCallsCountersResponseOptionsCallsByCompanyHours'; -import type { PerformanceCallsCountersResponseOptionsCallsByQueueSla } from './PerformanceCallsCountersResponseOptionsCallsByQueueSla'; -import type { PerformanceCallsCountersResponseOptionsCallsByActions } from './PerformanceCallsCountersResponseOptionsCallsByActions'; import type { PerformanceCallsCountersResponseOptionsCallsByType } from './PerformanceCallsCountersResponseOptionsCallsByType'; +import type { PerformanceCallsCountersResponseOptionsCallsSegments } from './PerformanceCallsCountersResponseOptionsCallsSegments'; // The formula is defined by `aggregationType` and `aggregationInterval` for every counter individually. If `aggregationType` is `Sum` or `Percent`, `aggregationInterval` is not supported. If `aggregationType` is `Min`, `Max` or `Average`,`aggregationInterval` is required export interface PerformanceCallsCountersResponseOptions { diff --git a/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsData.ts b/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsData.ts index 4c2f38aae2..9ce61199ca 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsData.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsData.ts @@ -1,6 +1,6 @@ import type { KeyInfo } from './KeyInfo'; -import type { PerformanceCallsTimers } from './PerformanceCallsTimers'; import type { PerformanceCallsCounters } from './PerformanceCallsCounters'; +import type { PerformanceCallsTimers } from './PerformanceCallsTimers'; export interface PerformanceCallsData { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsFilters.ts b/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsFilters.ts index 9f10f38c24..ea02c7154b 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsFilters.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsFilters.ts @@ -1,5 +1,5 @@ -import type { CallSegmentFilter } from './CallSegmentFilter'; import type { CallActionFilter } from './CallActionFilter'; +import type { CallSegmentFilter } from './CallSegmentFilter'; import type { PerformanceCallsFilterByLength } from './PerformanceCallsFilterByLength'; import type { PerformanceCallsFilterTimeSpentByMailbox } from './PerformanceCallsFilterTimeSpentByMailbox'; diff --git a/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsTimeSettings.ts b/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsTimeSettings.ts index 1ceeb0637b..edbe3d5afe 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsTimeSettings.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsTimeSettings.ts @@ -1,5 +1,5 @@ -import type { PerformanceCallsTimeRange } from './PerformanceCallsTimeRange'; import type { PerformanceCallsAdvancedTimeSettings } from './PerformanceCallsAdvancedTimeSettings'; +import type { PerformanceCallsTimeRange } from './PerformanceCallsTimeRange'; // Date-time range for which the calls are aggregated. The call is considered to be within time range if it started within time range. Both borders are inclusive export interface PerformanceCallsTimeSettings { diff --git a/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsTimelineRequest.ts b/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsTimelineRequest.ts index 536b6cc5bc..ca8d45955d 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsTimelineRequest.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsTimelineRequest.ts @@ -1,6 +1,6 @@ -import type { TimelinePerformanceCallsGrouping } from './TimelinePerformanceCallsGrouping'; -import type { PerformanceCallsTimeSettings } from './PerformanceCallsTimeSettings'; import type { PerformanceCallsFilters } from './PerformanceCallsFilters'; +import type { PerformanceCallsTimeSettings } from './PerformanceCallsTimeSettings'; +import type { TimelinePerformanceCallsGrouping } from './TimelinePerformanceCallsGrouping'; import type { TimelineResponseDataOptions } from './TimelineResponseDataOptions'; export interface PerformanceCallsTimelineRequest { diff --git a/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsTimers.ts b/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsTimers.ts index 18f8bee018..05a48de34a 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsTimers.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsTimers.ts @@ -1,12 +1,12 @@ -import type { PerformanceCallsTotal } from './PerformanceCallsTotal'; +import type { PerformanceCallsByCompanyHours } from './PerformanceCallsByCompanyHours'; import type { PerformanceCallsByDirection } from './PerformanceCallsByDirection'; import type { PerformanceCallsByOrigin } from './PerformanceCallsByOrigin'; +import type { PerformanceCallsByQueueSla } from './PerformanceCallsByQueueSla'; import type { PerformanceCallsByResponse } from './PerformanceCallsByResponse'; -import type { PerformanceCallsSegments } from './PerformanceCallsSegments'; import type { PerformanceCallsByResult } from './PerformanceCallsByResult'; -import type { PerformanceCallsByCompanyHours } from './PerformanceCallsByCompanyHours'; -import type { PerformanceCallsByQueueSla } from './PerformanceCallsByQueueSla'; import type { PerformanceCallsByType } from './PerformanceCallsByType'; +import type { PerformanceCallsSegments } from './PerformanceCallsSegments'; +import type { PerformanceCallsTotal } from './PerformanceCallsTotal'; // Call length data for the specified grouping export interface PerformanceCallsTimers { diff --git a/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsTimersResponseOptions.ts b/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsTimersResponseOptions.ts index 7fd7ef8d66..e12ba19dd4 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsTimersResponseOptions.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/PerformanceCallsTimersResponseOptions.ts @@ -1,12 +1,12 @@ import type { PerformanceCallsTimersResponseOptionsAllCallsDuration } from './PerformanceCallsTimersResponseOptionsAllCallsDuration'; +import type { PerformanceCallsTimersResponseOptionsCallsDurationByCompanyHours } from './PerformanceCallsTimersResponseOptionsCallsDurationByCompanyHours'; import type { PerformanceCallsTimersResponseOptionsCallsDurationByDirection } from './PerformanceCallsTimersResponseOptionsCallsDurationByDirection'; import type { PerformanceCallsTimersResponseOptionsCallsDurationByOrigin } from './PerformanceCallsTimersResponseOptionsCallsDurationByOrigin'; +import type { PerformanceCallsTimersResponseOptionsCallsDurationByQueueSla } from './PerformanceCallsTimersResponseOptionsCallsDurationByQueueSla'; import type { PerformanceCallsTimersResponseOptionsCallsDurationByResponse } from './PerformanceCallsTimersResponseOptionsCallsDurationByResponse'; -import type { PerformanceCallsTimersResponseOptionsCallsSegmentsDuration } from './PerformanceCallsTimersResponseOptionsCallsSegmentsDuration'; import type { PerformanceCallsTimersResponseOptionsCallsDurationByResult } from './PerformanceCallsTimersResponseOptionsCallsDurationByResult'; -import type { PerformanceCallsTimersResponseOptionsCallsDurationByCompanyHours } from './PerformanceCallsTimersResponseOptionsCallsDurationByCompanyHours'; -import type { PerformanceCallsTimersResponseOptionsCallsDurationByQueueSla } from './PerformanceCallsTimersResponseOptionsCallsDurationByQueueSla'; import type { PerformanceCallsTimersResponseOptionsCallsDurationByType } from './PerformanceCallsTimersResponseOptionsCallsDurationByType'; +import type { PerformanceCallsTimersResponseOptionsCallsSegmentsDuration } from './PerformanceCallsTimersResponseOptionsCallsSegmentsDuration'; // The formula is defined by `aggregationType` and `aggregationInterval` for every timer individually. If `aggregationType` is `Sum` or `Percent`, `aggregationInterval` is not supported. If `aggregationType` is `Min`, `Max` or `Average`,`aggregationInterval` is supported, but not required. If left empty, aggregation will be performed on per-call basis export interface PerformanceCallsTimersResponseOptions { diff --git a/packages/ringcentral-mock/src/platform/interfaces/PermissionCategoryCollectionResource.ts b/packages/ringcentral-mock/src/platform/interfaces/PermissionCategoryCollectionResource.ts index 2bb0d9e916..35539ba5a4 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/PermissionCategoryCollectionResource.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/PermissionCategoryCollectionResource.ts @@ -1,6 +1,6 @@ import type { PermissionCategoryResource } from './PermissionCategoryResource'; -import type { RNPPagingInfo } from './RNPPagingInfo'; import type { RNPNavigationInfo } from './RNPNavigationInfo'; +import type { RNPPagingInfo } from './RNPPagingInfo'; export interface PermissionCategoryCollectionResource { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/PermissionCollectionResource.ts b/packages/ringcentral-mock/src/platform/interfaces/PermissionCollectionResource.ts index 76b197ba6a..3904a44b1e 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/PermissionCollectionResource.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/PermissionCollectionResource.ts @@ -1,6 +1,6 @@ import type { PermissionResource } from './PermissionResource'; -import type { RNPPagingInfo } from './RNPPagingInfo'; import type { RNPNavigationInfo } from './RNPNavigationInfo'; +import type { RNPPagingInfo } from './RNPPagingInfo'; export interface PermissionCollectionResource { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/PhoneLinesInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/PhoneLinesInfo.ts index e3ce17096d..9435649d4c 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/PhoneLinesInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/PhoneLinesInfo.ts @@ -1,5 +1,5 @@ -import type { PhoneNumberInfoIntId } from './PhoneNumberInfoIntId'; import type { EmergencyAddress } from './EmergencyAddress'; +import type { PhoneNumberInfoIntId } from './PhoneNumberInfoIntId'; export interface PhoneLinesInfo { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/PhoneNumberInfoIntId.ts b/packages/ringcentral-mock/src/platform/interfaces/PhoneNumberInfoIntId.ts index 269a40ffb7..cf5e1e7c0b 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/PhoneNumberInfoIntId.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/PhoneNumberInfoIntId.ts @@ -1,5 +1,5 @@ -import type { PhoneNumberCountryInfo } from './PhoneNumberCountryInfo'; import type { DeviceProvisioningExtensionInfo } from './DeviceProvisioningExtensionInfo'; +import type { PhoneNumberCountryInfo } from './PhoneNumberCountryInfo'; // Phone number information export interface PhoneNumberInfoIntId { diff --git a/packages/ringcentral-mock/src/platform/interfaces/QueueInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/QueueInfo.ts index 9257bb4031..f32d747f12 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/QueueInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/QueueInfo.ts @@ -1,5 +1,5 @@ -import type { TransferInfo } from './TransferInfo'; import type { FixedOrderAgents } from './FixedOrderAgents'; +import type { TransferInfo } from './TransferInfo'; import type { UnconditionalForwardingInfo } from './UnconditionalForwardingInfo'; // Queue settings applied for department (call queue) extension type, with the 'AgentQueue' value specified as a call handling action diff --git a/packages/ringcentral-mock/src/platform/interfaces/RegionalSettings.ts b/packages/ringcentral-mock/src/platform/interfaces/RegionalSettings.ts index e3c22a973b..507e17d014 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/RegionalSettings.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/RegionalSettings.ts @@ -1,8 +1,8 @@ import type { CountryInfo } from './CountryInfo'; -import type { TimezoneInfo } from './TimezoneInfo'; -import type { RegionalLanguageInfo } from './RegionalLanguageInfo'; -import type { GreetingLanguageInfo } from './GreetingLanguageInfo'; import type { FormattingLocaleInfo } from './FormattingLocaleInfo'; +import type { GreetingLanguageInfo } from './GreetingLanguageInfo'; +import type { RegionalLanguageInfo } from './RegionalLanguageInfo'; +import type { TimezoneInfo } from './TimezoneInfo'; // Regional data (timezone, home country, language) of an extension/account. The default is Company (Auto-Receptionist) settings export interface RegionalSettings { diff --git a/packages/ringcentral-mock/src/platform/interfaces/ReplyParty.ts b/packages/ringcentral-mock/src/platform/interfaces/ReplyParty.ts index e7afca3764..5de9eced51 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/ReplyParty.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/ReplyParty.ts @@ -1,7 +1,7 @@ import type { CallStatusInfo } from './CallStatusInfo'; +import type { OwnerInfo } from './OwnerInfo'; import type { ParkInfo } from './ParkInfo'; import type { PartyInfo } from './PartyInfo'; -import type { OwnerInfo } from './OwnerInfo'; export interface ReplyParty { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/RolesBusinessSiteResource.ts b/packages/ringcentral-mock/src/platform/interfaces/RolesBusinessSiteResource.ts index 46219f9ba3..8cf1ebeb7e 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/RolesBusinessSiteResource.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/RolesBusinessSiteResource.ts @@ -1,6 +1,6 @@ import type { BasicExtensionInfoResource } from './BasicExtensionInfoResource'; -import type { RolesRegionalSettingsResource } from './RolesRegionalSettingsResource'; import type { ContactAddressInfoResource } from './ContactAddressInfoResource'; +import type { RolesRegionalSettingsResource } from './RolesRegionalSettingsResource'; export interface RolesBusinessSiteResource { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/RolesCollectionResource.ts b/packages/ringcentral-mock/src/platform/interfaces/RolesCollectionResource.ts index 5ac527a030..c636ab55ea 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/RolesCollectionResource.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/RolesCollectionResource.ts @@ -1,6 +1,6 @@ -import type { RoleResource } from './RoleResource'; -import type { RNPPagingInfo } from './RNPPagingInfo'; import type { RNPNavigationInfo } from './RNPNavigationInfo'; +import type { RNPPagingInfo } from './RNPPagingInfo'; +import type { RoleResource } from './RoleResource'; export interface RolesCollectionResource { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/RolesRegionalSettingsResource.ts b/packages/ringcentral-mock/src/platform/interfaces/RolesRegionalSettingsResource.ts index a9908761eb..9f9f005c50 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/RolesRegionalSettingsResource.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/RolesRegionalSettingsResource.ts @@ -1,7 +1,7 @@ -import type { RolesTimezoneResource } from './RolesTimezoneResource'; +import type { CurrencyResource } from './CurrencyResource'; import type { RolesCountryResource } from './RolesCountryResource'; import type { RolesLanguageResource } from './RolesLanguageResource'; -import type { CurrencyResource } from './CurrencyResource'; +import type { RolesTimezoneResource } from './RolesTimezoneResource'; export interface RolesRegionalSettingsResource { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/ScheduleInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/ScheduleInfo.ts index af192efe74..7d88744b64 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/ScheduleInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/ScheduleInfo.ts @@ -1,5 +1,5 @@ -import type { WeeklyScheduleInfo } from './WeeklyScheduleInfo'; import type { RangesInfo } from './RangesInfo'; +import type { WeeklyScheduleInfo } from './WeeklyScheduleInfo'; // Schedule when an answering rule should be applied export interface ScheduleInfo { diff --git a/packages/ringcentral-mock/src/platform/interfaces/ServiceInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/ServiceInfo.ts index 70f5ac8c87..b6c8e976ba 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/ServiceInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/ServiceInfo.ts @@ -1,8 +1,8 @@ import type { BillingPlanInfo } from './BillingPlanInfo'; import type { BrandInfo } from './BrandInfo'; +import type { ContractedCountryInfo } from './ContractedCountryInfo'; import type { ServicePlanInfo } from './ServicePlanInfo'; import type { TargetServicePlanInfo } from './TargetServicePlanInfo'; -import type { ContractedCountryInfo } from './ContractedCountryInfo'; // Account service information, including brand, service plan and billing plan export interface ServiceInfo { diff --git a/packages/ringcentral-mock/src/platform/interfaces/ServiceProviderConfig.ts b/packages/ringcentral-mock/src/platform/interfaces/ServiceProviderConfig.ts index eb3d0f6dd1..b99a540d58 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/ServiceProviderConfig.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/ServiceProviderConfig.ts @@ -1,7 +1,7 @@ import type { AuthenticationScheme } from './AuthenticationScheme'; import type { BulkSupported } from './BulkSupported'; -import type { Supported } from './Supported'; import type { FilterSupported } from './FilterSupported'; +import type { Supported } from './Supported'; export interface ServiceProviderConfig { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/SipRegistrationDeviceInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/SipRegistrationDeviceInfo.ts index ad52b18108..cdc04ab667 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/SipRegistrationDeviceInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/SipRegistrationDeviceInfo.ts @@ -1,10 +1,10 @@ -import type { DeviceModelInfo } from './DeviceModelInfo'; -import type { DeviceExtensionInfo } from './DeviceExtensionInfo'; import type { DeviceEmergencyServiceAddressResource } from './DeviceEmergencyServiceAddressResource'; -import type { SipRegistrationDeviceEmergencyInfo } from './SipRegistrationDeviceEmergencyInfo'; -import type { Shipping } from './Shipping'; +import type { DeviceExtensionInfo } from './DeviceExtensionInfo'; +import type { DeviceModelInfo } from './DeviceModelInfo'; import type { DevicePhoneLinesInfo } from './DevicePhoneLinesInfo'; import type { DeviceSiteInfo } from './DeviceSiteInfo'; +import type { Shipping } from './Shipping'; +import type { SipRegistrationDeviceEmergencyInfo } from './SipRegistrationDeviceEmergencyInfo'; export interface SipRegistrationDeviceInfo { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/SiteInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/SiteInfo.ts index e5d2ffdb0b..1179d087c5 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/SiteInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/SiteInfo.ts @@ -1,6 +1,6 @@ import type { ContactBusinessAddressInfo } from './ContactBusinessAddressInfo'; -import type { RegionalSettings } from './RegionalSettings'; import type { OperatorInfo } from './OperatorInfo'; +import type { RegionalSettings } from './RegionalSettings'; export interface SiteInfo { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/SubscriptionInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/SubscriptionInfo.ts index 1cbdca8777..2d0b356f04 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/SubscriptionInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/SubscriptionInfo.ts @@ -1,6 +1,6 @@ import type { DisabledFilterInfo } from './DisabledFilterInfo'; -import type { NotificationDeliveryMode } from './NotificationDeliveryMode'; import type { NotificationBlacklistedData } from './NotificationBlacklistedData'; +import type { NotificationDeliveryMode } from './NotificationDeliveryMode'; export interface SubscriptionInfo { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/SuperviseCallSession.ts b/packages/ringcentral-mock/src/platform/interfaces/SuperviseCallSession.ts index 17c511e287..9d6194306f 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/SuperviseCallSession.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/SuperviseCallSession.ts @@ -1,7 +1,7 @@ +import type { CallStatusInfo } from './CallStatusInfo'; +import type { OwnerInfo } from './OwnerInfo'; import type { SupervisePartyFrom } from './SupervisePartyFrom'; import type { SupervisePartyTo } from './SupervisePartyTo'; -import type { OwnerInfo } from './OwnerInfo'; -import type { CallStatusInfo } from './CallStatusInfo'; export interface SuperviseCallSession { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/SwitchInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/SwitchInfo.ts index de6c5028b7..6b7de40ff1 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/SwitchInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/SwitchInfo.ts @@ -1,6 +1,6 @@ -import type { SwitchSiteInfo } from './SwitchSiteInfo'; -import type { LocationUpdatesEmergencyAddressInfo } from './LocationUpdatesEmergencyAddressInfo'; import type { ERLLocationInfo } from './ERLLocationInfo'; +import type { LocationUpdatesEmergencyAddressInfo } from './LocationUpdatesEmergencyAddressInfo'; +import type { SwitchSiteInfo } from './SwitchSiteInfo'; export interface SwitchInfo { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/SwitchesList.ts b/packages/ringcentral-mock/src/platform/interfaces/SwitchesList.ts index a2511c5306..091678fa18 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/SwitchesList.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/SwitchesList.ts @@ -1,6 +1,6 @@ -import type { SwitchInfo } from './SwitchInfo'; import type { ProvisioningNavigationInfo } from './ProvisioningNavigationInfo'; import type { ProvisioningPagingInfo } from './ProvisioningPagingInfo'; +import type { SwitchInfo } from './SwitchInfo'; export interface SwitchesList { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/TimelineDataPoint.ts b/packages/ringcentral-mock/src/platform/interfaces/TimelineDataPoint.ts index 3ef64ff8af..07026c6577 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/TimelineDataPoint.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/TimelineDataPoint.ts @@ -1,5 +1,5 @@ -import type { PerformanceCallsTimers } from './PerformanceCallsTimers'; import type { PerformanceCallsCounters } from './PerformanceCallsCounters'; +import type { PerformanceCallsTimers } from './PerformanceCallsTimers'; export interface TimelineDataPoint { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/UnifiedPresence.ts b/packages/ringcentral-mock/src/platform/interfaces/UnifiedPresence.ts index 6ad17262a8..048004e546 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/UnifiedPresence.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/UnifiedPresence.ts @@ -1,6 +1,6 @@ import type { UnifiedPresenceGlip } from './UnifiedPresenceGlip'; -import type { UnifiedPresenceTelephony } from './UnifiedPresenceTelephony'; import type { UnifiedPresenceMeeting } from './UnifiedPresenceMeeting'; +import type { UnifiedPresenceTelephony } from './UnifiedPresenceTelephony'; export interface UnifiedPresence { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/UpdateAnsweringRuleRequest.ts b/packages/ringcentral-mock/src/platform/interfaces/UpdateAnsweringRuleRequest.ts index 4c4184ed06..c8abc535b3 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/UpdateAnsweringRuleRequest.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/UpdateAnsweringRuleRequest.ts @@ -1,13 +1,13 @@ -import type { ForwardingInfoCreateRuleRequest } from './ForwardingInfoCreateRuleRequest'; -import type { CallersInfoRequest } from './CallersInfoRequest'; import type { CalledNumberInfo } from './CalledNumberInfo'; +import type { CallersInfoRequest } from './CallersInfoRequest'; +import type { ForwardingInfoCreateRuleRequest } from './ForwardingInfoCreateRuleRequest'; +import type { GreetingInfo } from './GreetingInfo'; +import type { MissedCallInfo } from './MissedCallInfo'; +import type { QueueInfo } from './QueueInfo'; import type { ScheduleInfo } from './ScheduleInfo'; +import type { TransferredExtensionInfo } from './TransferredExtensionInfo'; import type { UnconditionalForwardingInfo } from './UnconditionalForwardingInfo'; -import type { QueueInfo } from './QueueInfo'; import type { VoicemailInfo } from './VoicemailInfo'; -import type { MissedCallInfo } from './MissedCallInfo'; -import type { GreetingInfo } from './GreetingInfo'; -import type { TransferredExtensionInfo } from './TransferredExtensionInfo'; export interface UpdateAnsweringRuleRequest { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/UpdateNetworkRequest.ts b/packages/ringcentral-mock/src/platform/interfaces/UpdateNetworkRequest.ts index 5162a645cf..47c813fb34 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/UpdateNetworkRequest.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/UpdateNetworkRequest.ts @@ -1,7 +1,7 @@ import type { AutomaticLocationUpdatesSiteInfo } from './AutomaticLocationUpdatesSiteInfo'; -import type { PublicIpRangeInfo } from './PublicIpRangeInfo'; -import type { PrivateIpRangeInfoRequest } from './PrivateIpRangeInfoRequest'; import type { ERLLocationInfo } from './ERLLocationInfo'; +import type { PrivateIpRangeInfoRequest } from './PrivateIpRangeInfoRequest'; +import type { PublicIpRangeInfo } from './PublicIpRangeInfo'; export interface UpdateNetworkRequest { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/UpdateSwitchInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/UpdateSwitchInfo.ts index 7384176664..53c1e65703 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/UpdateSwitchInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/UpdateSwitchInfo.ts @@ -1,6 +1,6 @@ -import type { SwitchSiteInfo } from './SwitchSiteInfo'; -import type { LocationUpdatesEmergencyAddressInfoRequest } from './LocationUpdatesEmergencyAddressInfoRequest'; import type { ERLLocationInfo } from './ERLLocationInfo'; +import type { LocationUpdatesEmergencyAddressInfoRequest } from './LocationUpdatesEmergencyAddressInfoRequest'; +import type { SwitchSiteInfo } from './SwitchSiteInfo'; export interface UpdateSwitchInfo { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/UpdateWirelessPoint.ts b/packages/ringcentral-mock/src/platform/interfaces/UpdateWirelessPoint.ts index edbfc54430..89ce8fe25c 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/UpdateWirelessPoint.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/UpdateWirelessPoint.ts @@ -1,6 +1,6 @@ import type { AutomaticLocationUpdatesSiteInfo } from './AutomaticLocationUpdatesSiteInfo'; -import type { LocationUpdatesEmergencyAddressInfoRequest } from './LocationUpdatesEmergencyAddressInfoRequest'; import type { ERLLocationInfo } from './ERLLocationInfo'; +import type { LocationUpdatesEmergencyAddressInfoRequest } from './LocationUpdatesEmergencyAddressInfoRequest'; export interface UpdateWirelessPoint { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/User.ts b/packages/ringcentral-mock/src/platform/interfaces/User.ts index aec512b347..a52e2a937a 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/User.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/User.ts @@ -1,9 +1,9 @@ -import type { UserAddress } from './UserAddress'; import type { Email } from './Email'; +import type { EnterpriseUser } from './EnterpriseUser'; import type { Name } from './Name'; import type { PhoneNumber } from './PhoneNumber'; import type { Photo } from './Photo'; -import type { EnterpriseUser } from './EnterpriseUser'; +import type { UserAddress } from './UserAddress'; export interface User { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/UserActiveCallsResponse.ts b/packages/ringcentral-mock/src/platform/interfaces/UserActiveCallsResponse.ts index 9f524a5d3f..9063aaea6b 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/UserActiveCallsResponse.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/UserActiveCallsResponse.ts @@ -1,6 +1,6 @@ -import type { UserCallLogRecord } from './UserCallLogRecord'; import type { CallLogNavigationInfo } from './CallLogNavigationInfo'; import type { CallLogPagingInfo } from './CallLogPagingInfo'; +import type { UserCallLogRecord } from './UserCallLogRecord'; export interface UserActiveCallsResponse { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/UserAnsweringRuleList.ts b/packages/ringcentral-mock/src/platform/interfaces/UserAnsweringRuleList.ts index cff1eceba0..0d76798fda 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/UserAnsweringRuleList.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/UserAnsweringRuleList.ts @@ -1,6 +1,6 @@ -import type { UserAnsweringRuleListRecord } from './UserAnsweringRuleListRecord'; -import type { UserAnsweringRuleListPaging } from './UserAnsweringRuleListPaging'; import type { UserAnsweringRuleListNavigation } from './UserAnsweringRuleListNavigation'; +import type { UserAnsweringRuleListPaging } from './UserAnsweringRuleListPaging'; +import type { UserAnsweringRuleListRecord } from './UserAnsweringRuleListRecord'; export interface UserAnsweringRuleList { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/UserCallLogRecord.ts b/packages/ringcentral-mock/src/platform/interfaces/UserCallLogRecord.ts index bdbd620fee..8be3b20144 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/UserCallLogRecord.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/UserCallLogRecord.ts @@ -1,10 +1,10 @@ +import type { BillingInfo } from './BillingInfo'; import type { CallLogCallerInfo } from './CallLogCallerInfo'; -import type { ExtensionInfoCallLog } from './ExtensionInfoCallLog'; +import type { CallLogDelegateInfo } from './CallLogDelegateInfo'; import type { CallLogRecordLegInfo } from './CallLogRecordLegInfo'; -import type { BillingInfo } from './BillingInfo'; import type { CallLogRecordMessage } from './CallLogRecordMessage'; -import type { CallLogDelegateInfo } from './CallLogDelegateInfo'; import type { CallLogRecordingInfo } from './CallLogRecordingInfo'; +import type { ExtensionInfoCallLog } from './ExtensionInfoCallLog'; export interface UserCallLogRecord { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/UserCallLogResponse.ts b/packages/ringcentral-mock/src/platform/interfaces/UserCallLogResponse.ts index 93084baccf..4aa6f8635c 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/UserCallLogResponse.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/UserCallLogResponse.ts @@ -1,6 +1,6 @@ -import type { UserCallLogRecord } from './UserCallLogRecord'; import type { CallLogNavigationInfo } from './CallLogNavigationInfo'; import type { CallLogPagingInfo } from './CallLogPagingInfo'; +import type { UserCallLogRecord } from './UserCallLogRecord'; export interface UserCallLogResponse { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/UserPhoneNumberInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/UserPhoneNumberInfo.ts index dcf6a4cef4..8fcba1d058 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/UserPhoneNumberInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/UserPhoneNumberInfo.ts @@ -1,5 +1,5 @@ -import type { CountryInfo } from './CountryInfo'; import type { ContactCenterProvider } from './ContactCenterProvider'; +import type { CountryInfo } from './CountryInfo'; import type { UserPhoneNumberExtensionInfo } from './UserPhoneNumberExtensionInfo'; export interface UserPhoneNumberInfo { diff --git a/packages/ringcentral-mock/src/platform/interfaces/UserResponse.ts b/packages/ringcentral-mock/src/platform/interfaces/UserResponse.ts index e1ee95c360..012e28f2e6 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/UserResponse.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/UserResponse.ts @@ -1,10 +1,10 @@ -import type { UserAddress } from './UserAddress'; import type { Email } from './Email'; +import type { EnterpriseUser } from './EnterpriseUser'; +import type { Meta } from './Meta'; import type { Name } from './Name'; import type { PhoneNumber } from './PhoneNumber'; import type { Photo } from './Photo'; -import type { EnterpriseUser } from './EnterpriseUser'; -import type { Meta } from './Meta'; +import type { UserAddress } from './UserAddress'; export interface UserResponse { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/UserTemplates.ts b/packages/ringcentral-mock/src/platform/interfaces/UserTemplates.ts index 4acdc565b5..04279ce352 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/UserTemplates.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/UserTemplates.ts @@ -1,6 +1,6 @@ -import type { TemplateInfo } from './TemplateInfo'; import type { ProvisioningNavigationInfo } from './ProvisioningNavigationInfo'; import type { ProvisioningPagingInfo } from './ProvisioningPagingInfo'; +import type { TemplateInfo } from './TemplateInfo'; export interface UserTemplates { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/VoicemailMessageEventBody.ts b/packages/ringcentral-mock/src/platform/interfaces/VoicemailMessageEventBody.ts index 4b20c1feca..d6da5f89b5 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/VoicemailMessageEventBody.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/VoicemailMessageEventBody.ts @@ -1,6 +1,6 @@ +import type { MessageAttachmentInfo } from './MessageAttachmentInfo'; import type { NotificationRecipientInfo } from './NotificationRecipientInfo'; import type { SenderInfo } from './SenderInfo'; -import type { MessageAttachmentInfo } from './MessageAttachmentInfo'; // Notification payload body export interface VoicemailMessageEventBody { diff --git a/packages/ringcentral-mock/src/platform/interfaces/WirelessPointInfo.ts b/packages/ringcentral-mock/src/platform/interfaces/WirelessPointInfo.ts index 5510b2d784..5f557b439e 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/WirelessPointInfo.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/WirelessPointInfo.ts @@ -1,6 +1,6 @@ import type { AutomaticLocationUpdatesSiteInfo } from './AutomaticLocationUpdatesSiteInfo'; -import type { LocationUpdatesEmergencyAddressInfo } from './LocationUpdatesEmergencyAddressInfo'; import type { ERLLocationInfo } from './ERLLocationInfo'; +import type { LocationUpdatesEmergencyAddressInfo } from './LocationUpdatesEmergencyAddressInfo'; export interface WirelessPointInfo { /** diff --git a/packages/ringcentral-mock/src/platform/interfaces/WirelessPointsList.ts b/packages/ringcentral-mock/src/platform/interfaces/WirelessPointsList.ts index e7dce50ae5..1fb1e3c2fe 100644 --- a/packages/ringcentral-mock/src/platform/interfaces/WirelessPointsList.ts +++ b/packages/ringcentral-mock/src/platform/interfaces/WirelessPointsList.ts @@ -1,6 +1,6 @@ -import type { WirelessPointInfo } from './WirelessPointInfo'; import type { ProvisioningNavigationInfo } from './ProvisioningNavigationInfo'; import type { ProvisioningPagingInfo } from './ProvisioningPagingInfo'; +import type { WirelessPointInfo } from './WirelessPointInfo'; export interface WirelessPointsList { /** diff --git a/packages/ringcentral-mock/src/subscription/PubnubMock.ts b/packages/ringcentral-mock/src/subscription/PubnubMock.ts index 522cf6a224..d6d9dd31a6 100644 --- a/packages/ringcentral-mock/src/subscription/PubnubMock.ts +++ b/packages/ringcentral-mock/src/subscription/PubnubMock.ts @@ -1,4 +1,7 @@ +// @ts-nocheck +// TODO: fix type import { sleep, waitUntilTo } from '@ringcentral-integration/commons/utils'; + import type { SubscriptionMock } from './interface'; const RealPubnub = jest.requireActual('pubnub'); diff --git a/packages/ringcentral-mock/src/subscription/WebSocketMock.ts b/packages/ringcentral-mock/src/subscription/WebSocketMock.ts index b0c04ec9c7..5e1efe063e 100644 --- a/packages/ringcentral-mock/src/subscription/WebSocketMock.ts +++ b/packages/ringcentral-mock/src/subscription/WebSocketMock.ts @@ -1,4 +1,5 @@ /* eslint-disable no-console */ +import { SOCKET_MOCK_URL } from '@ringcentral-integration/test-utils/lib/socketMockUrl'; import { WS } from 'jest-websocket-mock'; import type { Client } from 'mock-socket'; import { @@ -16,17 +17,24 @@ import wsConnectionDetailsBody from '../platform/data/ws/connectionDetails.json' import wsHeartbeatResponse from '../platform/data/ws/heartbeatResponse.json'; import serverNotification from '../platform/data/ws/serverNotification.json'; import wsSubscriptionResponse from '../platform/data/ws/subscriptionResponse.json'; + import type { SubscriptionMock } from './interface'; const subscriptionId = wsSubscriptionResponse[1].id; export class WebSocketMock implements SubscriptionMock { private connected$ = new BehaviorSubject(false); + get connected() { return this.connected$.value; } server?: WS; - constructor(public url = 'ws://whatever') { + constructor( + /** + * add random + */ + public url = SOCKET_MOCK_URL, + ) { this.subscribe(); } diff --git a/packages/ringcentral-mock/src/webphone/Session.ts b/packages/ringcentral-mock/src/webphone/Session.ts index b543319d9b..9f75fb6cef 100644 --- a/packages/ringcentral-mock/src/webphone/Session.ts +++ b/packages/ringcentral-mock/src/webphone/Session.ts @@ -1,6 +1,5 @@ -import { includes } from 'ramda'; -import type { WebPhoneUserAgent } from 'ringcentral-web-phone/lib/userAgent'; - +// @ts-nocheck +// TODO: fix type import type { TelephonyStatus } from '@ringcentral-integration/commons/enums/telephonyStatus'; import { telephonyStatus as telephonyStatuses } from '@ringcentral-integration/commons/enums/telephonyStatus'; import { recordStatus } from '@ringcentral-integration/commons/modules/Webphone/recordStatus'; @@ -9,6 +8,8 @@ import { isConferenceSession, normalizeSession, } from '@ringcentral-integration/commons/modules/Webphone/webphoneHelper'; +import { includes } from 'ramda'; +import type { WebPhoneUserAgent } from 'ringcentral-web-phone/lib/userAgent'; export const CONFERENCE_SESSION_ID = 'Y3MxNzI2MjI1NTQzODI0MzUzM0AxMC43NC4yLjIxOA'; @@ -42,7 +43,22 @@ export const flipFn = jest.fn(); export const startRecordFn = jest.fn(); export const stopRecordFn = jest.fn(); +interface PartyData { + partyId: string; + sessionId: string; +} + export class FakeSession { + static _partyData?: PartyData; + + static setCurrentPartyData(partyData?: PartyData) { + this._partyData = partyData; + } + + static clearCurrentPartyData() { + this.setCurrentPartyData(undefined); + } + id: string; _ua: WebPhoneUserAgent; __rc_callStatus: string; @@ -66,10 +82,7 @@ export class FakeSession { __rc_recordStatus: string; __rc_minimized?: boolean; __rc_lastActiveTime?: number; - __rc_partyData: { - partyId: string; - sessionId: string; - }; + __rc_partyData: PartyData; _events: Record void)[]>; constructor( @@ -128,7 +141,7 @@ export class FakeSession { this.__rc_recordStatus = recordStatus.idle; this.__rc_minimized = undefined; this.__rc_lastActiveTime = undefined; - this.__rc_partyData = { + this.__rc_partyData = FakeSession._partyData ?? { partyId: `cs17262255528361442${partyId}-1`, sessionId: CONFERENCE_SESSION_ID, }; @@ -197,13 +210,14 @@ export class FakeSession { return unmuteFn(this.id); } - async hold() { - await this.trigger('hold'); + hold() { + this.trigger('hold'); this.__rc_callStatus = sessionStatus.onHold; return holdFn(this.id); } unhold() { + this.trigger('unhold'); this.__rc_callStatus = sessionStatus.connected; return unholdFn(this.id); } @@ -253,6 +267,14 @@ export class FakeSession { } isConferenceSession() { - return isConferenceSession(normalizeSession(this)); + return isConferenceSession(normalizeSession(this as any)!); + } + + addTrack() { + return; + } + + removeListener() { + return; } } diff --git a/packages/ringcentral-mock/src/webphone/Webphone.ts b/packages/ringcentral-mock/src/webphone/Webphone.ts index 06908f603d..e47953d120 100644 --- a/packages/ringcentral-mock/src/webphone/Webphone.ts +++ b/packages/ringcentral-mock/src/webphone/Webphone.ts @@ -1,15 +1,26 @@ +import { sessionStatus } from '@ringcentral-integration/commons/modules/Webphone/sessionStatus'; import type { InviteOptions } from 'ringcentral-web-phone/lib/userAgent'; -import { FakeSession as Session } from './Session'; +import { FakeSession } from './Session'; -let webphone: FakeWebphone; +let webphone: FakeWebphone | null = null; export class WebphoneSessionMock { - telephoneSessionId: string; + telephonySessionId: string; + partyId: string; + webphoneSessionId: string; _events: Record void)[]> = {}; - - constructor(telephoneSessionId: string) { - this.telephoneSessionId = telephoneSessionId; + __rc_callStatus: string; + + constructor( + telephonySessionId: string, + partyId: string, + webphoneSessionId: string, + ) { + this.telephonySessionId = telephonySessionId; + this.partyId = partyId; + this.webphoneSessionId = webphoneSessionId; + this.__rc_callStatus = sessionStatus.connecting; } on(event: string, cb: (...args: any) => void) { @@ -19,7 +30,7 @@ export class WebphoneSessionMock { this._events[event].push(cb); } - trigger(event: string, ...args: []) { + trigger(event: string, ...args: any) { if (this._events[event]) { this._events[event].forEach((cb: (...args: any) => void) => { cb(...args); @@ -27,11 +38,13 @@ export class WebphoneSessionMock { } } - removeListener() {} + removeListener() { + // + } get request() { - const pRcApiIdsRaw = `party-id=${this.telephoneSessionId};session-id=${this.telephoneSessionId}`; - const callIDRaw = this.telephoneSessionId; + const pRcApiIdsRaw = `party-id=${this.partyId};session-id=${this.telephonySessionId}`; + const callIDRaw = this.telephonySessionId; const request = { headers: { 'P-Rc-Api-Ids': [{ raw: pRcApiIdsRaw }], @@ -42,7 +55,7 @@ export class WebphoneSessionMock { } get id() { - return this.telephoneSessionId; + return this.webphoneSessionId; } get webphone() { @@ -57,7 +70,35 @@ export class WebphoneSessionMock { this.trigger('terminated'); } - addTrack() {} + addTrack() { + // + } + + reject() { + this.trigger('rejected'); + delete this.webphone?.userAgent.sessions[this.id]; + this.__rc_callStatus = sessionStatus.finished; + } + + toVoicemail() { + this.trigger('rejected'); + this.__rc_callStatus = sessionStatus.finished; + } + + replyWithMessage() { + this.reject(); + this.trigger('terminated'); + } + + hold() { + this.trigger('hold'); + this.__rc_callStatus = sessionStatus.onHold; + } + + unhold() { + this.trigger('unhold'); + this.__rc_callStatus = sessionStatus.connected; + } } class Transport { @@ -67,7 +108,7 @@ class Transport { this._events = {}; } - on(event, cb) { + on(event: string, cb: (...args: any) => void) { this._events[event] = cb; } @@ -77,9 +118,13 @@ class Transport { } } - removeAllListeners() {} + removeAllListeners() { + // + } - disconnect() {} + disconnect() { + // + } isConnected() { return true; @@ -94,15 +139,32 @@ class Transport { } } +class AudioHelper { + setVolume() { + // + } + playIncoming() { + // + } + playOutgoing() { + // + } + loadAudio() { + // + } +} + export class UserAgent { _events: Record void)[]> = {}; transport: Transport; - sessions: Record; + sessions: Record; + audioHelper: AudioHelper; constructor() { this._events = {}; this.transport = new Transport(); this.sessions = {}; + this.audioHelper = new AudioHelper(); } on(event: string, cb: (...args: any) => void) { @@ -119,7 +181,7 @@ export class UserAgent { this._events[event].push(cb); } - trigger(event, ...args: any) { + trigger(event: string, ...args: any) { if (event === 'invite') { this.sessions[args[0].id] = args[0]; } @@ -132,7 +194,7 @@ export class UserAgent { invite(phoneNumber: string, inviteOptions: InviteOptions) { const sessionId = `${phoneNumber}-${Math.random().toString().slice(2, 10)}`; - const session = new Session( + const session = new FakeSession( { id: sessionId, direction: 'Outbound', @@ -140,13 +202,17 @@ export class UserAgent { from: inviteOptions?.fromNumber || '', callId: `call-${sessionId}`, }, - this, + this, // userAgent ); this.sessions[session.id] = session; return session; } - acceptConference(options) { + accept(acceptOptions: any) { + this.trigger('accepted', acceptOptions); + } + + acceptConference(options: any) { Object.keys(this.sessions).forEach((sessionKey) => { if (sessionKey.indexOf('conf') > -1) { this.sessions[sessionKey].accept(options); @@ -172,15 +238,8 @@ export class UserAgent { this._events = {}; } - removeListener() {} - - get audioHelper() { - return { - setVolume() {}, - playIncoming() {}, - playOutgoing() {}, - loadAudio() {}, - }; + removeListener() { + // } isRegistered() { @@ -199,6 +258,7 @@ export class FakeWebphone { constructor() { this._userAgent = new UserAgent(); + // eslint-disable-next-line @typescript-eslint/no-this-alias webphone = this; } diff --git a/packages/ringcentral-mock/test/features/PlatformMock.0.snap.ts b/packages/ringcentral-mock/test/features/PlatformMock.0.snap.ts index d5e7ae4557..a71a70e44d 100644 --- a/packages/ringcentral-mock/test/features/PlatformMock.0.snap.ts +++ b/packages/ringcentral-mock/test/features/PlatformMock.0.snap.ts @@ -6,4 +6,7 @@ fetch('http://example.com/restapi/v1.0/number-parser/parse', { body: JSON.stringify({ originalStrings: ['(165) 1223-4567'] }), method: 'POST', }); -expect(platformMock.fetchMock).toHaveFetchedTimes(1, 'http://example.com/restapi/v1.0/number-parser/parse'); +expect(platformMock.fetchMock).toHaveFetchedTimes( + 1, + 'http://example.com/restapi/v1.0/number-parser/parse', +); diff --git a/packages/ringcentral-mock/test/features/RcMock.0.snap.ts b/packages/ringcentral-mock/test/features/RcMock.0.snap.ts index 72195695c9..94e13ac265 100644 --- a/packages/ringcentral-mock/test/features/RcMock.0.snap.ts +++ b/packages/ringcentral-mock/test/features/RcMock.0.snap.ts @@ -3,4 +3,7 @@ import { RcMock, PubnubMock } from '@ringcentral-integration/mock'; const rcMock = new RcMock({ subscription: new PubnubMock() }); rcMock.init(); fetch('http://example.com/restapi/v1.0/account/~/extension/~/caller-id'); -expect(rcMock.fetchMock).toHaveFetchedTimes(1, 'http://example.com/restapi/v1.0/account/~/extension/~/caller-id'); +expect(rcMock.fetchMock).toHaveFetchedTimes( + 1, + 'http://example.com/restapi/v1.0/account/~/extension/~/caller-id', +); diff --git a/packages/ringcentral-mock/test/features/WebSocketMock/App.tsx b/packages/ringcentral-mock/test/features/WebSocketMock/App.tsx index e610c67042..0599487499 100644 --- a/packages/ringcentral-mock/test/features/WebSocketMock/App.tsx +++ b/packages/ringcentral-mock/test/features/WebSocketMock/App.tsx @@ -1,15 +1,14 @@ -import React, { useEffect } from 'react'; - import RingCentral from '@rc-ex/core'; import RcSdkExtension from '@rc-ex/rcsdk'; import WebSocketExtension from '@rc-ex/ws'; import { RcButton, RcThemeProvider } from '@ringcentral/juno'; import { SDK } from '@ringcentral/sdk'; +import React, { useEffect } from 'react'; const rcSdk = new SDK({ clientId: '', clientSecret: '', - server: 'https://api-xmnup.lab.nordigy.ru', + server: 'https://api-xmrupxmn.intlabs_domain', }); const main = async () => { diff --git a/packages/ringcentral-mock/test/features/WebSocketMock/WebSocketMock.spec.tsx b/packages/ringcentral-mock/test/features/WebSocketMock/WebSocketMock.spec.tsx index cce3a34e48..0bd6ebb42f 100644 --- a/packages/ringcentral-mock/test/features/WebSocketMock/WebSocketMock.spec.tsx +++ b/packages/ringcentral-mock/test/features/WebSocketMock/WebSocketMock.spec.tsx @@ -1,10 +1,8 @@ -import React from 'react'; -import { WS } from 'jest-websocket-mock'; - -import { BehaviorSubject, filter, firstValueFrom } from 'rxjs'; - import { RcMock, WebSocketMock } from '@ringcentral-integration/mock'; import { render } from '@ringcentral-integration/test-utils'; +import { WS } from 'jest-websocket-mock'; +import React from 'react'; +import { BehaviorSubject, filter, firstValueFrom } from 'rxjs'; import { App } from './App'; diff --git a/packages/ringcentral-widgets-cli/templates/Project/package-template.json b/packages/ringcentral-widgets-cli/templates/Project/package-template.json index afa710d358..f72a11b5ed 100644 --- a/packages/ringcentral-widgets-cli/templates/Project/package-template.json +++ b/packages/ringcentral-widgets-cli/templates/Project/package-template.json @@ -4,79 +4,64 @@ "description": "A RingCentral Widgets App", "browserslist": "> 0.25%, ie > 10", "scripts": { - "test": "echo \"Error: no test specified\" && exit 0", + "test": "echo \"Error: no test specified\"", "start": "npm run dev-server", "dev-server": "webpack serve --config webpack-dev-server.config.js", "build": "NODE_ENV=production webpack --config webpack-production.config.js" }, "devDependencies": { "@ringcentral-integration/babel-settings": "^0.2.0", - "@ringcentral-integration/locale-loader": "^2.2.0", - "@types/classnames": "^2.2.7", + "@ringcentral-integration/locale-loader": "^2.2.3", "@types/react-dom": "^17.0.11", "@types/react-redux": "^7.0.9", "@types/react-router": "^5.1.2", - "autoprefixer": "^9.6.1", + "autoprefixer": "^10.4.16", "assert": "^2.0.0", - "babel-loader": "^9.1.2", "browserify": "^14.4.0", "browserify-zlib": "^0.2.0", "buffer": "^6.0.3", - "copy-webpack-plugin": "^9.0.1", - "css-loader": "^2.1.1", + "copy-webpack-plugin": "^11.0.0", "crypto-browserify": "^3.12.0", "console-browserify": "^1.2.0", "constants-browserify": "^1.0.0", "dotenv": "^6.2.0", "domain-browser": "^4.22.0", - "events": "^3.3.0", - "file-loader": "^6.2.0", "https-browserify": "^1.0.0", "os-browserify": "^0.3.0", "path-browserify": "^1.0.1", - "postcss-loader": "^3.0.0", "process": "^0.11.10", "punycode": "^2.1.1", - "querystring-es3": "^0.2.1", - "react-svg-loader": "^3.0.3", - "sass": "^1.43.4", - "sass-loader": "^7.1.0", - "style-loader": "^0.23.1", "stream-browserify": "^3.0.0", "stream-http": "^3.2.0", "string_decoder": "^1.3.0", "string-replace-loader": "^3.0.3", "timers-browserify": "^2.0.12", "tty-browserify": "^0.0.1", - "url-loader": "^4.1.1", "url": "^0.11.0", "util": "^0.12.4", "vm-browserify": "1.1.2", - "webpack": "^5.58.1", - "webpack-cli": "^4.9.0", - "webpack-dev-server": "^4.3.1" + "webpack": "^5.89.0", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^4.15.1" }, "dependencies": { - "@rc-ex/core": "^1.2.1", - "@rc-ex/debug": "^1.0.10", - "@rc-ex/rcsdk": "^1.0.10", - "@rc-ex/ws": "^1.0.10", - "@ringcentral-integration/commons": "^0.14.0", - "@ringcentral-integration/core": "^0.14.0", + "@rc-ex/core": "^1.3.3", + "@rc-ex/debug": "^1.1.3", + "@rc-ex/rcsdk": "^1.1.3", + "@rc-ex/ws": "^1.1.3", + "@ringcentral-integration/commons": "^0.15.0", + "@ringcentral-integration/core": "^0.15.0", "@ringcentral-integration/i18n": "^2.2.0", "@ringcentral-integration/phone-number": "^1.1.0", - "@ringcentral-integration/widgets": "^0.14.0", - "@ringcentral-integration/utils": "^0.14.0", - "@ringcentral/juno": "^2.35.2", - "@ringcentral/juno-icon": "^1.43.0", + "@ringcentral-integration/widgets": "^0.15.0", + "@ringcentral-integration/utils": "^0.15.0", + "@ringcentral/juno": "^2.42.0", + "@ringcentral/juno-icon": "^1.76.0", "@ringcentral/sdk": "^4.7.2", "@ringcentral/subscriptions": "^4.7.2", "dayjs": "^1.11.7", - "classnames": "^2.2.5", - "isomorphic-fetch": "^2.2.1", "normalize.css": "^8.0.1", "prop-types": "^15.7.2", - "qs": "^6.8.0", "react": "^17.0.2", "react-dom": "^17.0.2", "react-redux": "^5.1.1", diff --git a/packages/ringcentral-widgets-cli/templates/Project/src/modules/Phone/index.js b/packages/ringcentral-widgets-cli/templates/Project/src/modules/Phone/index.js index 065af23486..8553efe9a4 100644 --- a/packages/ringcentral-widgets-cli/templates/Project/src/modules/Phone/index.js +++ b/packages/ringcentral-widgets-cli/templates/Project/src/modules/Phone/index.js @@ -65,7 +65,7 @@ import { SettingsUI } from '@ringcentral-integration/widgets/modules/SettingsUI' }, }, { provide: 'RateLimiter', useClass: RateLimiter }, - { provide: 'TabManager', useClass: TabManager}, + { provide: 'TabManager', useClass: TabManager }, { provide: 'RingCentralExtensions', useClass: RingCentralExtensions }, { provide: 'RingCentralExtensionsOptions', diff --git a/packages/ringcentral-widgets-cli/templates/Project/webpack-dev-server.config.js b/packages/ringcentral-widgets-cli/templates/Project/webpack-dev-server.config.js index 759602ce72..756eb499f8 100644 --- a/packages/ringcentral-widgets-cli/templates/Project/webpack-dev-server.config.js +++ b/packages/ringcentral-widgets-cli/templates/Project/webpack-dev-server.config.js @@ -1,5 +1,5 @@ -const webpack = require('webpack'); const path = require('path'); +const webpack = require('webpack'); const autoprefixer = require('autoprefixer'); const dotenv = require('dotenv'); const packageConfig = require('./package'); @@ -40,29 +40,10 @@ const config = { // more doc: https://webpack.js.org/configuration/resolve/#resolvefallback // fallback: { + vm: require.resolve('vm-browserify'), crypto: require.resolve('crypto-browserify'), stream: require.resolve('stream-browserify'), - vm: require.resolve('vm-browserify'), - timers: require.resolve('timers-browserify'), - process: require.resolve('process/browser'), - assert: require.resolve('assert'), - buffer: require.resolve('buffer'), - console: require.resolve('console-browserify'), - constants: require.resolve('constants-browserify'), - domain: require.resolve('domain-browser'), events: require.resolve('events'), - http: require.resolve('stream-http'), - https: require.resolve('https-browserify'), - os: require.resolve('os-browserify/browser'), - path: require.resolve('path-browserify'), - punycode: require.resolve('punycode'), - querystring: require.resolve('querystring-es3'), - string_decoder: require.resolve('string_decoder'), - sys: require.resolve('util'), - tty: require.resolve('tty-browserify'), - url: require.resolve('url'), - util: require.resolve('util'), - zlib: require.resolve('browserify-zlib'), }, extensions: ['.js', '.jsx', '.ts', '.tsx'], }, diff --git a/packages/ringcentral-widgets-cli/templates/Project/webpack-production.config.js b/packages/ringcentral-widgets-cli/templates/Project/webpack-production.config.js index b229aa42b6..4d810f3e5a 100644 --- a/packages/ringcentral-widgets-cli/templates/Project/webpack-production.config.js +++ b/packages/ringcentral-widgets-cli/templates/Project/webpack-production.config.js @@ -1,5 +1,5 @@ -const webpack = require('webpack'); const path = require('path'); +const webpack = require('webpack'); const dotenv = require('dotenv'); const autoprefixer = require('autoprefixer'); const CopyWebpackPlugin = require('copy-webpack-plugin'); @@ -35,29 +35,10 @@ const config = { // more doc: https://webpack.js.org/configuration/resolve/#resolvefallback // fallback: { + vm: require.resolve('vm-browserify'), crypto: require.resolve('crypto-browserify'), stream: require.resolve('stream-browserify'), - vm: require.resolve('vm-browserify'), - timers: require.resolve('timers-browserify'), - process: require.resolve('process/browser'), - assert: require.resolve('assert'), - buffer: require.resolve('buffer'), - console: require.resolve('console-browserify'), - constants: require.resolve('constants-browserify'), - domain: require.resolve('domain-browser'), events: require.resolve('events'), - http: require.resolve('stream-http'), - https: require.resolve('https-browserify'), - os: require.resolve('os-browserify/browser'), - path: require.resolve('path-browserify'), - punycode: require.resolve('punycode'), - querystring: require.resolve('querystring-es3'), - string_decoder: require.resolve('string_decoder'), - sys: require.resolve('util'), - tty: require.resolve('tty-browserify'), - url: require.resolve('url'), - util: require.resolve('util'), - zlib: require.resolve('browserify-zlib'), }, extensions: ['.js', '.jsx', '.ts', '.tsx'], }, diff --git a/packages/ringcentral-widgets-demo/demo-extension/background.html b/packages/ringcentral-widgets-demo/browser-extension/background.html similarity index 100% rename from packages/ringcentral-widgets-demo/demo-extension/background.html rename to packages/ringcentral-widgets-demo/browser-extension/background.html diff --git a/packages/ringcentral-widgets-demo/demo-extension/background.js b/packages/ringcentral-widgets-demo/browser-extension/background.js similarity index 98% rename from packages/ringcentral-widgets-demo/demo-extension/background.js rename to packages/ringcentral-widgets-demo/browser-extension/background.js index 908f755e85..f72bd233b1 100644 --- a/packages/ringcentral-widgets-demo/demo-extension/background.js +++ b/packages/ringcentral-widgets-demo/browser-extension/background.js @@ -16,7 +16,6 @@ const ProxyServer = getProxyServer(Phone); const server = new ProxyServer({ transport, useTabManager: false, - extensionMode: true, apiConfig, brandConfig, prefix, diff --git a/packages/ringcentral-widgets-demo/demo-extension/client.html b/packages/ringcentral-widgets-demo/browser-extension/client.html similarity index 100% rename from packages/ringcentral-widgets-demo/demo-extension/client.html rename to packages/ringcentral-widgets-demo/browser-extension/client.html diff --git a/packages/ringcentral-widgets-demo/demo-extension/client.js b/packages/ringcentral-widgets-demo/browser-extension/client.js similarity index 98% rename from packages/ringcentral-widgets-demo/demo-extension/client.js rename to packages/ringcentral-widgets-demo/browser-extension/client.js index 734ebff8da..a44eb31748 100644 --- a/packages/ringcentral-widgets-demo/demo-extension/client.js +++ b/packages/ringcentral-widgets-demo/browser-extension/client.js @@ -16,7 +16,6 @@ const ProxyServer = getProxyClient(Phone); const client = new ProxyServer({ transport, useTabManager: false, - extensionMode: true, apiConfig, brandConfig, prefix, diff --git a/packages/ringcentral-widgets-demo/demo-extension/manifest.json b/packages/ringcentral-widgets-demo/browser-extension/manifest.json similarity index 100% rename from packages/ringcentral-widgets-demo/demo-extension/manifest.json rename to packages/ringcentral-widgets-demo/browser-extension/manifest.json diff --git a/packages/ringcentral-widgets-demo/browser-extension/redirect.html b/packages/ringcentral-widgets-demo/browser-extension/redirect.html new file mode 100644 index 0000000000..01ca540c7b --- /dev/null +++ b/packages/ringcentral-widgets-demo/browser-extension/redirect.html @@ -0,0 +1,19 @@ + + + + + + + + + + + + + Redirect + + +
+ + + diff --git a/packages/ringcentral-widgets-demo/browser-extension/redirect.ts b/packages/ringcentral-widgets-demo/browser-extension/redirect.ts new file mode 100644 index 0000000000..122bca36a1 --- /dev/null +++ b/packages/ringcentral-widgets-demo/browser-extension/redirect.ts @@ -0,0 +1 @@ +import '@ringcentral-integration/widgets/lib/oAuthRedirect'; diff --git a/packages/ringcentral-widgets-demo/browser-extension/webpack.config.js b/packages/ringcentral-widgets-demo/browser-extension/webpack.config.js new file mode 100644 index 0000000000..b44a0e719f --- /dev/null +++ b/packages/ringcentral-widgets-demo/browser-extension/webpack.config.js @@ -0,0 +1,63 @@ +import { getBaseWebpackConfig } from '@ringcentral-integration/widgets/lib/getBaseWebpackConfig'; +import CopyWebpackPlugin from 'copy-webpack-plugin'; +import path from 'path'; +import { DefinePlugin, ProvidePlugin } from 'webpack'; +import { merge } from 'webpack-merge'; + +export const getWebpackConfig = ({ mode }) => { + const baseConfig = getBaseWebpackConfig({ + mode, + themeFolder: __dirname, + }); + return merge(baseConfig, { + plugins: [ + // TODO: use @babel/plugin-transform-react-jsx + new ProvidePlugin({ + React: 'react', + }), + // new WebpackCommandPlugin({ + // command: 'yarn workspace @ringcentral-integration/brand-config start', + // }), + new CopyWebpackPlugin({ + patterns: [ + { + from: 'browser-extension/client.html', + to: path.join(__dirname, 'build'), + force: true, + }, + { + from: 'browser-extension/background.html', + to: path.join(__dirname, 'build'), + force: true, + }, + { + from: 'browser-extension/redirect.html', + to: path.join(__dirname, 'build'), + force: true, + }, + { + from: 'browser-extension/manifest.json', + to: path.join(__dirname, 'build'), + force: true, + }, + ], + }), + new DefinePlugin({ + 'process.env.NODE_ENV': JSON.stringify('development'), + }), + ], + entry: { + background: [path.resolve(__dirname, 'background')], + client: [path.resolve(__dirname, 'client')], + redirect: [path.resolve(__dirname, 'redirect')], + }, + output: { + path: path.resolve(__dirname, './build'), + filename: '[name].js', + publicPath: '/', + clean: true, + }, + }); +}; + +export const port = 9090; diff --git a/packages/ringcentral-widgets-demo/demo-extension/proxy.html b/packages/ringcentral-widgets-demo/demo-extension/proxy.html deleted file mode 100644 index 5d28bb606d..0000000000 --- a/packages/ringcentral-widgets-demo/demo-extension/proxy.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - Proxy - - -
- - - diff --git a/packages/ringcentral-widgets-demo/demo-extension/proxy.js b/packages/ringcentral-widgets-demo/demo-extension/proxy.js deleted file mode 100644 index bfd09c6fe2..0000000000 --- a/packages/ringcentral-widgets-demo/demo-extension/proxy.js +++ /dev/null @@ -1,6 +0,0 @@ -import ProxyFrameController from '@ringcentral-integration/commons/lib/ProxyFrameController'; -import prefix from '../dev-server/prefix'; - -export default new ProxyFrameController({ - prefix, -}); diff --git a/packages/ringcentral-widgets-demo/demo-extension/webpack.config.js b/packages/ringcentral-widgets-demo/demo-extension/webpack.config.js deleted file mode 100644 index 2b4d966fde..0000000000 --- a/packages/ringcentral-widgets-demo/demo-extension/webpack.config.js +++ /dev/null @@ -1,122 +0,0 @@ -import path from 'path'; -import autoprefixer from 'autoprefixer'; -import webpack from 'webpack'; - -const base = { - module: { - rules: [ - { - enforce: 'pre', - test: /\.js$/, - use: 'source-map-loader', - }, - { - test: /\.js$/, - use: ['babel-loader', '@ringcentral-integration/locale-loader'], - exclude: /node_modules/, - }, - { - test: /\.css$/i, - use: ['style-loader', 'css-loader'], - }, - { - test: /\.woff|\.woff2|.eot|\.ttf/, - use: 'url-loader?limit=15000&publicPath=./&name=fonts/[name]_[hash].[ext]', - }, - { - test: /\.svg/, - exclude: /node_modules|font/, - use: [ - 'babel-loader', - { - loader: 'react-svg-loader', - options: { - jsx: true, - svgo: { - plugins: [ - { - removeViewBox: false, - }, - ], - }, - }, - }, - ], - }, - { - test: /\.png|\.jpg|\.gif|\.svg/, - use: 'url-loader?limit=20000&publicPath=./&name=images/[name]_[hash].[ext]', - exclude: [ - /assets(\/|\\)images(\/|\\).+\.svg/, - /dev-server(\/|\\).+\.svg/, - ], - }, - { - test: /\.sass|\.scss/, - use: [ - 'style-loader', - 'css-loader?modules&localIdentName=[path]_[name]_[local]_[hash:base64:5]', - { - loader: 'postcss-loader', - options: { - plugins: () => [autoprefixer], - }, - }, - { - loader: 'sass-loader', - options: { - includePaths: [ - __dirname, - path.resolve(__dirname, '../packages/ringcentral-widgets'), - path.resolve(__dirname, '../node_modules'), - ], - outputStyle: 'expanded', - }, - }, - ], - }, - { - test: /\.ogg$/, - use: 'file-loader?publicPath=./&name=audio/[name]_[hash].[ext]', - }, - ], - }, - devtool: 'inline-source-map', - plugins: [ - new webpack.DefinePlugin({ - 'process.env': { - NODE_ENV: JSON.stringify('development'), - }, - }), - ], -}; - -const config = [ - { - ...base, - resolve: { - alias: { - '@ringcentral-integration/commons': path.resolve( - __dirname, - '../packages/ringcentral-integration', - ), - '@ringcentral-integration/widgets': path.resolve( - __dirname, - '../packages/ringcentral-widgets', - ), - }, - }, - entry: { - background: [path.resolve(__dirname, 'background')], - client: [path.resolve(__dirname, 'client')], - proxy: [path.resolve(__dirname, 'proxy')], - }, - output: { - path: 'demo-extension-build', - filename: '[name].js', - publicPath: '/', - }, - }, -]; - -export default config; diff --git a/packages/ringcentral-widgets-demo/dev-server/Phone.js b/packages/ringcentral-widgets-demo/dev-server/Phone.js index 9f824e1154..99007b2615 100644 --- a/packages/ringcentral-widgets-demo/dev-server/Phone.js +++ b/packages/ringcentral-widgets-demo/dev-server/Phone.js @@ -1,10 +1,10 @@ -import callDirections from '@ringcentral-integration/commons/enums/callDirections'; -import { ModuleFactory } from '@ringcentral-integration/commons/lib/di'; +import { callDirection } from '@ringcentral-integration/commons/enums/callDirections'; import { LocalForageStorage } from '@ringcentral-integration/commons/lib/LocalForageStorage'; -import { normalizeNumber } from '@ringcentral-integration/commons/lib/normalizeNumber'; import RcModule from '@ringcentral-integration/commons/lib/RcModule'; import { RingCentralClient } from '@ringcentral-integration/commons/lib/RingCentralClient'; import '@ringcentral-integration/commons/lib/TabFreezePrevention'; +import { ModuleFactory } from '@ringcentral-integration/commons/lib/di'; +import { normalizeNumber } from '@ringcentral-integration/commons/lib/normalizeNumber'; import { AccountContacts } from '@ringcentral-integration/commons/modules/AccountContacts'; import { AccountInfo } from '@ringcentral-integration/commons/modules/AccountInfo'; import { ActiveCallControl } from '@ringcentral-integration/commons/modules/ActiveCallControl'; @@ -17,16 +17,16 @@ import { AudioSettings } from '@ringcentral-integration/commons/modules/AudioSet import { Auth } from '@ringcentral-integration/commons/modules/Auth'; import { AvailabilityMonitor } from '@ringcentral-integration/commons/modules/AvailabilityMonitor'; import { Brand } from '@ringcentral-integration/commons/modules/Brand'; -import { CallerId } from '@ringcentral-integration/commons/modules/CallerId'; +import { Call } from '@ringcentral-integration/commons/modules/Call'; import { CallHistory } from '@ringcentral-integration/commons/modules/CallHistory'; +import { CallLog } from '@ringcentral-integration/commons/modules/CallLog'; +import { CallMonitor } from '@ringcentral-integration/commons/modules/CallMonitor'; +import { CallerId } from '@ringcentral-integration/commons/modules/CallerId'; import { CallingSettings, callingModes, callingOptions, } from '@ringcentral-integration/commons/modules/CallingSettings'; -import { CallLog } from '@ringcentral-integration/commons/modules/CallLog'; -import { CallMonitor } from '@ringcentral-integration/commons/modules/CallMonitor'; -import { Call } from '@ringcentral-integration/commons/modules/Call'; import { CompanyContacts } from '@ringcentral-integration/commons/modules/CompanyContacts'; import { ComposeText } from '@ringcentral-integration/commons/modules/ComposeText'; import { ConferenceCall } from '@ringcentral-integration/commons/modules/ConferenceCall'; @@ -45,11 +45,11 @@ import { ExtensionInfo } from '@ringcentral-integration/commons/modules/Extensio import { ExtensionPhoneNumber } from '@ringcentral-integration/commons/modules/ExtensionPhoneNumber'; import { Feedback } from '@ringcentral-integration/commons/modules/Feedback'; import { ForwardingNumber } from '@ringcentral-integration/commons/modules/ForwardingNumber'; +import { GenericMeeting } from '@ringcentral-integration/commons/modules/GenericMeeting'; import { GlobalStorage } from '@ringcentral-integration/commons/modules/GlobalStorage'; import { Locale } from '@ringcentral-integration/commons/modules/Locale'; import { LocaleSettings } from '@ringcentral-integration/commons/modules/LocaleSettings'; import { Meeting } from '@ringcentral-integration/commons/modules/Meeting'; -import { RcVideo } from '@ringcentral-integration/commons/modules/RcVideo'; import { MessageSender } from '@ringcentral-integration/commons/modules/MessageSender'; import { MessageStore } from '@ringcentral-integration/commons/modules/MessageStore'; import { NumberValidate } from '@ringcentral-integration/commons/modules/NumberValidate'; @@ -57,6 +57,7 @@ import { Presence } from '@ringcentral-integration/commons/modules/Presence'; // import ConferenceCall from '@ringcentral-integration/commons/modules/ConferenceCall'; import { QuickAccess } from '@ringcentral-integration/commons/modules/QuickAccess'; import { RateLimiter } from '@ringcentral-integration/commons/modules/RateLimiter'; +import { RcVideo } from '@ringcentral-integration/commons/modules/RcVideo'; import { RecentCalls } from '@ringcentral-integration/commons/modules/RecentCalls'; import { RecentMessages } from '@ringcentral-integration/commons/modules/RecentMessages'; import { RegionSettings } from '@ringcentral-integration/commons/modules/RegionSettings'; @@ -72,11 +73,11 @@ import { import { Storage } from '@ringcentral-integration/commons/modules/Storage'; import { Subscription } from '@ringcentral-integration/commons/modules/Subscription'; import { TabManager } from '@ringcentral-integration/commons/modules/TabManager'; +import { ExtensionNumberAreaCode } from '@ringcentral-integration/commons/modules/ExtensionNumberAreaCode'; import { TierChecker } from '@ringcentral-integration/commons/modules/TierChecker'; import { UserGuide } from '@ringcentral-integration/commons/modules/UserGuide'; import { VideoConfiguration } from '@ringcentral-integration/commons/modules/VideoConfiguration'; import { Webphone } from '@ringcentral-integration/commons/modules/Webphone'; -import { GenericMeeting } from '@ringcentral-integration/commons/modules/GenericMeeting'; import hasActiveCalls from '@ringcentral-integration/widgets/lib/hasActiveCalls'; import { ActiveCallsUI } from '@ringcentral-integration/widgets/modules/ActiveCallsUI'; import { AlertUI } from '@ringcentral-integration/widgets/modules/AlertUI'; @@ -94,12 +95,13 @@ import { ConnectivityBadgeUI } from '@ringcentral-integration/widgets/modules/Co import { ConnectivityManager } from '@ringcentral-integration/widgets/modules/ConnectivityManager'; import { ContactDetailsUI } from '@ringcentral-integration/widgets/modules/ContactDetailsUI'; import { ContactListUI } from '@ringcentral-integration/widgets/modules/ContactListUI'; -import { ConversationsUI } from '@ringcentral-integration/widgets/modules/ConversationsUI'; import { ConversationUI } from '@ringcentral-integration/widgets/modules/ConversationUI'; +import { ConversationsUI } from '@ringcentral-integration/widgets/modules/ConversationsUI'; import { DialerAndCallsTabUI } from '@ringcentral-integration/widgets/modules/DialerAndCallsTabUI'; import { DialerUI } from '@ringcentral-integration/widgets/modules/DialerUI'; import { FeedbackUI } from '@ringcentral-integration/widgets/modules/FeedbackUI'; import { FlipUI } from '@ringcentral-integration/widgets/modules/FlipUI'; +import { GenericMeetingUI } from '@ringcentral-integration/widgets/modules/GenericMeetingUI'; import { IncomingCallUI } from '@ringcentral-integration/widgets/modules/IncomingCallUI'; import { LoginUI } from '@ringcentral-integration/widgets/modules/LoginUI'; import { ModalUI } from '@ringcentral-integration/widgets/modules/ModalUI'; @@ -111,7 +113,6 @@ import { SettingsUI } from '@ringcentral-integration/widgets/modules/SettingsUI' import { SimpleCallControlUI } from '@ringcentral-integration/widgets/modules/SimpleCallControlUI'; import { TransferUI } from '@ringcentral-integration/widgets/modules/TransferUI'; import { UserGuideUI } from '@ringcentral-integration/widgets/modules/UserGuideUI'; -import { GenericMeetingUI } from '@ringcentral-integration/widgets/modules/GenericMeetingUI'; import { SDK } from '@ringcentral/sdk'; import { hashHistory } from 'react-router'; import url from 'url'; @@ -207,6 +208,7 @@ const history = { provide: 'Meeting', useClass: Meeting }, { provide: 'VideoConfiguration', useClass: VideoConfiguration }, { provide: 'Webphone', useClass: Webphone }, + { provide: 'ExtensionNumberAreaCode', useClass: ExtensionNumberAreaCode }, { provide: 'ContactSearch', useClass: ContactSearch }, { provide: 'CallMonitor', useClass: CallMonitor }, { provide: 'DialerUI', useClass: DialerUI }, @@ -239,6 +241,9 @@ const history = provide: 'RcVideoOptions', useValue: { showSaveAsDefault: false, + enableInvitationApi: true, + enableInvitationBridgesApi: true, + enableInvitationApiFailedToast: true, }, }, { @@ -282,8 +287,9 @@ const history = { provide: 'EnvironmentOptions', useValue: { + useDataTrackingSetting: true, defaultRecordingHost: - 'https://apps.ringcentral.com/integrations/recording/dev/rc/index.html', + 'https://cdn.integration.ringcentral.com/integration/recording/dev/rc/index.html', }, }, // { @@ -312,14 +318,6 @@ const history = provide: 'Analytics', useClass: Analytics, }, - { - provide: 'AnalyticsOptions', - useValue: { - analyticsKey: '', - appVersion: '', - useLog: true, - }, - }, { provide: 'ExtensionInfoOptions', useValue: { @@ -445,7 +443,7 @@ export default class BasePhone extends RcModule { }); webphone.onCallStart((session) => { - if (session.direction === callDirections.outbound) { + if (session.direction === callDirection.outbound) { return; } const path = `/calls/active/${session.id}`; @@ -604,6 +602,7 @@ export function createPhone({ apiConfig, brandConfig, clientService, + analyticsKey = '', enableDiscovery = true, }) { @ModuleFactory({ @@ -614,6 +613,15 @@ export function createPhone({ new RingCentralClient(clientService || new SDK(sdkConfig)), deps: [{ dep: 'SdkConfig', useParam: true }], }, + { + provide: 'AnalyticsOptions', + useValue: { + analyticsKey, + appVersion: '', + useLog: true, + enableMixpanel: true, + }, + }, { provide: 'SdkConfig', useValue: { @@ -627,7 +635,7 @@ export function createPhone({ clearCacheOnRefreshError: false, clientId: apiConfig.clientId || apiConfig.appKey, clientSecret: apiConfig.clientSecret || apiConfig.appSecret, - redirectUri: url.resolve(window.location.href, './redirect.html'), + redirectUri: new URL('./redirect.html', window.location.href).href, }, }, { diff --git a/packages/ringcentral-widgets-demo/dev-server/containers/App/index.tsx b/packages/ringcentral-widgets-demo/dev-server/containers/App/index.tsx index fcca17671b..320a154f69 100644 --- a/packages/ringcentral-widgets-demo/dev-server/containers/App/index.tsx +++ b/packages/ringcentral-widgets-demo/dev-server/containers/App/index.tsx @@ -1,6 +1,6 @@ import { sleep } from '@ringcentral-integration/commons/utils'; import { ContactSourceFilter } from '@ringcentral-integration/widgets/components/ContactSourceFilter'; -import MeetingScheduleButton from '@ringcentral-integration/widgets/components/MeetingScheduleButton'; +import { GenericMeetingScheduleButton } from '@ringcentral-integration/widgets/components/GenericMeetingScheduleButton'; import ActiveCallsPage from '@ringcentral-integration/widgets/containers/ActiveCallsPage'; import AlertContainer from '@ringcentral-integration/widgets/containers/AlertContainer'; import AudioSettingsPage from '@ringcentral-integration/widgets/containers/AudioSettingsPage'; @@ -21,9 +21,9 @@ import { DialerAndCallsTabContainer } from '@ringcentral-integration/widgets/con import DialerPage from '@ringcentral-integration/widgets/containers/DialerPage'; import { FeedbackPage } from '@ringcentral-integration/widgets/containers/FeedbackPage'; import FlipPage from '@ringcentral-integration/widgets/containers/FlipPage'; +import GenericMeetingPage from '@ringcentral-integration/widgets/containers/GenericMeetingPage'; import { IncomingCallContainer } from '@ringcentral-integration/widgets/containers/IncomingCallContainer'; import { LoginPage } from '@ringcentral-integration/widgets/containers/LoginPage'; -import GenericMeetingPage from '@ringcentral-integration/widgets/containers/GenericMeetingPage'; import { ModalContainer } from '@ringcentral-integration/widgets/containers/ModalContainer'; import { RecentActivityContainer } from '@ringcentral-integration/widgets/containers/RecentActivityContainer'; import { RegionSettingsPage } from '@ringcentral-integration/widgets/containers/RegionSettingsPage'; @@ -36,6 +36,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { Provider } from 'react-redux'; import { Route, Router } from 'react-router'; + import AppView from '../AppView'; import MainView from '../MainView'; @@ -283,7 +284,15 @@ const App = ({ phone, icon }) => { ( - + ( + + )} + /> )} /> { +const AppView = ({ + children, + server, + enabled, + onSetData, + allowDataTracking, + useDataTrackingSetting, +}) => { return (
{children} @@ -16,6 +21,8 @@ const AppView = ({ children, server, enabled, onSetData }) => { server={server} enabled={enabled} onSetData={onSetData} + allowDataTracking={allowDataTracking} + useDataTrackingSetting={useDataTrackingSetting} recordingHost="" />
@@ -26,6 +33,8 @@ AppView.propTypes = { children: PropTypes.node, server: PropTypes.string, enabled: PropTypes.bool, + allowDataTracking: PropTypes.bool, + useDataTrackingSetting: PropTypes.bool, onSetData: PropTypes.func, }; @@ -33,6 +42,8 @@ AppView.defaultProps = { children: null, server: null, enabled: false, + allowDataTracking: false, + useDataTrackingSetting: false, onSetData: undefined, }; @@ -41,6 +52,8 @@ export default withPhone( (state, { phone: { environment } }) => ({ server: environment.server, enabled: environment.enabled, + allowDataTracking: environment.allowDataTracking, + useDataTrackingSetting: environment.useDataTrackingSetting, }), (dispatch, { phone: { environment } }) => ({ onSetData(options) { diff --git a/packages/ringcentral-widgets-demo/dev-server/containers/MainView/i18n/en-US.ts b/packages/ringcentral-widgets-demo/dev-server/containers/MainView/i18n/en-US.ts index 07224f440b..240567111d 100644 --- a/packages/ringcentral-widgets-demo/dev-server/containers/MainView/i18n/en-US.ts +++ b/packages/ringcentral-widgets-demo/dev-server/containers/MainView/i18n/en-US.ts @@ -9,4 +9,4 @@ export default { meetingLabel: 'Schedule Meeting', conferenceLabel: 'Schedule Conference', settingsLabel: 'Settings', -}; +} as const; diff --git a/packages/ringcentral-widgets-demo/dev-server/containers/MainView/i18n/index.ts b/packages/ringcentral-widgets-demo/dev-server/containers/MainView/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/ringcentral-widgets-demo/dev-server/containers/MainView/i18n/index.ts +++ b/packages/ringcentral-widgets-demo/dev-server/containers/MainView/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/ringcentral-widgets-demo/dev-server/containers/MainView/index.js b/packages/ringcentral-widgets-demo/dev-server/containers/MainView/index.js index 0b67198297..63403c2b07 100644 --- a/packages/ringcentral-widgets-demo/dev-server/containers/MainView/index.js +++ b/packages/ringcentral-widgets-demo/dev-server/containers/MainView/index.js @@ -1,4 +1,3 @@ -/* eslint-disable react/prop-types */ import { callingOptions } from '@ringcentral-integration/commons/modules/CallingSettings'; import HistoryIcon from '@ringcentral-integration/widgets/assets/images/CallHistory.svg'; import HistoryHoverIcon from '@ringcentral-integration/widgets/assets/images/CallHistoryHover.svg'; @@ -24,6 +23,7 @@ import hasActiveCalls from '@ringcentral-integration/widgets/lib/hasActiveCalls' import { withPhone } from '@ringcentral-integration/widgets/lib/phoneContext'; import React from 'react'; import { connect } from 'react-redux'; + import i18n from './i18n'; function getTabs({ @@ -46,6 +46,7 @@ function getTabs({ isActive: (currentPath) => currentPath === '/dialer' || (currentPath === '/calls' && conferenceCallEquipped), + dataSign: 'dialerTab', }, showCalls && { icon: CallsIcon, @@ -54,12 +55,14 @@ function getTabs({ path: '/calls', isActive: (currentPath) => currentPath === '/calls' || currentPath === '/calls/active', + dataSign: 'callsTab', }, showHistory && { icon: HistoryIcon, activeIcon: HistoryHoverIcon, label: i18n.getString('historyLabel', currentLocale), path: '/history', + dataSign: 'historyTab', }, showMessages && { icon: MessageIcon, @@ -70,6 +73,7 @@ function getTabs({ isActive: (currentPath) => currentPath === '/messages' || currentPath.indexOf('/conversations/') !== -1, + dataSign: 'messagesTab', }, showContact && { icon: ContactIcon, @@ -78,6 +82,7 @@ function getTabs({ label: i18n.getString('contactsLabel', currentLocale), path: '/contacts', isActive: (currentPath) => currentPath.substr(0, 9) === '/contacts', + dataSign: 'contactsTab', }, showMeeting && { icon: MeetingIcon, @@ -85,6 +90,7 @@ function getTabs({ moreMenuIcon: MeetingNavIcon, label: i18n.getString('meetingLabel', currentLocale), path: '/meeting', + dataSign: 'meetingTab', }, { icon: SettingsIcon, @@ -93,6 +99,7 @@ function getTabs({ label: i18n.getString('settingsLabel', currentLocale), path: '/settings', isActive: (currentPath) => currentPath.substr(0, 9) === '/settings', + dataSign: 'settingsTab', }, ].filter((x) => !!x); if (tabs.length > 5) { @@ -130,6 +137,7 @@ function getTabs({ isActive: (currentPath, currentVirtualPath) => currentVirtualPath === '!moreMenu', childTabs, + dataSign: 'moreMenu', }); } return tabs; @@ -144,7 +152,6 @@ function mapToProps( appFeatures, routerInteraction, callingSettings, - conference, conferenceCall, }, }, diff --git a/packages/ringcentral-widgets-demo/dev-server/index.html b/packages/ringcentral-widgets-demo/dev-server/index.html index d5133323ae..6d9046ba9e 100644 --- a/packages/ringcentral-widgets-demo/dev-server/index.html +++ b/packages/ringcentral-widgets-demo/dev-server/index.html @@ -11,11 +11,6 @@ RingCentral JS Widget -
diff --git a/packages/ringcentral-widgets-demo/dev-server/webpack.config.ts b/packages/ringcentral-widgets-demo/dev-server/webpack.config.ts index cbfe885b5c..4a0bc31660 100644 --- a/packages/ringcentral-widgets-demo/dev-server/webpack.config.ts +++ b/packages/ringcentral-widgets-demo/dev-server/webpack.config.ts @@ -1,5 +1,5 @@ -import path from 'path'; import { getBaseWebpackConfig } from '@ringcentral-integration/widgets/lib/getBaseWebpackConfig'; +import path from 'path'; export const devServerConfig = { ...getBaseWebpackConfig({ mode: 'development', themeFolder: __dirname }), diff --git a/packages/ringcentral-widgets-demo/gulpfile.js b/packages/ringcentral-widgets-demo/gulpfile.js index 3e4fac20b3..bb9c61afea 100644 --- a/packages/ringcentral-widgets-demo/gulpfile.js +++ b/packages/ringcentral-widgets-demo/gulpfile.js @@ -1,11 +1,14 @@ import path from 'path'; -import gulp from 'gulp'; import fs from 'fs-extra'; import webpack from 'webpack'; import WebpackDevServer from 'webpack-dev-server'; import yargs from 'yargs'; import { devServerConfig, port } from './dev-server/webpack.config'; -import demoExtensionConfig from './demo-extension/webpack.config'; + +import { + getWebpackConfig as getWebpackExtConfig, + port as extPort, +} from './browser-extension/webpack.config'; const { argv: { file }, @@ -30,6 +33,13 @@ export async function devServer() { publicPath: '/', }, port, + client: { + overlay: { + errors: true, + warnings: false, + runtimeErrors: true, + }, + }, }, compiler, ); @@ -37,31 +47,58 @@ export async function devServer() { console.log(`server listening to ${port}...`); } -export function demoExtensionClean() { - return fs.remove('demo-extension-build'); -} - -export function demoExtensionWebpack() { - return new Promise((resolve, reject) => { - webpack(demoExtensionConfig, (err, stats) => { - if (err || stats.hasErrors()) { - reject(err || new Error(stats.toJson().errors)); - return; - } - resolve(); - }); +export async function devExtensionServer() { + const devExtensionServerConfig = getWebpackExtConfig({ + mode: 'development', }); -} -export function demoExtensionCopy() { - return gulp - .src(['demo-extension/**/*', '!demo-extension/**/*.js']) - .pipe(gulp.dest('demo-extension-build')); -} + const excludeEntriesToHotReload = ['background']; + + for (const entryName in devExtensionServerConfig.entry) { + if (excludeEntriesToHotReload.indexOf(entryName) === -1) { + devExtensionServerConfig.entry[entryName] = [ + 'webpack/hot/dev-server', + `webpack-dev-server/client?hot=true&hostname=localhost&port=${extPort}`, + ].concat(devExtensionServerConfig.entry[entryName]); + } + } + + devExtensionServerConfig.plugins = [ + new webpack.HotModuleReplacementPlugin(), + ].concat(devExtensionServerConfig.plugins || []); + + devExtensionServerConfig.devtool = 'cheap-module-source-map'; + + const compiler = webpack(devExtensionServerConfig); + + const server = new WebpackDevServer( + { + https: false, + hot: false, + client: false, + host: 'localhost', + port: extPort, + static: { + directory: path.join(__dirname, '../build'), + }, + devMiddleware: { + publicPath: `http://localhost:${extPort}/`, + writeToDisk: true, + }, + headers: { + 'Access-Control-Allow-Origin': '*', + }, + allowedHosts: 'all', + }, + compiler, + ); + + if (module.hot) { + module.hot.accept(); + } -export const demoExtension = gulp.series( - demoExtensionClean, - gulp.parallel(demoExtensionWebpack, demoExtensionCopy), -); + await server.start(); + console.log(`server listening to ${extPort}...`); +} export async function copyConfig() { if (!(await fs.exists(file))) { diff --git a/packages/ringcentral-widgets-demo/package.json b/packages/ringcentral-widgets-demo/package.json index 4c8a3073b1..dd0c4c7420 100644 --- a/packages/ringcentral-widgets-demo/package.json +++ b/packages/ringcentral-widgets-demo/package.json @@ -8,14 +8,14 @@ "url": "https://github.com/ringcentral/ringcentral-js-widget.git" }, "scripts": { - "build-extension": "yarn gulp demoExtension", "copy-config": "yarn gulp copyConfig", "dev-server": "yarn gulp devServer", "gulp": "gulp --require @ringcentral-integration/babel-settings/lib/register.js", - "start": "yarn gulp devServer" + "start": "yarn gulp devServer", + "start:ext": "rm -rf ./browser-extension/build && yarn gulp devExtensionServer" }, "dependencies": { - "@rc-ex/core": "^1.2.1", + "@rc-ex/core": "^1.3.3", "@ringcentral-integration/babel-settings": "*", "@ringcentral-integration/commons": "*", "@ringcentral-integration/core": "*", @@ -26,22 +26,15 @@ "@ringcentral-integration/widgets": "*", "@ringcentral/sdk": "^4.7.2", "@ringcentral/subscriptions": "^4.6.0", - "autoprefixer": "^9.8.4", - "babel-istanbul": "^0.12.1", - "babel-loader": "^9.1.2", + "autoprefixer": "^10.4.16", + "copy-webpack-plugin": "^11.0.0", "coveralls": "^3.0.4", - "css-loader": "^2.1.1", - "dayjs": "^1.11.7", - "file-loader": "^6.2.0", "fs-extra": "^10.1.0", "gulp": "^4.0.2", "gulp-babel": "^8.0.0", "gulp-istanbul": "^1.1.1", "gulp-sourcemaps": "^2.6.5", "identity-obj-proxy": "^3.0.0", - "json-loader": "^0.5.7", - "moment": "^2.29.1", - "postcss-loader": "^3.0.0", "prop-types": "^15.7.2", "pubnub": "^4.29.11", "react": "^17.0.2", @@ -49,20 +42,14 @@ "react-redux": "^5.1.1", "react-router": "^3.2.6", "react-router-redux": "^4.0.8", - "react-svg-loader": "^3.0.3", - "react-test-renderer": "^17.0.2", "redux": "^4.2.0", "redux-logger": "^3.0.6", "redux-thunk": "^2.2.0", "ringcentral-client": "^1.0.0-beta.2", - "sass": "^1.43.4", - "sass-loader": "^7.1.0", - "source-map-loader": "^3.0.0", - "style-loader": "^0.23.1", "styled-components": "^5.3.3", - "url-loader": "^4.1.1", - "webpack": "^5.58.1", - "webpack-dev-server": "^4.3.1", + "webpack": "^5.89.0", + "webpack-dev-server": "^4.15.1", + "webpack-merge": "^5.10.0", "yargs": "^17.1.1" }, "peerDependencies": {}, diff --git a/packages/ringcentral-widgets-test/__test__/__mocks__/ringcentral-web-phone.js b/packages/ringcentral-widgets-test/__test__/__mocks__/ringcentral-web-phone/index.js similarity index 91% rename from packages/ringcentral-widgets-test/__test__/__mocks__/ringcentral-web-phone.js rename to packages/ringcentral-widgets-test/__test__/__mocks__/ringcentral-web-phone/index.js index 4492c003ba..51299c4855 100644 --- a/packages/ringcentral-widgets-test/__test__/__mocks__/ringcentral-web-phone.js +++ b/packages/ringcentral-widgets-test/__test__/__mocks__/ringcentral-web-phone/index.js @@ -1,4 +1,5 @@ -import Session from '../support/session'; +import Session from '../../support/session'; +import { AudioHelper } from './lib/audioHelper'; const webphoneCache = []; class Transport { @@ -30,6 +31,7 @@ class UserAgent { this._events = {}; this.transport = new Transport(); this.sessions = {}; + this.audioHelper = new AudioHelper(); } on(event, cb) { @@ -92,15 +94,6 @@ class UserAgent { removeListener() {} - get audioHelper() { - return { - setVolume() {}, - playIncoming() {}, - playOutgoing() {}, - loadAudio() {}, - }; - } - isRegistered() { return true; } diff --git a/packages/ringcentral-widgets-test/__test__/__mocks__/ringcentral-web-phone/lib/audioHelper.js b/packages/ringcentral-widgets-test/__test__/__mocks__/ringcentral-web-phone/lib/audioHelper.js new file mode 100644 index 0000000000..a8be30a5d0 --- /dev/null +++ b/packages/ringcentral-widgets-test/__test__/__mocks__/ringcentral-web-phone/lib/audioHelper.js @@ -0,0 +1,6 @@ +export class AudioHelper { + setVolume() {} + playIncoming() {} + playOutgoing() {} + loadAudio() {} +} diff --git a/packages/ringcentral-widgets-test/__test__/integration-test/CallCtrlPage/CurrentCallCtrl.spec.js b/packages/ringcentral-widgets-test/__test__/integration-test/CallCtrlPage/CurrentCallCtrl.spec.js index 7b20e49743..7280c869cd 100644 --- a/packages/ringcentral-widgets-test/__test__/integration-test/CallCtrlPage/CurrentCallCtrl.spec.js +++ b/packages/ringcentral-widgets-test/__test__/integration-test/CallCtrlPage/CurrentCallCtrl.spec.js @@ -241,8 +241,8 @@ describe('Current Call Control Page - Keypad', () => { expect(wrapper.find(ActiveCallDialPad)).toHaveLength(1); const backButton = wrapper .find(ActiveCallDialPad) - .find('BackHeader') - .find('Button') + .find('Styled(_PageHeaderBack)') + .find('RcIconButton') .first(); backButton.simulate('click'); await sleep(100); @@ -262,8 +262,8 @@ describe('Current Call Control Page - Keypad', () => { expect(wrapper.find(ActiveCallDialPad)).toHaveLength(1); const backButton = wrapper .find(ActiveCallDialPad) - .find('BackHeader') - .find('Button') + .find('Styled(_PageHeaderBack)') + .find('RcIconButton') .first(); backButton.simulate('click'); await sleep(100); @@ -638,9 +638,13 @@ describe('Current Call Control Page - Transfer', () => { wrapper.update(); const panel = wrapper.find(TransferPanel); expect(panel).toHaveLength(1); - expect(panel.find('BackHeader')).toHaveLength(1); - expect(panel.find('BackHeader').find('Button')).toHaveLength(1); - expect(panel.find('BackHeader').text()).toEqual('Transfer to'); + expect(panel.find('Styled(_PageHeaderBack)')).toHaveLength(1); + expect( + panel.find('Styled(_PageHeaderBack)').find('RcIconButton'), + ).toHaveLength(1); + expect(panel.find('Styled(Styled(Component))').text()).toEqual( + 'Transfer to', + ); expect(panel).toHaveLength(1); expect(panel.find('label').text()).toEqual('To:'); expect(panel.find('input').props().placeholder).toEqual( @@ -660,8 +664,8 @@ describe('Current Call Control Page - Transfer', () => { wrapper.update(); const backButton = wrapper .find(TransferPanel) - .find('BackHeader') - .find('Button') + .find('Styled(_PageHeaderBack)') + .find('RcIconButton') .first(); backButton.simulate('click'); await sleep(100); diff --git a/packages/ringcentral-widgets-test/__test__/integration-test/ConferenceCallDialerPage/ActiveCallPanel.spec.js b/packages/ringcentral-widgets-test/__test__/integration-test/ConferenceCallDialerPage/ActiveCallPanel.spec.js index 71429605db..558093fb07 100644 --- a/packages/ringcentral-widgets-test/__test__/integration-test/ConferenceCallDialerPage/ActiveCallPanel.spec.js +++ b/packages/ringcentral-widgets-test/__test__/integration-test/ConferenceCallDialerPage/ActiveCallPanel.spec.js @@ -20,6 +20,7 @@ import { mockPresencePubnub, } from '../../support/callHelper'; import { initPhoneWrapper, tearDownWrapper } from '../shared'; + import extensionsListBody from './data/extensions.json'; beforeEach(async () => { @@ -121,181 +122,6 @@ async function mockStartConference(phone, wrapper) { wrapper.update(); } -describe('Simplified Call Control Page:', () => { - test('Check buttons in Conference Call Ctrl Page', async () => { - const { wrapper, phone } = await initPhoneWrapper({ - mockNumberParser: false, - mockRecentActivity: true, - }); - const contactA = phone.contacts.allContacts[0]; - await mockAddCall(phone, wrapper, contactA, contactA); - wrapper.update(); - expect(wrapper.find(MergeInfo)).toHaveLength(1); - expect(wrapper.find(ActiveCallPad)).toHaveLength(1); - const buttons = wrapper.find(ActiveCallPad).find(ActiveCallButton); - expect(buttons.at(0).text()).toEqual('Mute'); - expect(buttons.at(1).text()).toEqual('Keypad'); - expect(buttons.at(2).text()).toEqual('Hold'); - expect(buttons.at(3).text()).toEqual('Merge'); - expect(buttons.at(4).text()).toEqual('Record'); - expect(buttons.at(5).text()).toEqual('Call Actions'); - const handupButton = wrapper.find('.stopButtonGroup').find(CircleButton); - expect(handupButton.props().className).toEqual('stopButton'); - await tearDownWrapper(wrapper); - }); -}); - -describe('RCI-1071: simplified call control page #3', () => { - test('#1 Check the merge info in Simplified Call control page', async () => { - const { wrapper, phone } = await initPhoneWrapper({ - mockNumberParser: false, - mockRecentActivity: true, - }); - // Prepare: Contacts has a internal contact with avatar and a external contact without avatar - await mockContacts(phone); - const contactA = phone.contacts.allContacts.find( - (item) => item.type === 'company' && item.hasProfileImage, - ); - const { sessionB } = await mockAddCall(phone, wrapper, contactA, contactA); - expect(phone.routerInteraction.currentPath).toEqual( - `/calls/active/${sessionB.id}`, - ); - await sleep(300); - const mergeInfo = wrapper.find(MergeInfo); - expect(mergeInfo).toHaveLength(1); - - const callAvatarB = mergeInfo.find(CallAvatar).at(1); - // TODO: mock contactsA's data - // expect(callAvatar.props().avatarUrl).toEqual('avatarUrl'); - - expect(mergeInfo.find('.callee_name').text()).toEqual(contactA.name); - expect(mergeInfo.find('.callee_status').text()).toEqual('On Hold'); - expect(callAvatarB.props().avatarUrl).toBeNull(); - expect(mergeInfo.find('.callee_name_active').text()).toEqual(contactA.name); - expect(mergeInfo.find(DurationCounter)).toHaveLength(1); - await tearDownWrapper(wrapper); - }); - test('#2 Contact A hangs up the call', async () => { - const { wrapper, phone } = await initPhoneWrapper({ - mockNumberParser: false, - mockRecentActivity: true, - }); - await mockContacts(phone); - const contactA = phone.contacts.allContacts.find( - (item) => item.type === 'company', - ); - await mockAddCall(phone, wrapper, contactA, contactA); - const sessionId = phone.webphone.sessions[1].id; - const sessionA = phone.webphone._sessions.get(sessionId); - sessionA.terminate(); - wrapper.update(); - await sleep(300); - - const mergeInfo = wrapper.find(MergeInfo); - expect(mergeInfo).toHaveLength(1); - - const callAvatarB = mergeInfo.find(CallAvatar).at(1); - - const domCalleeStatus = mergeInfo.find('.callee_status'); - - // TODO: mock contactsA's data - // expect(callAvatarA.props().avatarUrl).toEqual(''); - expect(mergeInfo.find('.callee_name').text()).toEqual(contactA.name); - expect(domCalleeStatus.text()).toEqual('Disconnected'); - expect(domCalleeStatus.props().className).toContain( - 'callee_status_disconnected', - ); - expect(callAvatarB.props().avatarUrl).toBeNull(); - expect(mergeInfo.find('.callee_name_active').text()).toEqual(contactA.name); - expect(mergeInfo.find(DurationCounter)).toHaveLength(1); - await tearDownWrapper(wrapper); - }); - test('#3 && #4 user makes a conference call then make an outbound call, then hangup', async () => { - const { wrapper, phone } = await initPhoneWrapper(); - // Prepare: Contacts has a internal contact with avatar and a external contact without avatar - await mockContacts(phone); - const contactA = phone.contacts.allContacts.find( - (item) => item.type === 'company' && item.hasProfileImage, - ); - await mockStartConference(phone, wrapper); - phone.webphone._updateSessions(); - const conferenceSessionId = Object.values( - phone.conferenceCall.conferences, - )[0].sessionId; - const conferenceSession = phone.webphone.sessions.find( - (x) => x.id === conferenceSessionId, - ); - expect( - phone.routerInteraction.currentPath.indexOf('/calls/active'), - ).toEqual(0); - const callCtrlContainer = wrapper.find(CallCtrlContainer); - const addButton = callCtrlContainer.find(CircleButton).at(3); - addButton.find('g').simulate('click'); - await sleep(500); - wrapper.update(); - expect(phone.routerInteraction.currentPath).toEqual( - `/conferenceCall/dialer/${conferenceSession.fromNumber}/${conferenceSession.id}`, - ); - const session = await call( - phone, - wrapper, - contactA.phoneNumbers[0].phoneNumber, - ); - await mockSub(phone); - expect(phone.routerInteraction.currentPath).toEqual( - `/calls/active/${session.id}`, - ); - wrapper.update(); - const mergeInfo = wrapper.find(MergeInfo); - expect(mergeInfo).toHaveLength(1); - expect(mergeInfo.find('.callee_name').text()).toEqual('Conference Call'); - expect(mergeInfo.find('.callee_status').text()).toEqual('On Hold'); - // FIXME: temporarily disable these lines. - // await phone.webphone.hangup(conferenceSessionId); - // await sleep(1000); - // phone.webphone._updateSessions(); - - // expect(mergeInfo.find('.callee_status').text()).toEqual('Disconnected'); - await tearDownWrapper(wrapper); - }); -}); - -describe('RCI-1710156: Call control add call flow', () => { - test('#5 User make an outbound call', async () => { - const { wrapper, phone } = await initPhoneWrapper(); - await mockContacts(phone); - const contactA = phone.contacts.allContacts.find( - (item) => item.type === 'company' && item.hasProfileImage, - ); - const { sessionB } = await mockAddCall(phone, wrapper, contactA, contactA); - expect(phone.routerInteraction.currentPath).toEqual( - `/calls/active/${sessionB.id}`, - ); - const activeCallButtons = wrapper - .find(ActiveCallPad) - .find(ActiveCallButton); - expect(activeCallButtons.at(0).props().title).toEqual('Mute'); - expect(activeCallButtons.at(1).props().title).toEqual('Keypad'); - expect(activeCallButtons.at(2).props().title).toEqual('Hold'); - expect(activeCallButtons.at(3).props().title).toEqual('Merge'); - const hangupBtn = wrapper.find('.stopButtonGroup').find(CircleButton); - expect(hangupBtn.props().className).toEqual('stopButton'); - await tearDownWrapper(wrapper); - }); -}); - -describe('RCI-1710156: Call control add call flow #6', () => { - test('#6 && #7 User clicked Merge button then go to Settings -> Calling page', async () => { - const { wrapper, phone } = await initPhoneWrapper(); - await mockStartConference(phone, wrapper); - phone.routerInteraction.push('/settings/calling'); - wrapper.update(); - const calling = wrapper.find(DropdownSelect); - expect(calling.props().disabled).toBe(true); - await tearDownWrapper(wrapper); - }); -}); - describe('RCI-1710156: Call control add call flow', () => { test('#1 User make an outbound call', async () => { const { wrapper, phone } = await initPhoneWrapper(); @@ -337,8 +163,8 @@ describe('RCI-1710156: Call control add call flow', () => { const toInput = wrapper.find("input[name='receiver']"); expect(toInput).toHaveLength(1); toInput.props().onFocus(); - await phone.contactSearch.search({ searchString: 'Something1 New1' }); toInput.props().onChange({ currentTarget: { value: 'Something1 New1' } }); + await phone.contactSearch.search({ searchString: 'Something1 New1' }); await sleep(100); wrapper.update(); const dropdownList = wrapper.find('DropdownList'); diff --git a/packages/ringcentral-widgets-test/__test__/integration-test/IncomingCallPage/IncomingCallPad.spec.js b/packages/ringcentral-widgets-test/__test__/integration-test/IncomingCallPage/IncomingCallPad.spec.js index 5eb6908b62..4d1df065c2 100644 --- a/packages/ringcentral-widgets-test/__test__/integration-test/IncomingCallPage/IncomingCallPad.spec.js +++ b/packages/ringcentral-widgets-test/__test__/integration-test/IncomingCallPage/IncomingCallPad.spec.js @@ -444,6 +444,7 @@ describe('Check Incoming Call Forward Button > ForwardForm', () => { }); test('RCI-1712302 Main Flow - select one of the forward numbers', async (done) => { const { wrapper, phone } = await initPhoneWrapper(); + mock.numberParserV2(); await makeInbountCall(phone, wrapper, sid111); const buttonForward = wrapper .find(IncomingCallPad) @@ -469,6 +470,7 @@ describe('Check Incoming Call Forward Button > ForwardForm', () => { // clear the forwardingNumbers mock.forwardingNumber(forwardingNumberBody); mock.numberParser(); + mock.numberParserV2(); await phone.forwardingNumber.fetchData(); await makeInbountCall(phone, wrapper, sid111); const buttonForward = wrapper diff --git a/packages/ringcentral-widgets-test/__test__/integration-test/LogBasicInfo/LogBasicInfo.spec.js b/packages/ringcentral-widgets-test/__test__/integration-test/LogBasicInfo/LogBasicInfo.spec.js index da6d51c435..f102509424 100644 --- a/packages/ringcentral-widgets-test/__test__/integration-test/LogBasicInfo/LogBasicInfo.spec.js +++ b/packages/ringcentral-widgets-test/__test__/integration-test/LogBasicInfo/LogBasicInfo.spec.js @@ -1,7 +1,6 @@ import { mount } from 'enzyme'; import React from 'react'; -import callDirections from '@ringcentral-integration/commons/enums/callDirections'; -import callResults from '@ringcentral-integration/commons/enums/callResults'; +import { callDirection } from '@ringcentral-integration/commons/enums/callDirections'; import telephonyStatuses from '@ringcentral-integration/commons/enums/telephonyStatus'; import LogBasicInfo from '@ringcentral-integration/widgets/components/LogBasicInfo'; @@ -19,7 +18,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.inbound, + direction: callDirection.inbound, to: { phoneNumber: '+16509807435', }, @@ -39,7 +38,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '+16509807435', }, @@ -59,7 +58,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.inbound, + direction: callDirection.inbound, to: { phoneNumber: '+16509807435', }, @@ -79,7 +78,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '+16509807435', }, @@ -99,7 +98,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '+16509807435', }, @@ -118,7 +117,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.inbound, + direction: callDirection.inbound, to: { phoneNumber: '+16509807435', }, @@ -137,7 +136,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.inbound, + direction: callDirection.inbound, to: { phoneNumber: '+16509807435', }, @@ -156,7 +155,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.inbound, + direction: callDirection.inbound, to: { extensionNumber: '111', }, @@ -175,7 +174,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '+16509807435', }, @@ -194,7 +193,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -213,7 +212,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -234,7 +233,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -253,7 +252,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.inbound, + direction: callDirection.inbound, to: { phoneNumber: '111', }, @@ -272,7 +271,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -291,7 +290,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -310,7 +309,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -329,7 +328,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -348,7 +347,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -367,7 +366,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -386,7 +385,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -405,7 +404,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -424,7 +423,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -443,7 +442,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -462,7 +461,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -481,7 +480,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -500,7 +499,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -519,7 +518,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -538,7 +537,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -557,7 +556,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -576,7 +575,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -595,7 +594,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.inbound, + direction: callDirection.inbound, to: { phoneNumber: '111', }, @@ -614,7 +613,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -633,7 +632,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -652,7 +651,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -671,7 +670,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -690,7 +689,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.inbound, + direction: callDirection.inbound, to: { phoneNumber: '111', }, @@ -709,7 +708,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -728,7 +727,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -747,7 +746,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -766,7 +765,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -785,7 +784,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -804,7 +803,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -823,7 +822,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -842,7 +841,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -861,7 +860,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -880,7 +879,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -899,7 +898,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -918,7 +917,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -937,7 +936,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -956,7 +955,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -975,7 +974,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -994,7 +993,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -1013,7 +1012,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -1032,7 +1031,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -1051,7 +1050,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -1070,7 +1069,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -1089,7 +1088,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -1108,7 +1107,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -1127,7 +1126,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -1146,7 +1145,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -1165,7 +1164,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -1184,7 +1183,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => '(123)4567-890', call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, @@ -1203,7 +1202,7 @@ describe('Call Basic Info:', () => { const props = { formatPhone: (value) => '+44123456789', call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '111', }, diff --git a/packages/ringcentral-widgets-test/__test__/integration-test/LogBasicInfoV2/LogBasicInfoV2.spec.js b/packages/ringcentral-widgets-test/__test__/integration-test/LogBasicInfoV2/LogBasicInfoV2.spec.js index 66bd538c3c..721219aaac 100644 --- a/packages/ringcentral-widgets-test/__test__/integration-test/LogBasicInfoV2/LogBasicInfoV2.spec.js +++ b/packages/ringcentral-widgets-test/__test__/integration-test/LogBasicInfoV2/LogBasicInfoV2.spec.js @@ -2,7 +2,7 @@ import React from 'react'; import { mount } from 'enzyme'; import { RcThemeProvider } from '@ringcentral/juno'; import LogBasicInfoV2 from '@ringcentral-integration/widgets/components/LogBasicInfoV2'; -import callDirections from '@ringcentral-integration/commons/enums/callDirections'; +import { callDirection } from '@ringcentral-integration/commons/enums/callDirections'; import getIntlDateTimeFormatter, { DEFAULT_TIME_OPTIONS, } from '@ringcentral-integration/commons/lib/getIntlDateTimeFormatter'; @@ -48,7 +48,7 @@ describe('', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.inbound, + direction: callDirection.inbound, to: { phoneNumber: '+16509807435', }, @@ -77,7 +77,7 @@ describe('', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.inbound, + direction: callDirection.inbound, to: { phoneNumber: '+16509807435', }, @@ -103,7 +103,7 @@ describe('', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.inbound, + direction: callDirection.inbound, to: { phoneNumber: '+16509807435', }, @@ -129,7 +129,7 @@ describe('', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.inbound, + direction: callDirection.inbound, to: { phoneNumber: '+16509807435', }, @@ -160,7 +160,7 @@ describe('', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '+16509807435', }, @@ -190,7 +190,7 @@ describe('', () => { const props = { formatPhone: (value) => value, call: { - direction: callDirections.outbound, + direction: callDirection.outbound, to: { phoneNumber: '+16509807435', }, diff --git a/packages/ringcentral-widgets-test/__test__/integration-test/LogNotification/LogNotification.spec.js b/packages/ringcentral-widgets-test/__test__/integration-test/LogNotification/LogNotification.spec.js index b81ae8df36..b514ce7d34 100644 --- a/packages/ringcentral-widgets-test/__test__/integration-test/LogNotification/LogNotification.spec.js +++ b/packages/ringcentral-widgets-test/__test__/integration-test/LogNotification/LogNotification.spec.js @@ -1,15 +1,13 @@ import { shallow } from 'enzyme'; import React from 'react'; -import callDirections from '@ringcentral-integration/commons/enums/callDirections'; -import callResults from '@ringcentral-integration/commons/enums/callResults'; -import telephonyStatuses from '@ringcentral-integration/commons/enums/telephonyStatus'; +import { callDirection } from '@ringcentral-integration/commons/enums/callDirections'; import LogNotification from '@ringcentral-integration/widgets/components/LogNotification'; import { Button } from '@ringcentral-integration/widgets/components/Button'; const setup = (props) => { const currentLog = { call: { - direction: callDirections.inbound, + direction: callDirection.inbound, to: { phoneNumber: '+16509807435', }, diff --git a/packages/ringcentral-widgets-test/__test__/integration-test/Meeting/ScheduleMeeting.spec.js b/packages/ringcentral-widgets-test/__test__/integration-test/Meeting/ScheduleMeeting.spec.js deleted file mode 100644 index b79346a3f0..0000000000 --- a/packages/ringcentral-widgets-test/__test__/integration-test/Meeting/ScheduleMeeting.spec.js +++ /dev/null @@ -1,177 +0,0 @@ -import moment from 'moment'; -import { MeetingType } from '@ringcentral-integration/commons/helpers/meetingHelper'; -import MeetingSection from '@ringcentral-integration/widgets/components/MeetingSection'; -import NavigationBar from '@ringcentral-integration/widgets/components/NavigationBar'; -import { sleep } from '@ringcentral-integration/commons/utils'; -import { getWrapper } from '../shared'; - -let app = null; -beforeAll(async () => { - jest.setTimeout(64000); - app = await getWrapper(); - // Nav to Meeting page - const navigationBar = app.find(NavigationBar).first(); - await navigationBar.props().goTo('/meeting'); - app.update(); -}); - -describe('Schedule Meeting', () => { - const EXPECT_HOUR = 5; - const EXPECT_MINUTES = 15; - test('', async () => { - const topic = app.find(MeetingSection).first(); - const maxInput = '#'.repeat(128); - const exceedInput = `${maxInput}#`; - topic.find('input').simulate('change', { target: { value: maxInput } }); - // To fit the Debounce timeout on MeetingPanel - await sleep(10); - expect(app.props().phone.meeting.meeting.topic).toBe(maxInput); - // Exceed 128 chars length should failed and keep the origin - topic.find('input').simulate('change', { target: { value: exceedInput } }); - await sleep(10); - expect(app.props().phone.meeting.meeting.topic).not.toBe(exceedInput); - topic.find('input').simulate('change', { target: { value: 'aloha' } }); - await sleep(10); - expect(app.props().phone.meeting.meeting.topic).toBe('aloha'); - }); - test('', async () => { - let when = app.find(MeetingSection).at(1); - // Tolerance one minute range - const now = moment(Date.now()); - expect( - moment(app.props().phone.meeting.meeting.schedule.startTime).diff( - now, - 'minutes', - ), - ).toBeLessThanOrEqual(60); - // DATE - const dateField = when.find('DateTimePicker').at(0); - const expectedDate = now.add(1, 'days'); - dateField.props().onChange(expectedDate.toDate()); - app.update(); - // Time - expectedDate.hours(EXPECT_HOUR).minutes(EXPECT_MINUTES); - when = app.find(MeetingSection).at(1); - const timeField = when.find('DateTimePicker').at(1); - timeField.props().onChange(expectedDate.toDate()); - app.update(); - - await sleep(100); - const actualDate = moment( - app.props().phone.meeting.meeting.schedule.startTime, - ); - expect(actualDate.diff(expectedDate, 'minutes')).toBeLessThan(1); - }); - test('', async () => { - const duration = app.find(MeetingSection).at(2); - expect(app.props().phone.meeting.meeting.schedule.durationInMinutes).toBe( - 60, - ); - const hourField = duration.find('DropdownList').first(); - hourField.props().onChange({ value: EXPECT_HOUR }); - expect(app.props().phone.meeting.meeting.schedule.durationInMinutes).toBe( - EXPECT_HOUR * 60, - ); - const minuteField = duration.find('DropdownList').at(1); - minuteField.props().onChange({ value: EXPECT_MINUTES }); - expect(app.props().phone.meeting.meeting.schedule.durationInMinutes).toBe( - EXPECT_HOUR * 60 + EXPECT_MINUTES, - ); - }); - test('', async () => { - const recurring = app.find(MeetingSection).at(3); - expect(app.props().phone.meeting.meeting.meetingType).toBe('Scheduled'); - const typeField = recurring.find('Switch').first(); - typeField.props().onChange(false); - expect(app.props().phone.meeting.meeting.meetingType).toBe( - MeetingType.SCHEDULED, - ); - typeField.props().onChange(true); - const meetingType = app.props().phone.meeting.meeting.meetingType; - expect( - meetingType === MeetingType.RECURRING || - meetingType === MeetingType.SCHEDULED_RECURRING, - ).toBe(true); - }); - test('
diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Call/WebPhone/InboundCall/SendToVoicemail/Checkpoint/RCI-808.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Call/WebPhone/InboundCall/SendToVoicemail/Checkpoint/RCI-808.test.tsx index c029eb4e28..cb59f5d8f6 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Call/WebPhone/InboundCall/SendToVoicemail/Checkpoint/RCI-808.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Call/WebPhone/InboundCall/SendToVoicemail/Checkpoint/RCI-808.test.tsx @@ -7,7 +7,6 @@ * 1. Make an inbound call * 2. Make a call and keep in call control page, then make an inbound call */ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { p1, @@ -29,9 +28,9 @@ import { CheckIncomingCallPageExist, MakeCall, } from '../../../../../../../steps/Call'; -import { CheckRouterNavigation } from '../../../../../../../steps/Navigate'; import { CommonLoginEntry } from '../../../../../../../steps/CommonLogin'; import { TriggerActiveCallChanged } from '../../../../../../../steps/Mock'; +import { CheckRouterNavigation } from '../../../../../../../steps/Navigate'; @autorun(test) @it @@ -96,8 +95,8 @@ export class RCI808 extends Step { User goes back to last page he/she is viewing." action={async ({ actionType }: any) => [ async (_: any, { phone, payload }: any) => { - expect(phone.webphone.toVoiceMail).toBeCalled(); - expect(phone.webphone.toVoiceMail).toBeCalledWith( + expect(phone.webphone.toVoiceMail).toHaveBeenCalled(); + expect(phone.webphone.toVoiceMail).toHaveBeenCalledWith( payload.toVoiceMailCallId, ); }, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Call/WebPhone/WebPhoneRegistration/RCI-804.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Call/WebPhone/WebPhoneRegistration/RCI-804.test.tsx new file mode 100644 index 0000000000..19bf8d685e --- /dev/null +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Call/WebPhone/WebPhoneRegistration/RCI-804.test.tsx @@ -0,0 +1,167 @@ +/** + * RCI-804: Web phone registration limitations - no DLs + * https://test_it_domain/test-cases/RCI-804 + * Preconditions: + * 1. account's WebRTC is enabled + * 2. user doesn't have DLs (for AT&T and TELUS brands, doesn't have Phone) + * Entry point(/s): + * Login CTI APP > Settings > Calling + */ +import { + p2, + it, + autorun, + Scenario, + Step, + Then, + title, + When, + common, +} from '@ringcentral-integration/test-utils'; + +import type { StepFunction } from '../../../../../lib/step'; +import { + CheckNoAnyAlerts, + CheckContainsAlertMessage, +} from '../../../../../steps/Alert'; +import { + CheckIncomingCallPageExist, + ClickBackButtonOfIncomingCallPanel, + MakeInboundCall, + MakeOutboundCall, +} from '../../../../../steps/Call'; +import { MockSipProvision } from '../../../../../steps/Call/Webphone'; +import { CommonLogin } from '../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../steps/CreateInstance'; +import { + CreateMock as CommonCreateMock, + MockExtensionDeviceList, + MockGetPhoneNumber, + MockMessageSync, +} from '../../../../../steps/Mock'; +import { CheckRoutePathIs, NavigateTo } from '../../../../../steps/Router'; +import { + ExpandCallingSettingDropdown, + SelectCallingSetting, + ClickSaveButton, +} from '../../../../../steps/Settings'; +import { CheckFromFieldExists } from '../../../../../steps/dialer'; + +@autorun(test) +@common +@it +@p2 +@title('Web phone registration limitations - no DLs') +export class RCI804 extends Step { + CreateMock: StepFunction = CommonCreateMock; + Login: StepFunction = (props) => ( + + ); + run() { + const { CreateMock, Login } = this; + return ( + [ + CreateMock, + MockGetPhoneNumber, + MockMessageSync, + , + , + Login, + ]} + > + [ + , + , + + // Select other option + ExpandCallingSettingDropdown, + , + ClickSaveButton, + + // Select back to "Browser" option + ExpandCallingSettingDropdown, + , + ClickSaveButton, + ]} + /> + + } + /> + } + /> + + } + /> + , + ]} + /> + } /> + + + ); + } +} + +@autorun(test) +@common +@it +@p2 +@title('Web phone registration limitations - no DLs') +export class RCI804_AddDL extends Step { + CreateMock: StepFunction = CommonCreateMock; + Login: StepFunction = (props) => ( + + ); + run() { + const { CreateMock, Login } = this; + return ( + + [ + CreateMock, + MockGetPhoneNumber, + MockMessageSync, + , + Login, + , + ]} + /> + } /> + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Call/WebPhone/WebPhoneRegistration/RCI-805.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Call/WebPhone/WebPhoneRegistration/RCI-805.test.tsx new file mode 100644 index 0000000000..bd8ccb757d --- /dev/null +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Call/WebPhone/WebPhoneRegistration/RCI-805.test.tsx @@ -0,0 +1,200 @@ +/** + * RCI-805: Web phone registration limitations - over limit + * https://test_it_domain/test-cases/RCI-805 + * Preconditions: + * 1. account's WebRTC is enabled + * 2. user has DLs (for AT&T and TELUS brands, has Phone) + * 3. there are 5 or more browsers/apps(eg. RC for Google/SfB/Outlook) logged in a same user and registered Web Phone + * Entry point(/s): + * Login CTI APP > Settings > Calling + */ +import { + p2, + it, + autorun, + Scenario, + Step, + Then, + title, + When, + common, +} from '@ringcentral-integration/test-utils'; + +import type { StepFunction } from '../../../../../lib/step'; +import { + CheckNoAnyAlerts, + CheckContainsAlertMessage, + CloseAlertMessage, +} from '../../../../../steps/Alert'; +import { + CheckIncomingCallPageNotExist, + MakeInboundCall, + MakeOutboundCall, +} from '../../../../../steps/Call'; +import { + MockSipProvision, + TriggerWebphoneEvent, +} from '../../../../../steps/Call/Webphone'; +import { CommonLogin } from '../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../steps/CreateInstance'; +import { + CreateMock as CommonCreateMock, + MockGetPhoneNumber, + MockMessageSync, +} from '../../../../../steps/Mock'; +import { CheckRoutePathIs, NavigateTo } from '../../../../../steps/Router'; +import { + ExpandCallingSettingDropdown, + SelectCallingSetting, + ClickSaveButton, +} from '../../../../../steps/Settings'; +import { CheckFromFieldExists } from '../../../../../steps/dialer'; + +@autorun(test) +@common +@it +@p2 +@title('Web phone registration limitations - over limit') +export class RCI805_ChangeCallingOption extends Step { + CreateMock: StepFunction = CommonCreateMock; + Login: StepFunction = (props) => ( + + ); + run() { + const { CreateMock, Login } = this; + return ( + [ + CreateMock, + MockGetPhoneNumber, + MockMessageSync, + , + Login, + , + ]} + > + [ + , + , + + // Select other option + ExpandCallingSettingDropdown, + , + ClickSaveButton, + + // Select back to "Browser" option + ExpandCallingSettingDropdown, + , + ClickSaveButton, + ]} + /> + + } + /> + + ); + } +} + +@autorun(test) +@common +@it +@p2 +@title('Web phone registration limitations - over limit') +export class RCI805_MakeOutboundCall extends Step { + CreateMock: StepFunction = CommonCreateMock; + Login: StepFunction = (props) => ( + + ); + run() { + const { CreateMock, Login } = this; + return ( + [ + CreateMock, + MockGetPhoneNumber, + MockMessageSync, + , + Login, + , + ]} + > + } + /> + + ]} + /> + , + ]} + /> + + ); + } +} + +@autorun(test) +@common +@it +@p2 +@title('Web phone registration limitations - over limit') +export class RCI805_MakeInboundCall extends Step { + CreateMock: StepFunction = CommonCreateMock; + Login: StepFunction = (props) => ( + + ); + run() { + const { CreateMock, Login } = this; + return ( + [ + CreateMock, + MockGetPhoneNumber, + MockMessageSync, + , + Login, + , + CloseAlertMessage, + ]} + > + + + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Call/WebPhone/WebPhoneRegistration/RCI-807.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Call/WebPhone/WebPhoneRegistration/RCI-807.test.tsx new file mode 100644 index 0000000000..232b2f85ff --- /dev/null +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Call/WebPhone/WebPhoneRegistration/RCI-807.test.tsx @@ -0,0 +1,72 @@ +/** + * RCI-807: Web phone registration limitations - no permission + * https://test_it_domain/test-cases/RCI-807 + * Preconditions: + * Account doesn't have WebPhone Permission + * Entry point(/s): + * Login CTI APP > Settings > Calling + */ +import { + p2, + it, + autorun, + Scenario, + Step, + Then, + title, + When, + common, +} from '@ringcentral-integration/test-utils'; + +import type { StepFunction } from '../../../../../lib/step'; +import { CommonLogin } from '../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../steps/CreateInstance'; +import { + CreateMock as CommonCreateMock, + MockGetPhoneNumber, + MockFeaturePermission, +} from '../../../../../steps/Mock'; +import { CheckRoutePathIs, NavigateTo } from '../../../../../steps/Router'; +import { + ExpandCallingSettingDropdown, + CheckCallWithOption, +} from '../../../../../steps/Settings'; + +@autorun(test) +@common +@it +@p2 +@title('Web phone registration limitations - no permission') +export class RCI807 extends Step { + CreateMock: StepFunction = CommonCreateMock; + Login: StepFunction = (props) => ( + + ); + run() { + const { CreateMock, Login } = this; + return ( + [ + CreateMock, + MockGetPhoneNumber, + , + Login, + ]} + > + [ + , + , + , + ]} + /> + } + /> + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Common/ToFieldSupportContactSearch/Checkpoint/RCI-3624.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Common/ToFieldSupportContactSearch/Checkpoint/RCI-3624.test.tsx new file mode 100644 index 0000000000..4343f3c07e --- /dev/null +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Common/ToFieldSupportContactSearch/Checkpoint/RCI-3624.test.tsx @@ -0,0 +1,111 @@ +/** + * RCI-3624: To field contact search no matches + * https://test_it_domain/test-cases/RCI-3624 + * Preconditions: + * RC CTI app is installed and enabled + * RC CTI App should be authorized to 3rd party + * User has login RC CTI App + * Extension type(/s): + * Entry point(/s): + * Entry point(/s): + * Dial Pad + * Compose Text + * Current call control > Transfer + * Single/Second incoming call > Forward > Custom number + */ +import { + p2, + it, + autorun, + examples, + Given, + Scenario, + Step, + Then, + title, + When, + StepProp, + WaitForRenderReady, +} from '@ringcentral-integration/test-utils'; + +import { + CallButtonBehavior, + CheckIncomingCallPageExist, + CustomForwardCall, + GoToTransferPage, + MakeCall, +} from '../../../../../steps/Call'; +import { CommonLogin } from '../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../steps/CreateInstance'; +import { InputRecipients } from '../../../../../steps/Messages'; +import { CreateMock } from '../../../../../steps/Mock'; +import { + NavigateToComposeText, + NavigateToDialer, +} from '../../../../../steps/Navigate'; +import { CheckNotContactsMatched } from '../../../../../steps/SearchField'; + +@autorun(test) +@it +@p2 +@title('To field contact search no matches') +export class NoMatchedContact extends Step { + Login: StepProp = (props) => ( + + ); + CreateMock: StepProp = CreateMock; + @examples([ + { + entry: , + action: , + }, + { + entry: , + action: , + }, + { + entry: ( + <> + + + + + ), + action: , + }, + { + entry: ( + <> + + + + + ), + action: ( + + ), + }, + ]) + run() { + const { Login, CreateMock } = this; + return ( + + + + + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Common/ToFieldSupportContactSearch/Checkpoint/RCI-3625.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Common/ToFieldSupportContactSearch/Checkpoint/RCI-3625.test.tsx new file mode 100644 index 0000000000..3f53424a14 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Common/ToFieldSupportContactSearch/Checkpoint/RCI-3625.test.tsx @@ -0,0 +1,228 @@ +/** + * RCI-3625: To field contact search UX + * https://test_it_domain/test-cases/RCI-3625 + * Preconditions: + * RC CTI app is installed and enabled + * RC CTI App should beauthorizedto 3rd party + * User has login RC CTI App withAccountA + * Extension type(/s): + * Entry point(/s): + * Entry point(/s): + * Dial Pad + * Compose Text + * Current call control > Transfer + * Single/Second incoming call > Forward > Custom number + */ +import { extensionStatusTypes } from '@ringcentral-integration/commons/enums/extensionStatusTypes'; +import { + p2, + it, + autorun, + examples, + Given, + Scenario, + Step, + Then, + title, + When, + StepProp, + WaitForRenderReady, + common, +} from '@ringcentral-integration/test-utils'; + +import { + CallButtonBehavior, + CheckIncomingCallPageExist, + ClickBackButton, + CustomForwardCall, + GoToTransferPage, + MakeCall, +} from '../../../../../steps/Call'; +import { ClickCurrentName } from '../../../../../steps/CallHistory'; +import { CommonLogin } from '../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../steps/CreateInstance'; +import { + FocusOnRecipients, + InputRecipients, +} from '../../../../../steps/Messages'; +import { + CreateMock, + MockExtensionsList, + mockExtensionsListData, + MockMessageSync, + MockNumberParserV2, + TriggerActiveCallChanged, +} from '../../../../../steps/Mock'; +import { + NavigateToComposeText, + NavigateToDialer, +} from '../../../../../steps/Navigate'; +import { NavigateTo } from '../../../../../steps/Router'; +import { + CheckContactDropdownList, + CheckNotContactsMatched, + CheckRecipientInput, + ClickContactItem, + DeleteRecipientItem, +} from '../../../../../steps/SearchField'; +import { + CheckInputToRecipientsNoExist, + CheckToFieldValue, +} from '../../../../../steps/dialer'; + +@autorun(test.skip) +@it +@p2 +@common +@title('To field contact search UX') +export class ToFieldContactsUX extends Step { + Login: StepProp = (props) => ( + + ); + CreateMock: StepProp = CreateMock; + @examples([ + { + entry: , + action: , + shouldKeptInput: true, + }, + { + entry: , + action: , + shouldKeptInput: true, + }, + { + entry: ( + <> + + + + + ), + action: , + shouldKeptInput: false, + }, + { + entry: ( + <> + + + + + ), + backToEntry: ( + <> + + + + + + + ), + action: , + shouldKeptInput: false, + }, + ]) + run() { + const { Login, CreateMock } = this; + return ( + + , + , + ({ + ...mockData, + ...mockExtensionsListData([ + { + firstName: 'Test', + lastName: 'user', + extensionNumber: '101', + status: extensionStatusTypes.notActivated, + phoneNumber: '+12054332854', + hidden: false, + phoneNumberHidden: false, + }, + ]), + })} + />, + Login, + this.example.entry, + WaitForRenderReady, + ]} + /> + { + const actions = []; + if (this.example.backToEntry) { + actions.push(ClickBackButton); + } + return actions; + }, + , + this.example.backToEntry + ? this.example.backToEntry + : this.example.entry, + WaitForRenderReady, + ]} + /> + { + const actions = []; + if (this.example.shouldKeptInput) { + actions.push(); + } + return actions; + }, + WaitForRenderReady, + ]} + /> + { + const actions = []; + actions.push( + this.example.shouldKeptInput + ? FocusOnRecipients + : this.example.action, + ); + return actions; + }, + WaitForRenderReady, + , + , + WaitForRenderReady, + ]} + /> + , + ]} + /> + + + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Contacts/Checkpoint/RCI-885.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Contacts/Checkpoint/RCI-885.test.tsx index 8d2f61ce8f..5b73aeb2e5 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Contacts/Checkpoint/RCI-885.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Contacts/Checkpoint/RCI-885.test.tsx @@ -8,7 +8,6 @@ * Entry point(/s): * More -> Contacts */ - import { p2, it, @@ -22,6 +21,7 @@ import { When, common, } from '@ringcentral-integration/test-utils'; + import type { StepProp } from '../../../../lib/step'; import { CommonLogin } from '../../../../steps/CommonLogin'; import { diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Contacts/ContactSearchField/Checkpoint/RCI-1101.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Contacts/ContactSearchField/Checkpoint/RCI-1101.test.tsx index 886c3ec035..8d8525c3dd 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Contacts/ContactSearchField/Checkpoint/RCI-1101.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Contacts/ContactSearchField/Checkpoint/RCI-1101.test.tsx @@ -10,9 +10,7 @@ * Entry point(/s): * */ - import { extensionStatusTypes } from '@ringcentral-integration/commons/enums/extensionStatusTypes'; -import { screen } from '@testing-library/react'; import { p2, it, @@ -25,6 +23,15 @@ import { Given, examples, } from '@ringcentral-integration/test-utils'; +import { screen } from '@testing-library/react'; + +import { mockMessageListData } from '../../../../../__mock__'; +import { + CheckContactDetailsProfile, + CheckContactItemExistInList, + SearchContacts, +} from '../../../../../steps/ContactsView'; +import { InputRecipients } from '../../../../../steps/Messages/actions'; import { MockExtensionsList, mockExtensionsListData, @@ -33,24 +40,17 @@ import { MockCallLogs, MockPresence, } from '../../../../../steps/Mock'; -import { - CheckContactDetailsProfile, - CheckContactItemExistInList, - SearchContacts, -} from '../../../../../steps/ContactsView'; import { NavigateToComposeText, NavigateToContactDetails, } from '../../../../../steps/Navigate'; import { NavigateToContacts } from '../../../../../steps/Navigate/actions/NavigateToContacts'; -import { InputRecipients } from '../../../../../steps/Messages/actions'; -import { mockMessageListData } from '../../../../../__mock__'; +import { NavigateToMessageHistory } from '../../../../../steps/Navigate/actions/NavigateToMessageHistory'; import { NavigateToMessagesTab } from '../../../../../steps/Navigate/actions/NavigateToMessages'; import { CheckContactDropdownList, Search, } from '../../../../../steps/SearchField'; -import { NavigateToMessageHistory } from '../../../../../steps/Navigate/actions/NavigateToMessageHistory'; @autorun(test.skip) @it diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Contacts/ContactSearchField/Checkpoint/RCI-3382.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Contacts/ContactSearchField/Checkpoint/RCI-3382.test.tsx index cc22887b92..1d40e77d64 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Contacts/ContactSearchField/Checkpoint/RCI-3382.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Contacts/ContactSearchField/Checkpoint/RCI-3382.test.tsx @@ -12,7 +12,6 @@ * Entry point(/s): * CTI > contact page >search box */ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { p2, @@ -26,6 +25,7 @@ import { common, } from '@ringcentral-integration/test-utils'; import { screen } from '@testing-library/react'; + import { CommonLogin } from '../../../../../steps/CommonLogin'; import { CheckContactNotFoundTextExist, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Contacts/ContactSearchField/Checkpoint/RCI-3384.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Contacts/ContactSearchField/Checkpoint/RCI-3384.test.tsx index 33393d990e..2e7c9b3509 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Contacts/ContactSearchField/Checkpoint/RCI-3384.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Contacts/ContactSearchField/Checkpoint/RCI-3384.test.tsx @@ -17,7 +17,6 @@ * Entry point(/s): * > Go to Contacts tab */ - import { p2, it, @@ -31,6 +30,7 @@ import { When, } from '@ringcentral-integration/test-utils'; import { waitFor } from '@testing-library/react'; + import type { StepProp } from '../../../../../lib/step'; import { CommonLogin } from '../../../../../steps/CommonLogin'; import { diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/ConnectToIncorrectNumber/RCI-4348.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/ConnectToIncorrectNumber/RCI-4348.test.tsx index 49de917365..344b969629 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/ConnectToIncorrectNumber/RCI-4348.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/ConnectToIncorrectNumber/RCI-4348.test.tsx @@ -17,7 +17,6 @@ | RC-CA |8 |7 |3135006 |205 |3135006 |Incorrect extension | */ - import { Category } from '@ringcentral-integration/commons/interfaces/NumberParserResponse.interface'; import { And, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/EdpEnableAndDialedNumber/RCI-4401.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/EdpEnableAndDialedNumber/RCI-4401.test.tsx index 743f5197ca..f15b791be5 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/EdpEnableAndDialedNumber/RCI-4401.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/EdpEnableAndDialedNumber/RCI-4401.test.tsx @@ -28,7 +28,6 @@ | RC-UK |7 |8 |3135 0033 |Match valid PSTN |12 |(12) 3135 0033 |PSTN |(12) 3135 0033 | */ - import { Category } from '@ringcentral-integration/commons/interfaces/NumberParserResponse.interface'; import { p1, @@ -43,6 +42,8 @@ import { When, common, } from '@ringcentral-integration/test-utils'; + +import { generateDialPlanData } from '../../../../../../__mock__/generateDialPlanData'; import type { StepProp } from '../../../../../../lib/step'; import { CheckCallControlPage, MakeCall } from '../../../../../../steps/Call'; import { CheckLogBaseInfoActive } from '../../../../../../steps/CallLog'; @@ -57,7 +58,6 @@ import { MockGetPhoneNumber, MockNumberParserV2, } from '../../../../../../steps/Mock'; -import { generateDialPlanData } from '../../../../../../__mock__/generateDialPlanData'; @autorun(test) @it diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/EdpEnableAndDialedNumber/RCI-4402.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/EdpEnableAndDialedNumber/RCI-4402.test.tsx index b68e0951ef..06194ad654 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/EdpEnableAndDialedNumber/RCI-4402.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/EdpEnableAndDialedNumber/RCI-4402.test.tsx @@ -28,7 +28,6 @@ | RC-UK |7 |3 |999 |Match Special Service |11 |999 |SpecialService |999 | */ - import { Category } from '@ringcentral-integration/commons/interfaces/NumberParserResponse.interface'; import { p1, @@ -43,6 +42,7 @@ import { When, common, } from '@ringcentral-integration/test-utils'; + import type { StepProp } from '../../../../../../lib/step'; import { CheckCallControlPage, MakeCall } from '../../../../../../steps/Call'; import { CheckLogBaseInfoActive } from '../../../../../../steps/CallLog'; diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/EdpEnableAndSendMessage/RCI-4340.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/EdpEnableAndSendMessage/RCI-4340.test.tsx index 9bbceeada5..425c55e529 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/EdpEnableAndSendMessage/RCI-4340.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/EdpEnableAndSendMessage/RCI-4340.test.tsx @@ -21,9 +21,8 @@ | RC-US |8 |7 |3135003 |205 | 3135003 |Extension |Sarah | */ - +import { trackEvents } from '@ringcentral-integration/commons/enums/trackEvents'; import { Category } from '@ringcentral-integration/commons/interfaces/NumberParserResponse.interface'; -import { waitUntilTo } from '@ringcentral-integration/utils'; import { p1, it, @@ -37,9 +36,9 @@ import { When, common, } from '@ringcentral-integration/test-utils'; +import { waitUntilTo } from '@ringcentral-integration/utils'; import { getNodeText, screen } from '@testing-library/react'; -import { trackEvents } from '@ringcentral-integration/commons/enums/trackEvents'; import type { StepProp } from '../../../../../../lib/step'; import { CommonLogin } from '../../../../../../steps/CommonLogin'; import { CreateInstance } from '../../../../../../steps/CreateInstance'; diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/EdpEnableAndSendMessage/RCI-4644.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/EdpEnableAndSendMessage/RCI-4644.test.tsx index 79df3d158e..987907cf28 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/EdpEnableAndSendMessage/RCI-4644.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/EdpEnableAndSendMessage/RCI-4644.test.tsx @@ -20,7 +20,6 @@ | RC-UK |8 |7 |3135003 |205 | 3135003 |Extension |Sarah | */ - import { Category } from '@ringcentral-integration/commons/interfaces/NumberParserResponse.interface'; import { autorun, @@ -34,6 +33,7 @@ import { title, When, } from '@ringcentral-integration/test-utils'; + import type { StepProp } from '../../../../../../lib/step'; import { CommonLogin } from '../../../../../../steps/CommonLogin'; import { CreateInstance } from '../../../../../../steps/CreateInstance'; diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/GetDefaultAreaCode/RCI-4153.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/GetDefaultAreaCode/RCI-4153.test.tsx index 5b4de08b5d..992d9ed6c1 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/GetDefaultAreaCode/RCI-4153.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/GetDefaultAreaCode/RCI-4153.test.tsx @@ -18,8 +18,6 @@ * Entry point(/s): * Open background.html of CTI > User log in to CTI app */ - -import { waitUntilTo } from '@ringcentral-integration/utils'; import { p2, it, @@ -31,6 +29,8 @@ import { title, When, } from '@ringcentral-integration/test-utils'; +import { waitUntilTo } from '@ringcentral-integration/utils'; + import type { StepProp } from '../../../../../../lib/step'; import { StepFunction } from '../../../../../../lib/step'; import { CommonLogin } from '../../../../../../steps/CommonLogin'; @@ -111,7 +111,7 @@ export class DefaultAreaCode extends Step { { mockData.regionalSettings.homeCountry = { - uri: 'https://api-rcapps-xmnup.rclabenv.com/restapi/v1.0/dictionary/country/75', + uri: 'https://api-rcapps-xmnuplabs_domain/restapi/v1.0/dictionary/country/75', id: '75', name: 'France', isoCode: 'FR', diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/GetDefaultAreaCode/RCI-4200.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/GetDefaultAreaCode/RCI-4200.test.tsx index a9018edee7..7c17d6a44c 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/GetDefaultAreaCode/RCI-4200.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/GetDefaultAreaCode/RCI-4200.test.tsx @@ -22,8 +22,8 @@ * Entry point(/s): * User log in to CTI app > Get {Api-default area}code fromnumber-parser/parse api */ - import { waitUntilTo } from '@ringcentral-integration/utils'; + import type { StepProp } from '../../../../../../lib/step'; import { p2, @@ -54,9 +54,9 @@ export class SelectedCountryCallingCode extends Step { CreateMock: StepProp | null = null; @examples(` - | countryName | selectedCountryCallingCode | primaryNumber | primaryNumberAreaCode | mainCompanyNumber | mainCompanyNumberAreaCode | expectAccountAreaCode | - | 'United Kingdom' | '44' | '+44(205) 879-3409' | '205' | '+44(403) 370-0051' | '403' | null | - `) + | countryName | countryCallingCode | primaryNumber | primaryNumberAreaCode | mainCompanyNumber | mainCompanyNumberAreaCode | expectAccountAreaCode | + | 'United Kingdom' | '44' | '+44(205) 879-3409' | '205' | '+44(403) 370-0051' | '403' | null | + `) run() { const { CreateMock, Login } = this; return ( @@ -94,17 +94,17 @@ export class SelectedCountryCallingCode extends Step { />, { - const selectedCountryCallingCode = - this.context.example.selectedCountryCallingCode; + const countryCallingCode = + this.context.example.countryCallingCode; const plan = mockData.find( - (plan) => plan.id === selectedCountryCallingCode, + (plan) => plan.id === countryCallingCode, ); if (!plan) { mockData.push({ - uri: `https://platform.ringcentral.com/restapi/v1.0/dictionary/country/${selectedCountryCallingCode}`, - id: selectedCountryCallingCode, + uri: `https://platform.ringcentral.com/restapi/v1.0/dictionary/country/${countryCallingCode}`, + id: countryCallingCode, name: this.context.example.countryName, - callingCode: selectedCountryCallingCode, + callingCode: countryCallingCode, isoCode: 'MockISOCode', }); } diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/RCI-4399.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/RCI-4399.test.tsx index a3bffc3aef..50e703b43b 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/RCI-4399.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/RCI-4399.test.tsx @@ -17,9 +17,7 @@ | RC-UK |> Make an inbound call>Navigate to the incoming call page> Click the 'Forward' button> Input{dialing text}in the 'Custom number' filed > Click the'Forward' button. |7 |8 |3135 0033 |Match valid PSTN |12 |(12) 3135 0033 |PSTN |(12) 3135 0033 | */ - import { Category } from '@ringcentral-integration/commons/interfaces/NumberParserResponse.interface'; -import { waitUntilTo } from '@ringcentral-integration/utils'; import { forwardFn, transferFn } from '@ringcentral-integration/mock'; import { And, @@ -35,8 +33,9 @@ import { title, When, } from '@ringcentral-integration/test-utils'; -import type { Context } from '../../../../../interfaces'; +import { waitUntilTo } from '@ringcentral-integration/utils'; +import type { Context } from '../../../../../interfaces'; import type { StepProp } from '../../../../../lib/step'; import { CheckConferenceCallControlPage, @@ -270,7 +269,7 @@ export class EDPEnabledAndForward extends Step { { mockData.regionalSettings.homeCountry = { - uri: 'https://api-rcapps-xmnup.rclabenv.com/restapi/v1.0/dictionary/country/75', + uri: 'https://api-rcapps-xmnuplabs_domain/restapi/v1.0/dictionary/country/75', id: '75', name: 'France', isoCode: 'FR', diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/RegionSetting/RCI-4372.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/RegionSetting/RCI-4372.test.tsx index cfcc05faf9..e257533c84 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/RegionSetting/RCI-4372.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/RegionSetting/RCI-4372.test.tsx @@ -8,9 +8,9 @@ * Entry point(/s): * Open CTI */ - import { autorun, + common, examples, Given, it, @@ -32,6 +32,7 @@ import { NavigateTo } from '../../../../../../steps/Router'; @autorun(test.skip) @it @p2 +@common @title('Open region setting for all RC brand') export class ShowRegionSettings extends Step { Login: StepProp = CommonLogin; diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/RegionSetting/RCI-4373.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/RegionSetting/RCI-4373.test.tsx index 49222079f5..da24573d5a 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/RegionSetting/RCI-4373.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/RegionSetting/RCI-4373.test.tsx @@ -8,7 +8,6 @@ * Entry point(/s): * Open CTI */ - import { autorun, examples, @@ -38,8 +37,8 @@ export class AreaCodeInRegionPage extends Step { CreateMock: StepProp | null = null; @examples(` - | countryName | selectedCountryCallingCode | isoCode | - | 'United Kingdom' | '44' | 'GB' | + | countryName | countryCallingCode | isoCode | + | 'United Kingdom' | '44' | 'GB' | `) run() { const { CreateMock, Login } = this; @@ -48,23 +47,19 @@ export class AreaCodeInRegionPage extends Step { [ + action={({ countryName, countryCallingCode, isoCode }: any) => [ CreateMock, { const plan = mockData.find( - (plan) => plan.id === selectedCountryCallingCode, + (plan) => plan.id === countryCallingCode, ); if (!plan) { mockData.push({ - uri: `https://platform.ringcentral.com/restapi/v1.0/dictionary/country/${selectedCountryCallingCode}`, - id: selectedCountryCallingCode, + uri: `https://platform.ringcentral.com/restapi/v1.0/dictionary/country/${countryCallingCode}`, + id: countryCallingCode, name: countryName, - callingCode: selectedCountryCallingCode, + callingCode: countryCallingCode, isoCode, }); } diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/RegionSetting/RCI-4374.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/RegionSetting/RCI-4374.test.tsx index 7dba449197..6099b283e8 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/RegionSetting/RCI-4374.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/RegionSetting/RCI-4374.test.tsx @@ -8,7 +8,6 @@ * Entry point(/s): * Open CTI> Go to the 'Settings' page> Go to the 'Settings' page */ - import { autorun, examples, @@ -52,7 +51,7 @@ export class RegionPage extends Step { { mockData.regionalSettings.homeCountry = { - uri: 'https://api-rcapps-xmnup.rclabenv.com/restapi/v1.0/dictionary/country/75', + uri: 'https://api-rcapps-xmnuplabs_domain/restapi/v1.0/dictionary/country/75', id: '75', name: 'France', isoCode: 'FR', diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/RegionSetting/RCI-4461.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/RegionSetting/RCI-4461.test.tsx index 499576f252..37efbe67d8 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/RegionSetting/RCI-4461.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/EDP/Checkpoint/RegionSetting/RCI-4461.test.tsx @@ -11,7 +11,6 @@ * Entry point(/s): * Open CTI >User login to the CTI app with {Account} */ - import { autorun, common, @@ -95,7 +94,13 @@ export class RCI4461 extends Step { /> } + action={ + // (+33) France + + } /> ); diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/Others/Checkpoint/RCI-1850.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/Others/Checkpoint/RCI-1850.test.tsx index 436fdf1881..d4ebb90f59 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/Others/Checkpoint/RCI-1850.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/Others/Checkpoint/RCI-1850.test.tsx @@ -17,7 +17,7 @@ * Entry point(/s): * */ - +import accountInfoBody from '@ringcentral-integration/mock/src/platform/data/accountInfo.json'; import { p2, it, @@ -28,18 +28,17 @@ import { title, When, } from '@ringcentral-integration/test-utils'; -import accountInfoBody from '@ringcentral-integration/mock/src/platform/data/accountInfo.json'; import type { StepProp } from '../../../../../lib/step'; import { CommonLogin } from '../../../../../steps/CommonLogin'; +import { CheckContactItemExistInList } from '../../../../../steps/ContactsView'; +import { CreateInstance } from '../../../../../steps/CreateInstance'; import { CreateMock, MockExtensionInfo, MockExtensionsList, } from '../../../../../steps/Mock'; import { NavigateToContacts } from '../../../../../steps/Navigate'; -import { CheckContactItemExistInList } from '../../../../../steps/ContactsView'; -import { CreateInstance } from '../../../../../steps/CreateInstance'; @autorun(test) @it diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/Others/Checkpoint/RCI-4493.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/Others/Checkpoint/RCI-4493.test.tsx index 2d3a593548..af5ceacb8c 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/Others/Checkpoint/RCI-4493.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/Others/Checkpoint/RCI-4493.test.tsx @@ -10,7 +10,6 @@ | RCCA |Send a message |RCUS |2507654321 |The message should be sent | */ - import { Category } from '@ringcentral-integration/commons/interfaces/NumberParserResponse.interface'; import { p1, @@ -26,10 +25,13 @@ import { common, And, } from '@ringcentral-integration/test-utils'; + +import { generateDialPlanData } from '../../../../../__mock__/generateDialPlanData'; import type { StepProp } from '../../../../../lib/step'; import { CheckCallControlPage, MakeCall } from '../../../../../steps/Call'; import { CommonLogin } from '../../../../../steps/CommonLogin'; import { CreateInstance } from '../../../../../steps/CreateInstance'; +import { SendSMS, CheckConversationPage } from '../../../../../steps/Messages'; import { CreateMock, MockExtensionInfo, @@ -45,8 +47,6 @@ import { } from '../../../../../steps/Mock'; import { NavigateTo } from '../../../../../steps/Router'; import { CheckCountryCodeField } from '../../../../../steps/Settings'; -import { SendSMS, CheckConversationPage } from '../../../../../steps/Messages'; -import { generateDialPlanData } from '../../../../../__mock__/generateDialPlanData'; @autorun(test) @it @@ -61,11 +61,11 @@ export class RCI4493 extends Step { ); CreateMock: StepProp = CreateMock; @examples(` - | regionCode | regionName | phoneNumber | e164ParsedNumber | parsedNumber | parsedCountry | textMessage | - | 'US' | '(+1) United States' | '6501234567' | '+16501234567' | '(650) 123-4567' | 'CA' | 'test message' | - | 'CA' | '(+1) Canada' | '2507654321' | '+12507654321' | '(250) 765-4321' | 'US' | 'test message' | - | 'PR' | '(+1) Puerto Rico' | '2507654321' | '+12507654321' | '(250) 765-4321' | 'US' | 'test message' | - | 'CA' | '(+1) Canada' | '4801234567' | '+14801234567' | '(480) 123-4567' | 'PR' | 'test message' | + | regionCode | countryCallingCode | countryName | phoneNumber | e164ParsedNumber | parsedNumber | parsedCountry | textMessage | + | 'US' | '1' | 'United States' | '6501234567' | '+16501234567' | '(650) 123-4567' | 'CA' | 'test message' | + | 'CA' | '1' | 'Canada' | '2507654321' | '+12507654321' | '(250) 765-4321' | 'US' | 'test message' | + | 'PR' | '1' | 'Puerto Rico' | '2507654321' | '+12507654321' | '(250) 765-4321' | 'US' | 'test message' | + | 'CA' | '1' | 'Canada' | '4801234567' | '+14801234567' | '(480) 123-4567' | 'PR' | 'test message' | `) run() { const { Login, CreateMock } = this; @@ -137,8 +137,11 @@ export class RCI4493 extends Step { /> ( - + action={async ({ countryCallingCode, countryName }: any) => ( + )} /> Make a inbound call >Navigate to incoming call page > Click 'Forward' button |8 |8 |3135 0033 |Not Match extbut valid PSTN |12 |(12) 3135 0033 |PSTN |(12) 3135 0033 | */ - import { Category } from '@ringcentral-integration/commons/interfaces/NumberParserResponse.interface'; -import { waitUntilTo } from '@ringcentral-integration/utils'; import { forwardFn, transferFn } from '@ringcentral-integration/mock'; import { And, @@ -38,7 +36,9 @@ import { title, When, } from '@ringcentral-integration/test-utils'; +import { waitUntilTo } from '@ringcentral-integration/utils'; +import { generateDialPlanData } from '../../../../../__mock__/generateDialPlanData'; import type { StepProp } from '../../../../../lib/step'; import { CheckConferenceCallControlPage, @@ -61,7 +61,6 @@ import { } from '../../../../../steps/Mock'; import { NavigateTo } from '../../../../../steps/Router'; import { SetAreaCode } from '../../../../../steps/Settings'; -import { generateDialPlanData } from '../../../../../__mock__/generateDialPlanData'; @autorun(test.skip) @it diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/SmartDialPlan/SdpEnableAndDialedNumber/Checkpoint/RCI-4388.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/SmartDialPlan/SdpEnableAndDialedNumber/Checkpoint/RCI-4388.test.tsx index edc889dd34..4b6bec3824 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/SmartDialPlan/SdpEnableAndDialedNumber/Checkpoint/RCI-4388.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/SmartDialPlan/SdpEnableAndDialedNumber/Checkpoint/RCI-4388.test.tsx @@ -26,7 +26,6 @@ | RC-UK |6 |8 |3135 0033 |Match valid PSTN |12 |(12) 3135 0033 |PSTN |(12) 3135 0033 | */ - import { Category } from '@ringcentral-integration/commons/interfaces/NumberParserResponse.interface'; import { autorun, @@ -41,6 +40,7 @@ import { title, When, } from '@ringcentral-integration/test-utils'; + import type { StepFunction } from '../../../../../../lib/step'; import { CheckCallControlPage as BaseCheckCallControlPage, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/SmartDialPlan/SdpEnableAndDialedNumber/Checkpoint/RCI-4389.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/SmartDialPlan/SdpEnableAndDialedNumber/Checkpoint/RCI-4389.test.tsx index 4d6945f0b8..ce90725e3f 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/SmartDialPlan/SdpEnableAndDialedNumber/Checkpoint/RCI-4389.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/CrossModule/SmartDialPlan/SdpEnableAndDialedNumber/Checkpoint/RCI-4389.test.tsx @@ -27,7 +27,6 @@ | RC-UK |8 |8 |3135 0033 |Not Match extbut valid PSTN |12 |(12) 3135 0033 |PSTN |(12) 3135 0033 | */ - import { Category } from '@ringcentral-integration/commons/interfaces/NumberParserResponse.interface'; import type { StepFunction } from '@ringcentral-integration/test-utils'; import { @@ -46,9 +45,15 @@ import { } from '@ringcentral-integration/test-utils'; import { screen } from '@testing-library/react'; +import { generateDialPlanData } from '../../../../../../__mock__/generateDialPlanData'; import type { StepProp } from '../../../../../../lib/step'; - +import { + CallButtonBehavior, + CheckCallControlPage as BaseCheckCallControlPage, + MakeCall, +} from '../../../../../../steps/Call'; import { CommonLogin } from '../../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../../steps/CreateInstance'; import { CreateMock, generateCallLogData, @@ -60,13 +65,6 @@ import { MockNumberParserV2, MockPermission, } from '../../../../../../steps/Mock'; -import { - CallButtonBehavior, - CheckCallControlPage as BaseCheckCallControlPage, - MakeCall, -} from '../../../../../../steps/Call'; -import { CreateInstance } from '../../../../../../steps/CreateInstance'; -import { generateDialPlanData } from '../../../../../../__mock__/generateDialPlanData'; import { NavigateToHistory as BaseNavigateToHistory } from '../../../../../../steps/Navigate'; @autorun(test) @@ -79,7 +77,6 @@ export class DialPSTNSDPEnabled extends Step { ); CreateMock: StepProp | null = CreateMock; - historyTestId = 'History'; CheckNumberInHistoryPage: StepFunction = ({ e164ParsedNumber, }: any) => { @@ -97,7 +94,6 @@ export class DialPSTNSDPEnabled extends Step { const { Login, CreateMock, - historyTestId, NavigateToHistory, CheckCallControlPage, CheckNumberInHistoryPage, @@ -196,10 +192,7 @@ export class DialPSTNSDPEnabled extends Step { desc="hang up current call" action={} /> - } - /> + } /> } + action={ + + } + /> + ]} + /> + { + expect(phone.analytics.enableMixpanel).toEqual(true); + }) as StepFunction + } + /> + , + ]} + /> + { + expect(phone.analytics.enableMixpanel).toEqual(false); + }) as StepFunction, + UseRealTimers, + ]} /> } + action={[ + ToggleEnv, + , + ]} /> Go to {Entry} + * > Do the {Action} to call{Intercepted Url} + */ +import { + p2, + it, + autorun, + Given, + Scenario, + Step, + Then, + title, + When, + WaitForRenderReady, + UseFakeTimers, + AdvanceTimersByTime, + UseRealTimers, + StepProp, + examples, + common, +} from '@ringcentral-integration/test-utils'; + +import { + CheckAlertAutoDismiss, + CheckContainsAlertMessage, +} from '../../../../../steps/Alert'; +import { CheckLimitedModeBadge } from '../../../../../steps/Badge/actions/CheckLimitedModeBadge'; +import { CommonLogin } from '../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../steps/CreateInstance'; +import { SendSMS } from '../../../../../steps/Messages'; +import { + CreateMock, + MockNumberParserV2, + MockPresence, + MockLimitedPutPresence, +} from '../../../../../steps/Mock'; +import { NavigateTo } from '../../../../../steps/Router'; +import { SetPresenceStatus } from '../../../../../steps/Settings'; + +@autorun(test) +@it +@p2 +@common +@title('Verify the App will go into limited mode') +export class LimitedMode extends Step { + Login: StepProp = (props) => ( + + ); + CreateMock: StepProp = CreateMock; + + @examples([ + { + action: ( + <> + + + + ), + }, + { + action: ( + <> + + + + ), + }, + ]) + run() { + const { Login, CreateMock } = this; + return ( + + , + { + return { + status: 503, + errorCode: 'CMN-211', + errors: [{ errorCode: 'CMN-211' }], + }; + }} + />, + , + , + Login, + UseFakeTimers, + this.example.action, + ]} + /> + + , + ]} + /> + , WaitForRenderReady]} + /> + + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/LimitedMode/Checkpoint/RCI-2643.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/LimitedMode/Checkpoint/RCI-2643.test.tsx new file mode 100644 index 0000000000..555b2b2cc0 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/LimitedMode/Checkpoint/RCI-2643.test.tsx @@ -0,0 +1,123 @@ +/** + * RCI-2643: Verify the App will go back to normal mode when health check returns 200 + * https://test_it_domain/test-cases/RCI-2643 + * Preconditions: + * 1. It have already installed the CTI. + * 2. The CTI is in 'Limited Mode'. + * 3. Set the{Intercepted Health Check Url}response status to 200 + * + | Intercepted Health Check Url | + | p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px 'Helvetica Neue'; color: #dca10d} p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px 'Helvetica Neue'}https://api-rcapps-xmnup.lab.nordigy.ru/restapi/v1.0/status | + + * Entry point(/s): + * + */ +import { + p2, + it, + autorun, + Given, + Scenario, + Step, + Then, + title, + When, + WaitForRenderReady, + UseFakeTimers, + AdvanceTimersByTime, + UseRealTimers, + StepProp, +} from '@ringcentral-integration/test-utils'; + +import { CheckLimitedModeBadge } from '../../../../../steps/Badge/actions/CheckLimitedModeBadge'; +import { CheckGetStatusApi } from '../../../../../steps/Badge/checks/CheckGetStatusApi'; +import { CommonLogin } from '../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../steps/CreateInstance'; +import { SendSMS } from '../../../../../steps/Messages'; +import { + CreateMock, + MockNumberParserV2, + MockGetStatus, +} from '../../../../../steps/Mock'; +import { NavigateTo } from '../../../../../steps/Router'; + +@autorun(test) +@it +@p2 +@title( + 'Verify the App will go back to normal mode when health check returns 200', +) +export class BackToNormalModeWhenHealthCheckSuccess extends Step { + Login: StepProp = (props) => ( + + ); + CreateMock: StepProp = CreateMock; + retryAfter = 1; + run() { + const { Login, CreateMock } = this; + return ( + + , + , + { + return { + status: 503, + errorCode: 'CMN-211', + errors: [{ errorCode: 'CMN-211' }], + }; + }} + />, + Login, + UseFakeTimers, + , + SendSMS, + WaitForRenderReady, + CheckLimitedModeBadge, + ]} + /> + , + WaitForRenderReady, + , + ]} + /> + , + CheckLimitedModeBadge, + ]} + /> + , + UseRealTimers, + ]} + /> + , + , + ]} + /> + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/LimitedMode/Checkpoint/RCI-2644.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/LimitedMode/Checkpoint/RCI-2644.test.tsx new file mode 100644 index 0000000000..4b4d8c6461 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/LimitedMode/Checkpoint/RCI-2644.test.tsx @@ -0,0 +1,116 @@ +/** + * RCI-2644: Verify the App will keep the health check when in limited mode + * https://test_it_domain/test-cases/RCI-2644 + * Preconditions: + * 1. It have already installed the CTI. + * 2. The CTI is in 'Limited Mode'. + * 3. Set the{Intercepted Health Check Url}response status to 503 & error code CMN-211 + * + | Intercepted Health Check Url |Retry-After Value |Response Header | + | https://api-rcapps-xmnup.lab.nordigy.ru/restapi/v1.0/status |1 | p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px 'Helvetica Neue'}Retry-After: 1 | + + * Entry point(/s): + * + */ +import { + p2, + it, + autorun, + Given, + Scenario, + Step, + Then, + title, + When, + WaitForRenderReady, + UseFakeTimers, + AdvanceTimersByTime, + UseRealTimers, + StepProp, +} from '@ringcentral-integration/test-utils'; + +import { CheckLimitedModeBadge } from '../../../../../steps/Badge/actions/CheckLimitedModeBadge'; +import { CheckGetStatusApi } from '../../../../../steps/Badge/checks/CheckGetStatusApi'; +import { CommonLogin } from '../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../steps/CreateInstance'; +import { SendSMS } from '../../../../../steps/Messages'; +import { + CreateMock, + MockNumberParserV2, + MockGetStatus, +} from '../../../../../steps/Mock'; +import { NavigateTo } from '../../../../../steps/Router'; + +@autorun(test) +@it +@p2 +@title('Verify the App will keep the health check when in limited mode') +export class KeepHealthCheckInLAMode extends Step { + Login: StepProp = (props) => ( + + ); + CreateMock: StepProp = CreateMock; + retryAfter = 1; + run() { + const { Login, CreateMock } = this; + return ( + + , + , + { + return { + status: 503, + errorCode: 'CMN-211', + errors: [{ errorCode: 'CMN-211' }], + }; + }} + />, + Login, + UseFakeTimers, + , + SendSMS, + WaitForRenderReady, + CheckLimitedModeBadge, + ]} + /> + , WaitForRenderReady]} + /> + ]} + /> + , + WaitForRenderReady, + UseRealTimers, + ]} + /> + , CheckLimitedModeBadge]} + /> + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2602.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2602.test.tsx new file mode 100644 index 0000000000..e3faaf1598 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2602.test.tsx @@ -0,0 +1,103 @@ +/** + * RCI-2602: Verify the app enter offline mode when disconnect network + * https://test_it_domain/test-cases/RCI-2602 + * Preconditions: + * User login CTI app + * Account type(/s): + * RC US/CA/UK/EU/AU + * Extension type(/s): + * Entry point(/s): + * Entry point(/s): + * The user is making an inbound /outbound call + */ +import { + p2, + it, + autorun, + Scenario, + Step, + Then, + title, + When, + common, + UseFakeTimers, +} from '@ringcentral-integration/test-utils'; + +import type { Context } from '../../../../../interfaces'; +import type { StepFunction } from '../../../../../lib/step'; +import { CheckContainsAlertMessage } from '../../../../../steps/Alert'; +import { CommonLogin } from '../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../steps/CreateInstance'; +import { + MockNetworkOffline, + CheckConnectivityBadge, +} from '../../../../../steps/Ha'; +import { + CreateMock as CommonCreateMock, + MockMessageSync, +} from '../../../../../steps/Mock'; +import { NavigateTo } from '../../../../../steps/Router'; +import { CheckCallButtonDisabled } from '../../../../../steps/dialer'; + +@autorun(test) +@common +@it +@p2 +@title('Verify the app enter offline mode when disconnect network') +export class RCI2602 extends Step { + CreateMock: StepFunction = CommonCreateMock; + Login: StepFunction = (props) => ( + + ); + run() { + const { CreateMock, Login } = this; + let checkConnectionFuncSpy: jest.SpyInstance; + return ( + [CreateMock, MockMessageSync, Login]} + > + [ + () => { + const { connectivityMonitor } = context.phone; + // spy on request GET https://pubsub.pubnub.com/time/0 + checkConnectionFuncSpy = jest + .spyOn(connectivityMonitor, '_checkConnectionFunc') + .mockImplementation(() => { + throw new Error(); + }); + }, + , + , + ]} + /> + [ + , + , + , + , + () => { + checkConnectionFuncSpy.mockReset(); + jest.advanceTimersByTime(5000 + 1000); + jest.useRealTimers(); + expect(checkConnectionFuncSpy).toHaveBeenCalledTimes(1); + }, + ]} + /> + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2603.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2603.test.tsx new file mode 100644 index 0000000000..132de0b9a7 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2603.test.tsx @@ -0,0 +1,104 @@ +/** + * RCI-2603: Verify the app should recover normal from offline mode when connected network + * https://test_it_domain/test-cases/RCI-2603 + * Preconditions: + * User login CTI app + * The user has entered the offline mode + * Note(/s):OpenBackground > Network > Network conditions > 'Network throttling > 'Select 'Offline' + * Account type(/s): + * RC US/CA/UK/EU/AU + * Extension type(/s): + * Entry point(/s): + * + */ +import { + p2, + it, + autorun, + Scenario, + Step, + Then, + title, + When, + common, + UseFakeTimers, +} from '@ringcentral-integration/test-utils'; + +import type { Context } from '../../../../../interfaces'; +import type { StepFunction } from '../../../../../lib/step'; +import { + MakeOutboundCall, + CheckCallControlPage, +} from '../../../../../steps/Call'; +import { CommonLogin } from '../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../steps/CreateInstance'; +import { + MockNetworkOffline, + MockNetworkOnline, + CheckConnectivityBadge, +} from '../../../../../steps/Ha'; +import { + CreateMock as CommonCreateMock, + MockMessageSync, +} from '../../../../../steps/Mock'; +import { NavigateTo } from '../../../../../steps/Router'; +import { CheckCallButtonActive } from '../../../../../steps/dialer'; + +@autorun(test) +@common +@it +@p2 +@title( + 'Verify the app should recover normal from offline mode when connected network', +) +export class RCI2603 extends Step { + CreateMock: StepFunction = CommonCreateMock; + Login: StepFunction = (props) => ( + + ); + run() { + const { CreateMock, Login } = this; + let checkConnectionFuncSpy: jest.SpyInstance; + return ( + [CreateMock, MockMessageSync, Login, MockNetworkOffline]} + > + [ + () => { + const { connectivityMonitor } = context.phone; + // spy on request GET https://pubsub.pubnub.com/time/0 + checkConnectionFuncSpy = jest + .spyOn(connectivityMonitor, '_checkConnectionFunc') + .mockImplementation(jest.fn()); + }, + , + , + ]} + /> + [ + , + , + , + () => { + checkConnectionFuncSpy.mockReset(); + jest.advanceTimersByTime(5000 + 1000); + jest.useRealTimers(); + expect(checkConnectionFuncSpy).not.toHaveBeenCalled(); + }, + , + , + ]} + /> + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2607.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2607.test.tsx new file mode 100644 index 0000000000..501d90477a --- /dev/null +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2607.test.tsx @@ -0,0 +1,74 @@ +/** + * RCI-2607: Verify the alert message when user clicks the offline badge + * https://test_it_domain/test-cases/RCI-2607 + * Preconditions: + * User login CTI app + * Account type(/s): + * RC US/CA/UK/EU/AU + * Extension type(/s): + * Entry point(/s): + * > Disconnect the network then the app enters the offline mode + * Entry point(/s): + * > Disconnect the network then the app enters the offline mode + * Note(/s):OpenBackground > Network > Network conditions > 'Network throttling > 'Select 'Offline' + */ +import { + p2, + it, + autorun, + Scenario, + Step, + Then, + title, + When, + common, +} from '@ringcentral-integration/test-utils'; + +import type { StepFunction } from '../../../../../lib/step'; +import { CheckContainsAlertMessage } from '../../../../../steps/Alert'; +import { CommonLogin } from '../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../steps/CreateInstance'; +import { + ClickConnectivityBadge, + MockNetworkOffline, +} from '../../../../../steps/Ha'; +import { + CreateMock as CommonCreateMock, + MockMessageSync, +} from '../../../../../steps/Mock'; + +@autorun(test) +@common +@it +@p2 +@title('Verify the alert message when user clicks the offline badge') +export class RCI2607 extends Step { + CreateMock: StepFunction = CommonCreateMock; + Login: StepFunction = (props) => ( + + ); + run() { + const { CreateMock, Login } = this; + return ( + [CreateMock, MockMessageSync, Login, MockNetworkOffline]} + > + } + /> + + } + /> + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2609.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2609.test.tsx new file mode 100644 index 0000000000..3135f8b3fc --- /dev/null +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2609.test.tsx @@ -0,0 +1,97 @@ +/** + * RCI-2609: Verify the app enter offline mode when platform is not accessible + * https://test_it_domain/test-cases/RCI-2609 + * Preconditions: + * User login CTI app + * Account type(/s): + * RC US/CA/UK/EU/AU + * Extension type(/s): + * Entry point(/s): + * Entry point(/s): + * The user opens the app + */ +import { + p2, + it, + autorun, + Scenario, + Step, + Then, + title, + When, + common, + UseFakeTimers, +} from '@ringcentral-integration/test-utils'; + +import type { StepFunction } from '../../../../../lib/step'; +import { CheckContainsAlertMessage } from '../../../../../steps/Alert'; +import { CommonLogin } from '../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../steps/CreateInstance'; +import { + MockCheckConnection, + CheckConnectivityBadge, +} from '../../../../../steps/Ha'; +import { + CreateMock as CommonCreateMock, + MockMessageSync, +} from '../../../../../steps/Mock'; +import { NavigateTo } from '../../../../../steps/Router'; +import { CheckCallButtonDisabled } from '../../../../../steps/dialer'; + +@autorun(test) +@common +@it +@p2 +@title('Verify the app enter offline mode when platform is not accessible') +export class RCI2609 extends Step { + CreateMock: StepFunction = CommonCreateMock; + Login: StepFunction = (props) => ( + + ); + run() { + const { CreateMock, Login } = this; + const checkConnectionFuncSpy = jest.fn(); + return ( + [CreateMock, MockMessageSync, Login]} + > + [ + , + { + checkConnectionFuncSpy(); + throw new Error(); + }} + />, + ]} + /> + [ + , + , + , + , + () => { + checkConnectionFuncSpy.mockReset(); + jest.advanceTimersByTime(5000 + 1000); + jest.useRealTimers(); + expect(checkConnectionFuncSpy).toHaveBeenCalledTimes(1); + }, + ]} + /> + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2618.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2618.test.tsx new file mode 100644 index 0000000000..28ac42f75e --- /dev/null +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2618.test.tsx @@ -0,0 +1,104 @@ +/** + * RCI-2618: Verify the app should recover normal mode when platform is accessible + * https://test_it_domain/test-cases/RCI-2618 + * Preconditions: + * Open the Charles + * User login CTI app + * Account type(/s): + * RC US/CA/UK/EU/AU + * Extension type(/s): + * Entry point(/s): + * The user opens the app-> Use Charles to catch this request and abort the response to simulate that platform is not accessible + * Note(/s): + * Entry point(/s): + * The user opens the app-> Use Charles to catch this request and abort the response to simulate that platform is not accessible + * Note(/s): + * Request:'https://pubsub.pubnub.com/time/0' + */ +import { + p2, + it, + autorun, + Scenario, + Step, + Then, + title, + When, + common, + UseFakeTimers, +} from '@ringcentral-integration/test-utils'; + +import type { StepFunction } from '../../../../../lib/step'; +import { + MakeOutboundCall, + CheckCallControlPage, +} from '../../../../../steps/Call'; +import { CommonLogin } from '../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../steps/CreateInstance'; +import { + MockCheckConnection, + CheckConnectivityBadge, +} from '../../../../../steps/Ha'; +import { + CreateMock as CommonCreateMock, + MockMessageSync, +} from '../../../../../steps/Mock'; +import { NavigateTo } from '../../../../../steps/Router'; +import { CheckCallButtonActive } from '../../../../../steps/dialer'; + +@autorun(test) +@common +@it +@p2 +@title('Verify the app should recover normal mode when platform is accessible') +export class RCI2618 extends Step { + CreateMock: StepFunction = CommonCreateMock; + Login: StepFunction = (props) => ( + + ); + run() { + const { CreateMock, Login } = this; + const checkConnectionFuncSpy = jest.fn(); + return ( + [CreateMock, MockMessageSync, Login]} + > + [ + , + { + checkConnectionFuncSpy(); + // throw new Error(); + }} + />, + ]} + /> + [ + , + , + , + () => { + checkConnectionFuncSpy.mockReset(); + jest.advanceTimersByTime(5000 + 1000); + jest.useRealTimers(); + expect(checkConnectionFuncSpy).not.toHaveBeenCalled(); + }, + , + , + ]} + /> + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2621.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2621.test.tsx new file mode 100644 index 0000000000..e965e64682 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2621.test.tsx @@ -0,0 +1,148 @@ +/** + * RCI-2621: Verify the app enter offline mode when API return 503 in the Ringout mode + * https://test_it_domain/test-cases/RCI-2621 + * Preconditions: + * Open the charles + * API Request: + * + | API Request |Request URL | + | 1 |https://api-rcapps-xmnup.lab.nordigy.ru/restapi/v1.0/account/~/extension/~/sms | + | 2 |https://api-rcapps-xmnup.lab.nordigy.ru/restapi/oauth/token | + | 3 |https://api-rcapps-xmnup.lab.nordigy.ru/restapi/v1.0/status | + + * Account type(/s): + * RC US/CA/UK/EU/AU + * Extension type(/s): + * Entry point(/s): + * > Login app with RC account + * Entry point(/s): + * > Login app with RC account + * > Change the calling setting to Ring out types + */ +import type { AvailabilityMonitor } from '@ringcentral-integration/commons/modules/AvailabilityMonitor'; +import { + And, + Given, + Scenario, + Step, + Then, + When, + autorun, + common, + it, + p2, + title, +} from '@ringcentral-integration/test-utils'; + +import type { Context } from '../../../../../interfaces'; +import type { StepFunction } from '../../../../../lib/step'; +import { CheckContainsAlertMessage } from '../../../../../steps/Alert'; +import { RefreshToken } from '../../../../../steps/Common'; +import { CommonLogin } from '../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../steps/CreateInstance'; +import { MockGetStatus, CheckConnectivityBadge } from '../../../../../steps/Ha'; +import { + CreateMock as CommonCreateMock, + MockGetPhoneNumber, + MockGetRingOut, + MockPostOauthToken, +} from '../../../../../steps/Mock'; +import { NavigateTo } from '../../../../../steps/Router'; +import { + CheckCallWithOption, + ClickSaveButton, + ExpandCallingSettingDropdown, + SelectCallingSetting, +} from '../../../../../steps/Settings'; +import { CheckCallButtonDisabled } from '../../../../../steps/dialer'; + +@autorun(test) +@common +@it +@p2 +@title( + 'Verify the app enter offline mode when API return 503 in the Ringout mode', +) +export class RCI2621 extends Step { + CreateMock: StepFunction = CommonCreateMock; + Login: StepFunction = (props) => ( + + ); + run() { + const { CreateMock, Login } = this; + let availabilityMonitor: AvailabilityMonitor; + let retrySpy: jest.SpyInstance; + const checkConnectionFuncSpy = jest.fn(); + return ( + + [ + CreateMock, + MockGetPhoneNumber, + MockGetRingOut, + + // [API Request2] -> 503 + , + ]} + /> + [ + Login, + // Mock [API Request3] after logged in + { + checkConnectionFuncSpy(); + return data; + }} + />, + // Spy on [API Request3] after logged in + () => { + availabilityMonitor = context.phone.availabilityMonitor; + expect(availabilityMonitor).toBeTruthy(); + retrySpy = jest.spyOn(availabilityMonitor, '_retry'); + }, + ]} + /> + [ + // Select RingOut mode + , + , + , + , + , + ]} + /> + []} + /> + [ + , + , + , + , + () => { + expect(retrySpy).toHaveBeenCalledTimes(1); + expect(availabilityMonitor._healthRetryTime).toBe(60 * 1000); + expect(checkConnectionFuncSpy).toHaveBeenCalledTimes(1); // check [API Request3] + }, + ]} + /> + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2625.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2625.test.tsx new file mode 100644 index 0000000000..301bc9bbbf --- /dev/null +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2625.test.tsx @@ -0,0 +1,183 @@ +/** + * RCI-2625: Verify the app should recover normal mode when API return 200 in the Ringout mode + * https://test_it_domain/test-cases/RCI-2625 + * Preconditions: + * Open the Charles + * Change the calling setting to Ring out types + * API Request: + * + | API Request |Request URL | + | 1 |https://api-rcapps-xmnup.lab.nordigy.ru/restapi/v1.0/account/~/extension/~/sms | + | 2 |https://api-rcapps-xmnup.lab.nordigy.ru/restapi/oauth/token | + | 3 |https://api-rcapps-xmnup.lab.nordigy.ru/restapi/v1.0/status | + + * Account type(/s): + * RC US/CA/UK/EU/AU + * Extension type(/s): + * Entry point(/s): + * > Login app + * > Change the valid time of accessToken by Charles or change some API{API Request1}response status to 401 + * Entry point(/s): + * > Login app + * > Change the valid time of accessToken by Charles or change some API{API Request1}response status to 401 + * > Simulate Refresh token API{API Request2}returns 503 use Charles + */ +import type { AvailabilityMonitor } from '@ringcentral-integration/commons/modules/AvailabilityMonitor'; +import { Ringout } from '@ringcentral-integration/commons/modules/Ringout'; +import { + And, + Given, + Scenario, + Step, + Then, + When, + autorun, + common, + it, + p2, + title, +} from '@ringcentral-integration/test-utils'; + +import type { Context } from '../../../../../interfaces'; +import type { StepFunction } from '../../../../../lib/step'; +import { MakeOutboundCall } from '../../../../../steps/Call'; +import { RefreshToken } from '../../../../../steps/Common'; +import { CommonLogin } from '../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../steps/CreateInstance'; +import { CheckConnectivityBadge, MockGetStatus } from '../../../../../steps/Ha'; +import { + CreateMock as CommonCreateMock, + MockGetPhoneNumber, + MockGetRingOut, + MockPostOauthToken, +} from '../../../../../steps/Mock'; +import { NavigateTo } from '../../../../../steps/Router'; +import { + CheckCallWithOption, + ClickSaveButton, + ExpandCallingSettingDropdown, + SelectCallingSetting, +} from '../../../../../steps/Settings'; +import { CheckCallButtonActive } from '../../../../../steps/dialer'; + +@autorun(test) +@common +@it +@p2 +@title( + 'Verify the app should recover normal mode when API return 200 in the Ringout mode', +) +export class RCI2625 extends Step { + CreateMock: StepFunction = CommonCreateMock; + Login: StepFunction = (props) => ( + + ); + run() { + const { CreateMock, Login } = this; + let ringout: Ringout; + let fetchRingoutStatusSpy: jest.SpyInstance; + const checkConnectionFuncSpy = jest.fn(); + let retrySpy: jest.SpyInstance; + return ( + + [ + CreateMock, + MockGetPhoneNumber, + MockGetRingOut, + + // [API Request2] -> 503 + , + ]} + /> + [ + Login, + // Mock [API Request3] after logged in + { + checkConnectionFuncSpy(); + return data; + }} + />, + () => { + // AvailabilityMonitor module + const availabilityMonitor: AvailabilityMonitor = + context.phone.availabilityMonitor; + expect(availabilityMonitor).toBeTruthy(); + retrySpy = jest.spyOn(availabilityMonitor, '_retry'); + // Ringout module + ringout = context.phone.ringout; + expect(ringout).toBeTruthy(); + fetchRingoutStatusSpy = jest.spyOn( + ringout, + '_fetchRingoutStatus', + ); + }, + ]} + /> + [ + // Select RingOut mode + , + , + , + , + , + ]} + /> + [ + , + async () => { + expect(retrySpy).toHaveBeenCalledTimes(1); + expect(checkConnectionFuncSpy).toHaveBeenCalledTimes(1); + retrySpy.mockReset(); + checkConnectionFuncSpy.mockReset(); + }, + ]} + /> + [ + // [API Request2] -> 200 + , + , + ]} + /> + [ + , + , + , + () => { + expect(retrySpy).toHaveBeenCalledTimes(0); + expect(checkConnectionFuncSpy).toHaveBeenCalledTimes(0); + }, + , + async () => { + const ringoutId = fetchRingoutStatusSpy.mock.calls[0][0]; + expect(ringoutId).toBe( + 'Y3MxNjg2MzAwMTIyMTAwMTM1OTM5QDEwLjEzLjIyLjI1Mg', + ); + }, + ]} + /> + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2626.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2626.test.tsx new file mode 100644 index 0000000000..c33d875c2c --- /dev/null +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2626.test.tsx @@ -0,0 +1,169 @@ +/** + * RCI-2626: Verify the app enter offline mode when API return 503 in the Webphone is unavailable + * https://test_it_domain/test-cases/RCI-2626 + * Preconditions: + * Open the Charles + * The webphone is unavailable + * API Request: + * + | API Request |Request URL | + | 1 |https://api-rcapps-xmnup.lab.nordigy.ru/restapi/v1.0/account/~/extension/~/sms | + | 2 |https://api-rcapps-xmnup.lab.nordigy.ru/restapi/oauth/token | + | 3 | https://api-rcapps-xmnup.lab.nordigy.ru/restapi/v1.0/status | + + * Note(/s): Webphone unavailable:Simulate 3 times sipProvisionError + * >Intercept requesthttps://api-rcapps-xmnup.lab.nordigy.ru/restapi/v1.0/client-info/sip-provisionwith Charles + * > ChangeHTTPS response's status code to 500 + * >Retry another 2 times sipProvisionError + * > Enter WebRTC unavailable mode + * Account type(/s): + * RC US/CA/UK/EU/AU + * Extension type(/s): + * Entry point(/s): + * Entry point(/s): + * > Login app with rc account + */ +import type { AvailabilityMonitor } from '@ringcentral-integration/commons/modules/AvailabilityMonitor'; +import { + And, + p2, + it, + autorun, + Scenario, + Step, + Then, + title, + When, + common, + WaitForRenderReady, +} from '@ringcentral-integration/test-utils'; + +import type { Context } from '../../../../../interfaces'; +import type { StepFunction } from '../../../../../lib/step'; +import { CheckContainsAlertMessage } from '../../../../../steps/Alert'; +import { + MockSipProvision, + TriggerWebphoneEvent, +} from '../../../../../steps/Call/Webphone'; +import { RefreshToken } from '../../../../../steps/Common'; +import { CommonLogin } from '../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../steps/CreateInstance'; +import { MockGetStatus, CheckConnectivityBadge } from '../../../../../steps/Ha'; +import { + CreateMock as CommonCreateMock, + MockGetPhoneNumber, + MockMessageSync, + MockPostOauthToken, +} from '../../../../../steps/Mock'; +import { NavigateTo } from '../../../../../steps/Router'; +import { + CheckCallWithOption, + ClickSaveButton, + ExpandCallingSettingDropdown, + SelectCallingSetting, +} from '../../../../../steps/Settings'; +import { CheckCallButtonDisabled } from '../../../../../steps/dialer'; + +@autorun(test) +@common +@it +@p2 +@title( + 'Verify the app enter offline mode when API return 503 in the Webphone is unavailable', +) +export class RCI2626 extends Step { + CreateMock: StepFunction = CommonCreateMock; + Login: StepFunction = (props) => ( + + ); + run() { + const { CreateMock, Login } = this; + const checkConnectionFuncSpy = jest.fn(); + return ( + + [ + CreateMock, + MockMessageSync, + MockGetPhoneNumber, + Login, + ]} + /> + [ + // Select WebRTC mode + , + , + , + , + , + ]} + /> + [ + // Simulate sipProvisionError -> 500 + , + , + ]} + /> + [ + , + , + ]} + /> + [ + // oauth token -> 503 + , + // Mock [API Request3] after logged in + { + checkConnectionFuncSpy(); + throw new Error(); + }} + />, + , + ]} + /> + [ + , + , + , + , + , + (_: unknown, context: Context) => { + const availabilityMonitor: AvailabilityMonitor = + context.phone.availabilityMonitor; + expect(availabilityMonitor).toBeTruthy(); + expect(availabilityMonitor._healthRetryTime).toBe(60 * 1000); + expect(checkConnectionFuncSpy).toHaveBeenCalledTimes(1); // check [API Request3] + }, + ]} + /> + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2627.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2627.test.tsx new file mode 100644 index 0000000000..cbacc8dd44 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/OfflineMode/Checkpoint/RCI-2627.test.tsx @@ -0,0 +1,188 @@ +/** + * RCI-2627: Verify the app should recover normal mode when API return 200 when webphone is available + * https://test_it_domain/test-cases/RCI-2627 + * Preconditions: + * Open the Charles + * The webphone is unavailable + * The app is in the offline mode + * Account type(/s): + * RC US/CA/UK/EU/AU + * Extension type(/s): + * Entry point(/s): + * + */ +import type { ConnectivityMonitor } from '@ringcentral-integration/commons/modules/ConnectivityMonitor'; +import { + And, + p2, + it, + autorun, + Scenario, + Step, + Then, + title, + Given, + When, + common, + WaitForRenderReady, + UseFakeTimers, +} from '@ringcentral-integration/test-utils'; + +import type { Context } from '../../../../../interfaces'; +import type { StepFunction } from '../../../../../lib/step'; +import { + MakeOutboundCall, + CheckCallControlPage, +} from '../../../../../steps/Call'; +import { + MockSipProvision, + TriggerWebphoneEvent, +} from '../../../../../steps/Call/Webphone'; +import { RefreshToken } from '../../../../../steps/Common'; +import { CommonLogin } from '../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../steps/CreateInstance'; +import { CheckConnectivityBadge, MockGetStatus } from '../../../../../steps/Ha'; +import { + CreateMock as CommonCreateMock, + MockGetPhoneNumber, + MockMessageSync, + MockPostOauthToken, +} from '../../../../../steps/Mock'; +import { NavigateTo } from '../../../../../steps/Router'; +import { + CheckCallWithOption, + ClickSaveButton, + ExpandCallingSettingDropdown, + SelectCallingSetting, +} from '../../../../../steps/Settings'; +import { CheckCallButtonActive } from '../../../../../steps/dialer'; + +@autorun(test) +@common +@it +@p2 +@title( + 'Verify the app should recover normal mode when API return 200 when webphone is available', +) +export class RCI2627 extends Step { + CreateMock: StepFunction = CommonCreateMock; + Login: StepFunction = (props) => ( + + ); + run() { + const { CreateMock, Login } = this; + let checkConnectionFuncSpy: jest.SpyInstance; + return ( + + [ + CreateMock, + MockGetPhoneNumber, + MockMessageSync, + Login, + // Select WebRTC mode + , + , + , + , + , + // Spy on request GET https://pubsub.pubnub.com/time/0 + (_: unknown, context: Context) => { + const connectivityMonitor: ConnectivityMonitor = + context.phone.connectivityMonitor; + checkConnectionFuncSpy = jest + .spyOn(connectivityMonitor, '_checkConnectionFunc') + .mockImplementation(jest.fn()); + }, + ]} + /> + [ + // Simulate sipProvisionError -> 500 + , + , + // Check + , + , + ]} + /> + [ + // oauth token -> 503 + , + // [API Request3] -> 503 + { + throw new Error(); + }} + />, + , + // Check + , + , + ]} + /> + [ + // oauth token -> 200 + , + // [API Request3] -> 200 + , + , + // webphone is available + , + , + , + // Fake timers + , + ]} + /> + [ + // Badge 'Offline' disappear + , + , + + // Button enabled + , + , + + // No specified request + () => { + checkConnectionFuncSpy.mockReset(); + jest.advanceTimersByTime(5000 + 1000); + jest.useRealTimers(); + expect(checkConnectionFuncSpy).not.toHaveBeenCalled(); + }, + + // Can make call + , + , + ]} + /> + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/VoipMode/Checkpoint/RCI-2628.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/VoipMode/Checkpoint/RCI-2628.test.tsx index 1383510247..ba60bdc287 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/VoipMode/Checkpoint/RCI-2628.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/VoipMode/Checkpoint/RCI-2628.test.tsx @@ -16,7 +16,6 @@ * Entry point(/s): * > Login app with RC account */ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { p1, @@ -29,40 +28,39 @@ import { title, When, } from '@ringcentral-integration/test-utils'; -import { CommonLogin } from '../../../../../steps/CommonLogin'; + import { CheckAlertMessage } from '../../../../../steps/Alert'; -import { NavigateToHistory } from '../../../../../steps/Navigate'; -import { CheckCallIcon, CheckTextIcon } from '../../../../../steps/CallHistory'; +import { CheckVoipOnlyBadge } from '../../../../../steps/Badge'; import { CallButtonBehavior, CheckActiveCallExist, MakeCall, AnswerCall, } from '../../../../../steps/Call'; -import { CheckVoipOnlyBadge } from '../../../../../steps/Badge'; -import { MockPostOauthToken } from '../../../../../steps/Mock/MockPostOauthToken'; -import { CreateMock, MockPostSMS } from '../../../../../steps/Mock'; +import { CheckCallIcon, CheckTextIcon } from '../../../../../steps/CallHistory'; import { CheckLogBaseInfoActive } from '../../../../../steps/CallLog/checks/CheckCallLogBaseInfo'; +import { RefreshToken } from '../../../../../steps/Common'; +import { CommonLogin } from '../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../steps/CreateInstance'; import { CheckIsCRM } from '../../../../../steps/IDB/checks/CheckIsCRM'; - -export const RefreshToken: StepFunction = async (_, { phone }: any) => { - try { - await phone.auth.refreshToken(); - } catch (error) { - console.error('refreshToken error', error); - } -}; +import { + CreateMock, + MockPostSMS, + MockPostOauthToken, +} from '../../../../../steps/Mock'; +import { NavigateToHistory } from '../../../../../steps/Navigate'; @autorun(test.skip) @it @p1 @title('Verify the app enter Voip mode when Refresh token API returns >=500') export class VoipOnlyRefreshToken extends Step { - Login: StepFunction = CommonLogin; + Login?: StepFunction = (props) => ( + + ); CreateMock: StepFunction = CreateMock; callIconDisabled = false; smsIconDisabled = true; - historyTabDataSign = 'History'; appName = 'common'; run() { const { Login, CreateMock } = this; @@ -118,7 +116,7 @@ export class VoipOnlyRefreshToken extends Step { , + , , , (_: any, { phone }: any) => { diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/VoipMode/Checkpoint/RCI-2635.spec.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/VoipMode/Checkpoint/RCI-2635.spec.tsx index 08d06705f6..4836ca8b6b 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/VoipMode/Checkpoint/RCI-2635.spec.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/VoipMode/Checkpoint/RCI-2635.spec.tsx @@ -12,32 +12,29 @@ */ import { autorun, + common, Given, p2, Scenario, Step, - StepFunction, Then, title, When, } from '@ringcentral-integration/test-utils'; -import { CommonLogin } from '../../../../../steps/CommonLogin'; -import { NavigateToHistory } from '../../../../../steps/Navigate'; -import { CheckCallIcon, CheckTextIcon } from '../../../../../steps/CallHistory'; + +import type { Context } from '../../../../../interfaces'; +import type { StepFunction } from '../../../../../lib/step'; import { CheckVoipOnlyBadge } from '../../../../../steps/Badge'; -import { MockPostOauthToken } from '../../../../../steps/Mock/MockPostOauthToken'; -import { CreateMock } from '../../../../../steps/Mock'; +import { RefreshToken } from '../../../../../steps/Common'; +import { CommonLogin } from '../../../../../steps/CommonLogin'; import { CreateInstance } from '../../../../../steps/CreateInstance'; - -const RefreshToken: StepFunction = async (_, { phone }: any) => { - try { - await phone.auth.refreshToken(); - } catch (error) { - console.error('refreshToken error', error); - } -}; +import { MockGetStatus } from '../../../../../steps/Ha'; +import { CreateMock, MockPostOauthToken } from '../../../../../steps/Mock'; +import { NavigateTo } from '../../../../../steps/Router'; +import { CheckCallButtonActive } from '../../../../../steps/dialer'; @autorun(test) +@common @p2 @title('Verify the app recover normal mode when Refresh token API returns 200') export class VerifyAppRecoverNormalMode extends Step { @@ -45,8 +42,6 @@ export class VerifyAppRecoverNormalMode extends Step { ); CreateMock: StepFunction = CreateMock; - historyTabDataSign = 'History'; - appName = 'common'; run() { const { Login, CreateMock } = this; return ( @@ -55,6 +50,7 @@ export class VerifyAppRecoverNormalMode extends Step { desc="App enter Voip mode when Refresh token API returns 503" action={[ CreateMock, + MockGetStatus, , Login, RefreshToken, @@ -69,7 +65,7 @@ export class VerifyAppRecoverNormalMode extends Step { repeat={1} isDefaultInit={false} />, - RefreshToken, + , ]} /> , - (_: any, { phone }: any) => { + (_: unknown, { phone }: Context) => { expect(phone.connectivityManager.isVoipOnlyMode).toBeFalsy(); }, - , - , - , + , + , ]} /> diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/VoipMode/Checkpoint/RCI-2636.spec.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/VoipMode/Checkpoint/RCI-2636.spec.tsx index 8cbd2ff242..6991037a94 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/VoipMode/Checkpoint/RCI-2636.spec.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/VoipMode/Checkpoint/RCI-2636.spec.tsx @@ -11,6 +11,7 @@ */ import { autorun, + common, Given, p2, Scenario, @@ -20,24 +21,17 @@ import { title, When, } from '@ringcentral-integration/test-utils'; -import { CommonLogin } from '../../../../../steps/CommonLogin'; -import { NavigateToHistory } from '../../../../../steps/Navigate'; + import { CheckCallIcon, CheckTextIcon } from '../../../../../steps/CallHistory'; -import { CheckVoipOnlyBadge } from '../../../../../steps/Badge'; -import { MockPostOauthToken } from '../../../../../steps/Mock/MockPostOauthToken'; -import { - CreateMock, - MockDialingPlan, - MockExtensionsList, - mockExtensionsListData, - MockPostSMS, -} from '../../../../../steps/Mock'; +import { CommonLogin } from '../../../../../steps/CommonLogin'; import { CreateInstance } from '../../../../../steps/CreateInstance'; -import { NavigateTo } from '../../../../../steps/Router'; import { SendSMS } from '../../../../../steps/Messages'; -import { generateDialPlanData } from '../../../../../__mock__/generateDialPlanData'; +import { CreateMock, MockPostSMS } from '../../../../../steps/Mock'; +import { NavigateToHistory } from '../../../../../steps/Navigate'; +import { NavigateTo } from '../../../../../steps/Router'; @autorun(test) +@common @p2 @title( 'Verify the app is in the normal mode when any rc API except Refresh token api returns >=502', @@ -47,7 +41,6 @@ export class VerifyAppInNormalModeWhenOtherAPI500 extends Step { ); CreateMock: StepFunction = CreateMock; - historyTabDataSign = 'History'; appName = 'common'; run() { const { Login, CreateMock } = this; @@ -67,7 +60,7 @@ export class VerifyAppInNormalModeWhenOtherAPI500 extends Step { , + , , , ]} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/VoipMode/Checkpoint/RCI-2638.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/VoipMode/Checkpoint/RCI-2638.test.tsx new file mode 100644 index 0000000000..576ec5405d --- /dev/null +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Ha/VoipMode/Checkpoint/RCI-2638.test.tsx @@ -0,0 +1,303 @@ +/** + * RCI-2638: Verify the app enter Voip mode when token API returns >=500 in the limited mode + * https://test_it_domain/test-cases/RCI-2638 + * Preconditions: + * Open the Charles + * Turn on the read-only mode then the app enter the limited mode + * API Request: + * API RequestRequest URL1(Token API)https://api-rcapps-xmnup.lab.nordigy.ru/restapi/oauth/token2(Health check request)https://api-rcapps-xmnup.lab.nordigy.ru/restapi/v1.0/status + * + | API Request |Request URL | + | 1(Token API) |https://api-rcapps-xmnup.lab.nordigy.ru/restapi/oauth/token | + | 2(Health check request) |https://api-rcapps-xmnup.lab.nordigy.ru/restapi/v1.0/status | + + * https://api-rcapps-xmnup.lab.nordigy.ru/restapi/oauth/token + * https://api-rcapps-xmnup.lab.nordigy.ru/restapi/v1.0/status + * Account type(/s): + * RC US/CA/UK/EU/AU + * Extension type(/s): + * Entry point(/s): + * + */ +import { + p2, + it, + autorun, + Given, + StepFunction, + And, + Scenario, + Step, + Then, + title, + When, + WaitForRenderReady, + UseFakeTimers, + AdvanceTimersByTime, + UseRealTimers, + StepProp, + common, +} from '@ringcentral-integration/test-utils'; + +import { Context } from '../../../../../interfaces'; +import { CheckContainsAlertMessage } from '../../../../../steps/Alert'; +import { CheckVoipOnlyBadge } from '../../../../../steps/Badge'; +import { CheckLimitedModeBadge } from '../../../../../steps/Badge/actions/CheckLimitedModeBadge'; +import { CheckGetStatusApi } from '../../../../../steps/Badge/checks/CheckGetStatusApi'; +import { + CallButtonBehavior, + CheckActiveCallExist, + MakeCall, + AnswerCall, +} from '../../../../../steps/Call'; +import { CheckCallIcon, CheckTextIcon } from '../../../../../steps/CallHistory'; +import { CheckLogBaseInfoActive } from '../../../../../steps/CallLog/checks/CheckCallLogBaseInfo'; +import { CommonLogin } from '../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../steps/CreateInstance'; +import { CheckIsCRM } from '../../../../../steps/IDB/checks/CheckIsCRM'; +import { SendSMS } from '../../../../../steps/Messages'; +import { + CreateMock, + MockNumberParserV2, + MockGetStatus, +} from '../../../../../steps/Mock'; +import { MockPostOauthToken } from '../../../../../steps/Mock/MockPostOauthToken'; +import { NavigateToHistory } from '../../../../../steps/Navigate'; +import { NavigateTo } from '../../../../../steps/Router'; + +export const TriggerRefreshTokenError: StepFunction = async ( + _, + { phone }: any, +) => { + try { + // phone.auth.refreshToken will cause issue in fakerTimer, so mock trigger platform refreshError event here + phone.availabilityMonitor._deps.client.service + .platform() + .emit('refreshError', { + message: 'none', + response: { status: 500 }, + }); + } catch (error) { + console.error('refreshToken error', error); + } +}; + +@autorun(test) +@common +@it +@p2 +@title( + 'Verify the app enter Voip mode should retry status api when token API returns >=500 in the limited mode', +) +export class RetryHealthStatusInVoipMode extends Step { + Login: StepProp = (props) => ( + + ); + CreateMock: StepProp = CreateMock; + retryAfter = 10; + run() { + const { Login, CreateMock } = this; + return ( + + , + , + , + { + return { + status: 503, + errorCode: 'CMN-211', + errors: [{ errorCode: 'CMN-211' }], + }; + }} + />, + Login, + UseFakeTimers, + , + SendSMS, + WaitForRenderReady, + CheckLimitedModeBadge, + ]} + /> + + , + ]} + /> + , WaitForRenderReady]} + /> + , CheckVoipOnlyBadge]} + /> + , + WaitForRenderReady, + ]} + /> + , CheckVoipOnlyBadge]} + /> + , + , + WaitForRenderReady, + ]} + /> + , + // 121s is the maximum of AvailabilityMonitor._randomTime + // which is used for switch to normal mode + , + WaitForRenderReady, + UseRealTimers, + // no more status api requests sent + , + , + ]} + /> + + ); + } +} + +@autorun(test) +@common +@it +@p2 +@title( + 'Verify the app enter Voip mode when token API returns >=500 in the limited mode', +) +export class EnterVoipModeFromLimitMode extends Step { + Login: StepProp = (props) => ( + + ); + CreateMock: StepProp = CreateMock; + callIconDisabled = false; + smsIconDisabled = false; + appName = 'common'; + run() { + const { Login, CreateMock } = this; + return ( + + , + , + { + return { + status: 503, + errorCode: 'CMN-211', + errors: [{ errorCode: 'CMN-211' }], + }; + }} + />, + Login, + , + SendSMS, + WaitForRenderReady, + CheckLimitedModeBadge, + ]} + /> + + , + , + , + , + ]} + /> + { + if (CheckIsCRM(this.appName)) { + return [ + , + , + AnswerCall, + , + ]; + } + return [ + , + AnswerCall, + CheckActiveCallExist, + , + ]; + }} + /> + } + /> + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/History/Checkpoint/RCI-1121.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/History/Checkpoint/RCI-1121.test.tsx index e7a053a595..5aec89b6da 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/History/Checkpoint/RCI-1121.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/History/Checkpoint/RCI-1121.test.tsx @@ -22,7 +22,6 @@ * > Login CTI > History */ - import { common, StepFunction, @@ -47,7 +46,6 @@ import { GenerateDateBeforeMinute, GenerateDateBeforeToday, } from '../../../../steps/CallHistory'; -import { NavigateToHistory } from '../../../../steps/Navigate'; import { CommonLogin } from '../../../../steps/CommonLogin'; import { CreateInstance } from '../../../../steps/CreateInstance'; import { @@ -58,6 +56,7 @@ import { MockExtensionsList, mockExtensionsListData, } from '../../../../steps/Mock'; +import { NavigateToHistory } from '../../../../steps/Navigate'; const yesterday = GenerateDateBeforeToday(1); const fiveMinsAgo = GenerateDateBeforeMinute(5); @@ -159,7 +158,6 @@ export class HistoryWithCalls extends Step { ); CreateMock: StepFunction = CreateMock; - historyTabDataSign = 'History'; @examples(` | callHistoryRecords | expectedValues | @@ -213,11 +211,7 @@ export class HistoryWithCalls extends Step { }} /> - } - /> - + } /> ); CreateMock: StepFunction = CreateMock; - historyTabDataSign = 'History'; appName = 'common'; run() { const { Login = CommonLogin, appName, CreateMock } = this; @@ -70,7 +71,7 @@ export class CheckCallHistorySectionWithoutCalls extends Step { } + action={} /> = CommonCreateMock; + Login: StepFunction = (props) => ( + + ); + + run() { + const { CreateMock, Login } = this; + const MockNumberParserV2TokenInvalid: StepFunction = () => ( + + ({ + errorCode: 'TokenInvalid', + message: 'Access token corrupted', + errors: [ + { + errorCode: 'OAU-129', + message: 'Access token corrupted', + }, + ], + } as any) + } + /> + ); + + return ( + [ + CreateMock, + MockGetPhoneNumber, + MockMessageSync, + MockNumberParserV2TokenInvalid, + Login, + ]} + > + [ + , + , + , + , + ]} + /> + [ + , + , + , + ]} + /> + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/LogInLogOut/Checkpoint/RCI-3908.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/LogInLogOut/Checkpoint/RCI-3908.test.tsx index 88d6109799..a685366fb0 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/LogInLogOut/Checkpoint/RCI-3908.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/LogInLogOut/Checkpoint/RCI-3908.test.tsx @@ -9,7 +9,7 @@ | Brand |Brand name | | Avaya | Avaya Cloud Office | | Telus |TELUS Business Connect | - | AT&T |Office@Hand | + | AT&T |AT&T Office@Hand | * Entry point(/s): * @@ -22,7 +22,6 @@ * BT/Telus/AT&T * User tries to log in {Partner brandBrand} for {Project} */ - import type { StepProp } from '@ringcentral-integration/test-utils'; import { p1, @@ -38,7 +37,6 @@ import { waitForRenderReady, } from '@ringcentral-integration/test-utils'; -import { CheckRouterNavigation } from '../../../../steps/Navigate'; import { CommonLogin } from '../../../../steps/CommonLogin'; import { CreateInstance } from '../../../../steps/CreateInstance'; import { @@ -46,6 +44,7 @@ import { ClickConfirmInModal, CheckModalExist, } from '../../../../steps/Modal'; +import { CheckRouterNavigation } from '../../../../steps/Navigate'; // common server mock as rc brand so it doesn't has this scenario @autorun(test.skip) diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/LogInLogOut/Checkpoint/RCI-4160.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/LogInLogOut/Checkpoint/RCI-4160.test.tsx index 6aa854cf53..c8dc1afc15 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/LogInLogOut/Checkpoint/RCI-4160.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/LogInLogOut/Checkpoint/RCI-4160.test.tsx @@ -11,7 +11,6 @@ * > Enter the phone email address or phone number * >Press the 'Sign In' button */ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { p2, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/LogInLogOut/Checkpoint/RCI-4328.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/LogInLogOut/Checkpoint/RCI-4328.test.tsx index 06b226d2ae..9b999e15c7 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/LogInLogOut/Checkpoint/RCI-4328.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/LogInLogOut/Checkpoint/RCI-4328.test.tsx @@ -22,17 +22,19 @@ import { p1, common, } from '@ringcentral-integration/test-utils'; -import { CommonLogin } from '../../../../steps/CommonLogin'; -import { CheckAlertMessage } from '../../../../steps/Alert'; +import { waitUntilTo } from '@ringcentral-integration/utils'; + +import type { Context } from '../../../../interfaces'; import type { StepFunction } from '../../../../lib/step'; -import { MockPostOauthToken } from '../../../../steps/Mock/MockPostOauthToken'; +import { CheckAlertMessage } from '../../../../steps/Alert'; +import { CommonLogin } from '../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../steps/CreateInstance'; import { CreateMock, MockExtensionInfo, MockGetPhoneNumber, + MockPostOauthToken, } from '../../../../steps/Mock'; -import { CreateInstance } from '../../../../steps/CreateInstance'; -import type { Context } from '../../../../interfaces'; export const AARRefresh: StepFunction = async (_, { rcMock, phone }) => { try { @@ -60,8 +62,10 @@ const CheckAARAlert: StepFunction<{ export const CheckLogInResult: StepFunction<{ loginFail?: boolean; }> = async (props, context) => { - const { loggedIn } = context.phone.auth; - expect(loggedIn).toBe(!props.loginFail); + await waitUntilTo(() => { + const { loggedIn } = context.phone.auth; + expect(loggedIn).toBe(!props.loginFail); + }); }; interface CheckAARProps { @@ -69,7 +73,7 @@ interface CheckAARProps { CustomCreateMock: StepFunction; } -@autorun(test.skip) +@autorun(test) @it @p1 @common diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/MakeCallWith/OutboundCall/CallFromPhone(RCPhone)/Checkpoint/RCI-4125.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/MakeCallWith/OutboundCall/CallFromPhone(RCPhone)/Checkpoint/RCI-4125.test.tsx index d358d7bf31..6153576203 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/MakeCallWith/OutboundCall/CallFromPhone(RCPhone)/Checkpoint/RCI-4125.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/MakeCallWith/OutboundCall/CallFromPhone(RCPhone)/Checkpoint/RCI-4125.test.tsx @@ -21,13 +21,12 @@ * | Brand |Brand Name |Link | | RC |'RingCentral Phone' |'rcmobile://call?number={phoneNumber}' | - | AT&T |'Office@Hand Phone' |'https://app.officeathand.att.com/r/call?number={phoneNumber}' | + | AT&T |'AT&T Office@Hand Phone' |'https://app.officeathand.att.com/r/call?number={phoneNumber}' | | BT |'BT Cloud Work Phone' |'rcbtmobile://call?number={phoneNumber}' | | Telus |'Business Connect Phone' |'rctelus://call?number={phoneNumber}' | * Settings > Calling > Make my calls with > {BrandName Phone } */ - import { autorun, examples, @@ -45,10 +44,10 @@ import type { StepFunction } from '../../../../../../lib/step'; import { MakeCall } from '../../../../../../steps/Call'; import { CommonLogin } from '../../../../../../steps/CommonLogin'; import { NavigateToDialer } from '../../../../../../steps/Navigate'; -import { NavigateTo } from '../../../../../../steps/Router/action'; +import { NavigateTo } from '../../../../../../steps/Router'; import { ClickSaveButton, - ExpandDropdown, + ExpandCallingSettingDropdown, SelectCallingSetting, } from '../../../../../../steps/Settings'; @@ -60,7 +59,7 @@ const exampleData = [ }, { brand: 'att', - brandName: 'Office@Hand Phone', + brandName: 'AT&T Office@Hand Phone', link: /attvr20:\/\/call\?number=.+/, }, { @@ -109,7 +108,7 @@ export class RCI4125 extends Step { jest.spyOn(phone.softphone, 'makeCall'); }, , - ExpandDropdown, + ExpandCallingSettingDropdown, , ClickSaveButton, ]} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Meeting/RemoveMeetingNotification/Checkpoint/RCI-4119.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Meeting/RemoveMeetingNotification/Checkpoint/RCI-4119.test.tsx new file mode 100644 index 0000000000..33dff0e37e --- /dev/null +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Meeting/RemoveMeetingNotification/Checkpoint/RCI-4119.test.tsx @@ -0,0 +1,124 @@ +/** + * RCI-4119: Display remove meeting notification in meeting page + * https://test_it_domain/test-cases/RCI-4119 + * Preconditions: + * RC CTI app is installed and enabled + * User has logged in to 3rd party + * User has logged in to {CTI} app with {Brand} + * Entry point(/s): + * Check the meeting page in {CTI} app with {Brand} + * + | Brand |Text |Link | + | RC |RingCentral Google Workspace Add-on |https://www.ringcentral.com/apps/download?app=gsuite&brandId=1210 | + | BT |BT Cloud Work Google Workspace Add-on |https://www.ringcentral.com/apps/download?app=gsuite&brandId=7710 | + | TELUS | TELUS Business Connect for Google Workspace |https://www.ringcentral.com/apps/download?app=gsuite&brandId=7310 | + | AT&T |AT&T Office@Hand for Google Workspace |https://www.ringcentral.com/apps/download?app=gsuite&brandId=3420 | + | Avaya |Avaya Cloud Office Google Workspace Add-on |https://www.ringcentral.com/apps/download?app=gsuite&brandId=6010 | + | Unify Office(Atos) |Unify Office Google Workspace Add-on |https://www.ringcentral.com/apps/download?app=gsuite&brandId=2020 | + | Rainbow Office |Rainbow Office Google Workspace Add-on |https://www.ringcentral.com/apps/download?app=gsuite&brandId=2110 | + | Verizon |RingCentral with Verizon Google Workspace Add-on |https://www.ringcentral.com/apps/download?app=gsuite&brandId=2210 | + | Vodafone |Vodafone Business Google Workspace Add-on |https://www.ringcentral.com/apps/download?app=gsuite&brandId=7010 | + | Ecotel |RingCentral Google Workspace Add-on |https://www.ringcentral.com/apps/download?app=gsuite&brandId=4210 | + | MCM |RingCentral Google Workspace Add-on |https://www.ringcentral.com/apps/download?app=gsuite&brandId=4810 | + | Eastlink |RingCentral Google Workspace Add-on |https://www.ringcentral.com/apps/download?app=gsuite&brandId=4610 | + | Versatel |RingCentral Google Workspace Add-on |https://www.ringcentral.com/apps/download?app=gsuite&brandId=4710 | + | Frontier |RingCentral Google Workspace Add-on |https://www.ringcentral.com/apps/download?app=gsuite&brandId=4910 | + | DT-RCO-Telekom |RingCentral Google Workspace Add-on |https://www.ringcentral.com/apps/download?app=gsuite&brandId=2030 | + | DT-RCO-ATOS-Unify Office Telekom |RingCentral Google Workspace Add-on |https://www.ringcentral.com/apps/download?app=gsuite&brandId=2040 | + | Sunrise |RingCentral Google Workspace Add-on |https://www.ringcentral.com/apps/download?app=gsuite&brandId=2050 | + | Fedramp |RingCentral Google Workspace Add-on |https://www.ringcentral.com/apps/download?app=gsuite&brandId=1250 | + | Charter SMB | RingCentral Google Workspace Add-on |https://www.ringcentral.com/apps/download?app=gsuite&brandId=5110 | + | Charter Enterprise |RingCentral Google Workspace Add-on |https://www.ringcentral.com/apps/download?app=gsuite&brandId=5210 | + + * https://www.ringcentral.com/apps/download?app=gsuite&brandId=1210 + * https://www.ringcentral.com/apps/download?app=gsuite&brandId=7710 + * TELUS Business Connect for Google Workspace + * https://www.ringcentral.com/apps/download?app=gsuite&brandId=7310 + * https://www.ringcentral.com/apps/download?app=gsuite&brandId=3420 + * https://www.ringcentral.com/apps/download?app=gsuite&brandId=6010 + * Unify Office(Atos) + * Unify Office Google Workspace Add-on + * https://www.ringcentral.com/apps/download?app=gsuite&brandId=2020 + * https://www.ringcentral.com/apps/download?app=gsuite&brandId=2110 + * RingCentral with Verizon Google Workspace Add-on + * https://www.ringcentral.com/apps/download?app=gsuite&brandId=2210 + * https://www.ringcentral.com/apps/download?app=gsuite&brandId=7010 + * https://www.ringcentral.com/apps/download?app=gsuite&brandId=4210 + * RingCentral Google Workspace Add-on + * https://www.ringcentral.com/apps/download?app=gsuite&brandId=4810 + * https://www.ringcentral.com/apps/download?app=gsuite&brandId=4610 + * https://www.ringcentral.com/apps/download?app=gsuite&brandId=4710 + * https://www.ringcentral.com/apps/download?app=gsuite&brandId=4910 + * https://www.ringcentral.com/apps/download?app=gsuite&brandId=2030 + * DT-RCO-ATOS-Unify Office Telekom + * https://www.ringcentral.com/apps/download?app=gsuite&brandId=2040 + * https://www.ringcentral.com/apps/download?app=gsuite&brandId=2050 + * https://www.ringcentral.com/apps/download?app=gsuite&brandId=1250 + * RingCentral Google Workspace Add-on + * https://www.ringcentral.com/apps/download?app=gsuite&brandId=5110 + * https://www.ringcentral.com/apps/download?app=gsuite&brandId=5210 + */ +import { + p2, + it, + autorun, + examples, + Scenario, + Step, + Then, + title, + When, + StepProp, +} from '@ringcentral-integration/test-utils'; + +import { CommonLogin } from '../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../steps/CreateInstance'; +import { CreateMock, MockAccountInfo } from '../../../../../steps/Mock'; +import { NavigateToMeeting } from '../../../../../steps/Navigate'; + +// not enable this feature in common +// TODO now the copy is different with RCINT-24629 need to update after confirmation +@autorun(test.skip) +@it +@p2 +@title('Display remove meeting notification in meeting page') +export class RemoveMeetingNotification extends Step { + Login: StepProp = (props) => ( + + ); + CreateMock: StepProp = CreateMock; + CheckRemoveMeetingNotification: StepProp = () => ({}); + @examples(` + | brandId | brand | appName | + | '1210' | 'rc' | 'RingCentral Google Workspace Add-on' | + `) + run() { + const { CreateMock, Login, CheckRemoveMeetingNotification } = this; + return ( + + { + mockData.serviceInfo.brand.id = this.example.brandId; + return mockData; + }} + />, + Login, + NavigateToMeeting, + ]} + /> + + + + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Meeting/ScheduleMeeting/RCI-1370.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Meeting/ScheduleMeeting/RCI-1370.test.tsx index ef1b40e4f4..a84bdde30b 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Meeting/ScheduleMeeting/RCI-1370.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Meeting/ScheduleMeeting/RCI-1370.test.tsx @@ -10,7 +10,6 @@ * 1.Login CTI with RCM provider account > More > Schedule meeting * 2.Login CTI with RCV provider account > More > Schedule meeting */ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { p2, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Meeting/ScheduleMeeting/Rcv/ScheduleMeeting/E2Ee/Checkpoint/RCI-3287.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Meeting/ScheduleMeeting/Rcv/ScheduleMeeting/E2Ee/Checkpoint/RCI-3287.test.tsx index 69ce68e379..e1820842dd 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Meeting/ScheduleMeeting/Rcv/ScheduleMeeting/E2Ee/Checkpoint/RCI-3287.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Meeting/ScheduleMeeting/Rcv/ScheduleMeeting/E2Ee/Checkpoint/RCI-3287.test.tsx @@ -14,7 +14,6 @@ * Scheduler:ClickSchedulebutton > And the API failed to return the value ofinvitation Info * Outlook Appointment:ClickSchedulebutton > And the APIfailedto return the value ofinvitation Info */ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { autorun, @@ -30,12 +29,12 @@ import { } from '@ringcentral-integration/test-utils'; import type { StepProp } from '../../../../../../../../lib/step'; +import { CheckAlertToBeCallWith } from '../../../../../../../../steps/Alert'; import { CommonLogin } from '../../../../../../../../steps/CommonLogin'; import { CheckRCVPageDisplay, ClickScheduleButton, } from '../../../../../../../../steps/Meeting'; -import { CheckAlertToBeCallWith } from '../../../../../../../../steps/Alert'; @autorun(test.skip) @it diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Meeting/ScheduleMeeting/Rcv/ScheduleMeeting/Pmi/Checkpoint/RCI-4821.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Meeting/ScheduleMeeting/Rcv/ScheduleMeeting/Pmi/Checkpoint/RCI-4821.test.tsx index d1c646fdbe..0906571a64 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Meeting/ScheduleMeeting/Rcv/ScheduleMeeting/Pmi/Checkpoint/RCI-4821.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Meeting/ScheduleMeeting/Rcv/ScheduleMeeting/Pmi/Checkpoint/RCI-4821.test.tsx @@ -9,7 +9,6 @@ * Entry point(/s): * 'Schedule video meeting' page > Select 'Use Personal Meeting ID XXX-XXX-XXX' */ - import { p2, it, @@ -22,14 +21,15 @@ import { When, StepProp, } from '@ringcentral-integration/test-utils'; + import { CommonLogin } from '../../../../../../../../steps/CommonLogin'; import { CreateInstance } from '../../../../../../../../steps/CreateInstance'; import { + CheckPatchMeetingParams, ClickScheduleButton, OperateTopic, TurnOnToggle, } from '../../../../../../../../steps/Meeting'; -import { CheckRcvPatchBridge } from '../../../../../../../../steps/Mock/CheckFetchMockResponse.ts'; import { NavigateToMeeting } from '../../../../../../../../steps/Navigate'; @autorun(test.skip) @@ -64,10 +64,8 @@ export class RCI4821 extends Step { ( - + action={() => ( + )} /> diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Meeting/ScheduleMeeting/Rcv/ScheduleMeeting/RcvConnector/Checkpoint/RCI-2997.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Meeting/ScheduleMeeting/Rcv/ScheduleMeeting/RcvConnector/Checkpoint/RCI-2997.test.tsx index cbb8c3e703..8f3809bdea 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Meeting/ScheduleMeeting/Rcv/ScheduleMeeting/RcvConnector/Checkpoint/RCI-2997.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Meeting/ScheduleMeeting/Rcv/ScheduleMeeting/RcvConnector/Checkpoint/RCI-2997.test.tsx @@ -16,42 +16,51 @@ * Note(/s): * 793123 is the dial-in password */ - import type { StepProp } from '@ringcentral-integration/test-utils'; import { - p3, - it, - autorun, - examples, - Given, And, + Given, Scenario, Step, Then, - title, When, + autorun, + common, + examples, + it, + p3, + title, } from '@ringcentral-integration/test-utils'; +import type { Context } from '../../../../../../../../interfaces'; +import { CheckAlertMessage } from '../../../../../../../../steps/Alert'; import { CommonLogin } from '../../../../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../../../../steps/CreateInstance'; +import { + CheckRCVPageDisplay, + ClickScheduleButton, + EnterPassword, + MockPostBridges, + TurnOffToggle, + TurnOnToggle, +} from '../../../../../../../../steps/Meeting'; import { CreateMock, + MockCallLogSync, MockErrorRcvInvitation, - MockPermission, MockGetTelephonyState, MockMessageSync, - MockCallLogSync, + MockPermission, } from '../../../../../../../../steps/Mock'; -import { CreateInstance } from '../../../../../../../../steps/CreateInstance'; -import { CheckAlertMessage } from '../../../../../../../../steps/Alert'; -import { - CheckRCVPageDisplay, - TurnOnToggle, - TurnOffToggle, - EnterPassword, - ClickScheduleButton, -} from '../../../../../../../../steps/Meeting'; +import { NavigateTo } from '../../../../../../../../steps/Router'; -@autorun(test.skip) +interface ExampleItem { + isMeetingSecret: boolean; + meetingPassword: string; +} + +@autorun(test) +@common @it @p3 @title( @@ -79,6 +88,7 @@ export class RCVConnector extends Step { , ]} /> - + [Login, ]} + /> { + action={({ isMeetingSecret, meetingPassword }: ExampleItem) => { if (isMeetingSecret) { return [ , @@ -123,12 +136,18 @@ export class RCVConnector extends Step { - } + action={(_: ExampleItem, context: Context) => { + const rcVideo = context.phone.rcVideo; + expect(rcVideo).toBeDefined(); + return rcVideo._enableInvitationApiFailedToast + ? [ + , + ] + : []; + }} /> ); diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/CheckPoint/RCI-3542.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/CheckPoint/RCI-3542.test.tsx index 83e377cc51..7454d818c4 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/CheckPoint/RCI-3542.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/CheckPoint/RCI-3542.test.tsx @@ -1,3 +1,4 @@ +import { mockMessageListData } from '../../../../__mock__'; import type { StepFunction } from '../../../../lib/step'; import { p2, @@ -12,12 +13,6 @@ import { When, And, } from '../../../../lib/step'; -import { - MockMessageList, - MockMessageSync, - MockMessagePut, -} from '../../../../steps/Mock'; -import { NavigateToMessagesTab } from '../../../../steps/Navigate'; import { ExpandTheActionMenu, ClickActionButton, @@ -26,7 +21,12 @@ import { CheckNavUnreadCount, CheckFlagButtonTitle, } from '../../../../steps/Messages'; -import { mockMessageListData } from '../../../../__mock__'; +import { + MockMessageList, + MockMessageSync, + MockMessagePut, +} from '../../../../steps/Mock'; +import { NavigateToMessagesTab } from '../../../../steps/Navigate'; @autorun(test.skip) @p2 diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/CheckPoint/RCI-3564.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/CheckPoint/RCI-3564.test.tsx index ee08b6ccf3..c45f8e9fe6 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/CheckPoint/RCI-3564.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/CheckPoint/RCI-3564.test.tsx @@ -3,7 +3,9 @@ * https://test_it_domain/test-cases/RCI-3564 */ import { fireEvent, screen } from '@testing-library/react'; -import { Login as CommonLogin } from '../../../../steps/Login'; + +import { mockMessageListData } from '../../../../__mock__'; +import type { Context } from '../../../../interfaces'; import type { StepFunction } from '../../../../lib/step'; import { p2, @@ -17,6 +19,13 @@ import { When, And, } from '../../../../lib/step'; +import { CommonLogin } from '../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../steps/CreateInstance'; +import { + CheckUnreadCounts, + ClickMessageItemAndBack, + ExpandTheActionMenu, +} from '../../../../steps/Messages'; import { CreateMock as CommonCreateMock, generateMessage, @@ -28,20 +37,15 @@ import { NavigateToMessagesTab, NavigateToTypeTabUnderMessage, } from '../../../../steps/Navigate'; -import { mockMessageListData } from '../../../../__mock__'; -import { - CheckUnreadCounts, - ClickMessageItemAndBack, - ExpandTheActionMenu, -} from '../../../../steps/Messages'; -import type { Context } from '../../../../interfaces'; -@autorun(test.skip) +@autorun(test) @p2 @title('SMS unread count ${countAfterAction}') export class SMSUnreadCount extends Step { - CustomLogin?: StepFunction; - CustomCreateMock?: StepFunction; + CustomLogin: StepFunction = (props) => ( + + ); + CustomCreateMock: StepFunction = CommonCreateMock; @examples(` | unreadCount | countAfterAction | @@ -51,8 +55,7 @@ export class SMSUnreadCount extends Step { run() { const { unreadCount, countAfterAction } = this.context.example; - const { CustomLogin = CommonLogin, CustomCreateMock = CommonCreateMock } = - this; + const { CustomLogin, CustomCreateMock } = this; const mockMsgData = mockMessageListData([ { @@ -115,12 +118,14 @@ export class SMSUnreadCount extends Step { } } -@autorun(test.skip) +@autorun(test) @p2 @title('${type} unread count ${countAfterAction}') export class VoicemailOrFaxUnreadCount extends Step { - CustomLogin?: StepFunction; - CustomCreateMock?: StepFunction; + CustomLogin: StepFunction = (props) => ( + + ); + CustomCreateMock: StepFunction = CommonCreateMock; @examples(` | type | unreadCount | action | countAfterAction | messageTypeTabDataSign | mockReadStatus | updateReadStatus | @@ -142,8 +147,7 @@ export class VoicemailOrFaxUnreadCount extends Step { updateReadStatus, } = this.context.example; - const { CustomLogin = CommonLogin, CustomCreateMock = CommonCreateMock } = - this; + const { CustomLogin, CustomCreateMock } = this; const mockMsgData = mockMessageListData([ { @@ -212,12 +216,14 @@ export class VoicemailOrFaxUnreadCount extends Step { ); } } -@autorun(test.skip) +@autorun(test) @p2 @title('{type} unread count after delete') export class deleteVoicemailOrFaxAfterUnreadCount extends Step { - CustomLogin?: StepFunction; - CustomCreateMock?: StepFunction; + CustomLogin: StepFunction = (props) => ( + + ); + CustomCreateMock: StepFunction = CommonCreateMock; @examples(` | type | unreadCount | action | countAfterAction | messageTypeTabDataSign | @@ -233,8 +239,7 @@ export class deleteVoicemailOrFaxAfterUnreadCount extends Step { action, } = this.context.example; - const { CustomLogin = CommonLogin, CustomCreateMock = CommonCreateMock } = - this; + const { CustomLogin, CustomCreateMock } = this; const mockMsgData = mockMessageListData([ { @@ -288,7 +293,11 @@ export class deleteVoicemailOrFaxAfterUnreadCount extends Step { desc="User delete a {type} message " action={() => { fireEvent.click(screen.getAllByTestId(action)[0]); - fireEvent.click(screen.getAllByTestId('confirm')[1]); + fireEvent.click( + screen + .getByTestId('deleteModal') + .querySelector('[data-sign="confirm"]')!, + ); }} /> ; - CustomCreateMock?: StepFunction; + CustomLogin: StepProp = (props) => ( + + ); + CustomCreateMock: StepProp = CreateMock; @examples(` | unreadConversationCount | unreadTextMessageCount | countAfterAction | @@ -77,7 +83,8 @@ export class ConversationUnread extends Step { })} />, ({ + handler={(mockData) => ({ + ...mockData, ...mockMsgData, })} />, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/CheckPoint/RCI-4154.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/CheckPoint/RCI-4154.test.tsx index 5e5125acaa..8f8bf1d9a3 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/CheckPoint/RCI-4154.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/CheckPoint/RCI-4154.test.tsx @@ -2,7 +2,7 @@ * RCI-4154: Check action buttons when offline * https://test_it_domain/test-cases/RCI-4154 */ -import { Login as CommonLogin } from '../../../../steps/Login'; +import { mockMessageListData } from '../../../../__mock__'; import type { StepFunction } from '../../../../lib/step'; import { p2, @@ -16,8 +16,15 @@ import { When, And, } from '../../../../lib/step'; +import { CommonLogin } from '../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../steps/CreateInstance'; +import { + CheckAllActionButtonsStatusWhenOffline, + ExpandTheActionMenu, +} from '../../../../steps/Messages'; import { CreateMock as CommonCreateMock, + MockAddressBookSync, MockMessageList, MockMessageSync, } from '../../../../steps/Mock'; @@ -25,18 +32,15 @@ import { NavigateToMessagesTab, NavigateToTypeTabUnderMessage, } from '../../../../steps/Navigate'; -import { mockMessageListData } from '../../../../__mock__'; -import { - CheckAllActionButtonsStatusWhenOffline, - ExpandTheActionMenu, -} from '../../../../steps/Messages'; -@autorun(test.skip) +@autorun(test) @p2 @title('Check action buttons in ${type} when offline') export class MessageActionButtonWhenOffline extends Step { - CustomLogin?: StepFunction; - CustomCreateMock?: StepFunction; + CustomLogin: StepFunction = (props) => ( + + ); + CustomCreateMock: StepFunction = CommonCreateMock; @examples(` | actionButtons | type | number | @@ -47,8 +51,7 @@ export class MessageActionButtonWhenOffline extends Step { run() { const { type, actionButtons, number } = this.context.example; - const { CustomLogin = CommonLogin, CustomCreateMock = CommonCreateMock } = - this; + const { CustomLogin, CustomCreateMock } = this; return ( , + { + personalUsers[0].homePhone = number; + return personalUsers; + }} + />, ({ ...mockData, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/CheckPoint/RCI-4313.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/CheckPoint/RCI-4313.test.tsx index 607ce86a22..f6b603a861 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/CheckPoint/RCI-4313.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/CheckPoint/RCI-4313.test.tsx @@ -22,10 +22,11 @@ import { title, When, } from '@ringcentral-integration/test-utils'; -import { CreateMock as CommonCreateMock } from '../../../../steps/Mock'; -import { NavigateToComposeText } from '../../../../steps/Navigate'; + import { CommonLogin } from '../../../../steps/CommonLogin'; import { CheckButton, CheckComposeTextUI } from '../../../../steps/Messages'; +import { CreateMock as CommonCreateMock } from '../../../../steps/Mock'; +import { NavigateToComposeText } from '../../../../steps/Navigate'; @autorun(test.skip) @p2 diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/Checkpoint/RCI-3238.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/Checkpoint/RCI-3238.test.tsx index 32a13c0b17..bdccc40f3e 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/Checkpoint/RCI-3238.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/Checkpoint/RCI-3238.test.tsx @@ -10,20 +10,21 @@ import { title, When, } from '@ringcentral-integration/test-utils'; -import { MockMessageSync, MockMessageList } from '../../../../../steps/Mock'; -import { TypeCharacter } from '../../../../../steps/Messages/actions/TypeCharacter'; -import { - CheckCurrentName, - CheckSearchResult, -} from '../../../../../steps/Messages/checks/CheckCurrentName'; -import { NavigateToFax } from '../../../../../steps/Navigate/actions/NavigateToFax'; + import { mockMessageListData, modifyMessageStatus, } from '../../../../../__mock__/mockMessageListData'; +import type { Context } from '../../../../../interfaces'; import { ForceContactMatch } from '../../../../../steps/ContactMatch'; import { CheckMessageNumbers } from '../../../../../steps/Conversation/CheckMessageNumbers'; -import type { Context } from '../../../../../interfaces'; +import { TypeCharacter } from '../../../../../steps/Messages/actions/TypeCharacter'; +import { + CheckCurrentName, + CheckSearchResult, +} from '../../../../../steps/Messages/checks/CheckCurrentName'; +import { MockMessageSync, MockMessageList } from '../../../../../steps/Mock'; +import { NavigateToFax } from '../../../../../steps/Navigate/actions/NavigateToFax'; const faxList = [ { @@ -65,7 +66,6 @@ export class FaxContactMatchAndSearch3238 extends Step { CustomEntry: StepProp | null = null; CustomLogin: StepProp | null = null; CustomContactMatch: StepProp | null = null; - messageTabDataSign = 'Messages'; defaultQueryType: 'title' | 'text' = 'title'; @examples(` | phoneNumber | contactMatch | currentName | searchText | searchResult | @@ -152,10 +152,7 @@ export class FaxContactMatchAndSearch3238 extends Step { }), ); }, - , + , ]} /> , + , ]} /> diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/Checkpoint/RCI-3751.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/Checkpoint/RCI-3751.test.tsx index 92d78026d0..28ed11573b 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/Checkpoint/RCI-3751.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/Checkpoint/RCI-3751.test.tsx @@ -12,7 +12,6 @@ * Entry point(/s): * Message->All/Fax */ - import { p3, it, @@ -25,12 +24,13 @@ import { When, Given, } from '@ringcentral-integration/test-utils'; + +import { mockMessageListData } from '../../../../../__mock__'; import type { StepFunction } from '../../../../../lib/step'; -import { MockMessageList, MockMessageSync } from '../../../../../steps/Mock'; import { CheckFaxPageCount } from '../../../../../steps/Conversation/CheckConversations'; +import { MockMessageList, MockMessageSync } from '../../../../../steps/Mock'; import { NavigateToMessageHistory } from '../../../../../steps/Navigate/actions/NavigateToMessageHistory'; import { NavigateToMessagesTab } from '../../../../../steps/Navigate/actions/NavigateToMessages'; -import { mockMessageListData } from '../../../../../__mock__'; interface CheckFaxPageNumberProps { Login: StepFunction; diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/Checkpoint/RCI-4182.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/Checkpoint/RCI-4182.test.tsx index ebad8d3391..cd3d39e47d 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/Checkpoint/RCI-4182.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/Checkpoint/RCI-4182.test.tsx @@ -8,7 +8,6 @@ * Entry point(/s): * */ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { p2, @@ -22,6 +21,7 @@ import { When, } from '@ringcentral-integration/test-utils'; import { screen } from '@testing-library/react'; + import { MockPermission } from '../../../../../steps/Mock'; import { NavigateToMessagesTab } from '../../../../../steps/Navigate/actions/NavigateToMessages'; diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/Checkpoint/RCI-4183.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/Checkpoint/RCI-4183.test.tsx index b260c9f679..354ff9d668 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/Checkpoint/RCI-4183.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/Checkpoint/RCI-4183.test.tsx @@ -8,7 +8,6 @@ * Entry point(/s): * */ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { p2, @@ -22,10 +21,11 @@ import { When, common, } from '@ringcentral-integration/test-utils'; -import { CheckNoMessagesDisplay } from '../../../../../steps/Messages'; + import { Login } from '../../../../../steps/Login'; -import { NavigateToFax } from '../../../../../steps/Navigate'; +import { CheckNoMessagesDisplay } from '../../../../../steps/Messages'; import { MockMessageSync, MockMessageList } from '../../../../../steps/Mock'; +import { NavigateToFax } from '../../../../../steps/Navigate'; @autorun(test) @common diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/Checkpoint/RCI-4519.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/Checkpoint/RCI-4519.test.tsx index 081d5992d8..1543f536f7 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/Checkpoint/RCI-4519.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/Checkpoint/RCI-4519.test.tsx @@ -15,7 +15,6 @@ * Entry point(/s): * */ - import type { StepProp } from '@ringcentral-integration/test-utils'; import { p2, @@ -28,11 +27,11 @@ import { When, } from '@ringcentral-integration/test-utils'; -import { CreateMock } from '../../../../../steps/Mock'; -import { CheckGetMessageSyncParams } from '../../../../../steps/Messages'; -import { NavigateToFax } from '../../../../../steps/Navigate/actions/NavigateToFax'; import { CommonLogin } from '../../../../../steps/CommonLogin'; import { CreateInstance } from '../../../../../steps/CreateInstance'; +import { CheckGetMessageSyncParams } from '../../../../../steps/Messages'; +import { CreateMock } from '../../../../../steps/Mock'; +import { NavigateToFax } from '../../../../../steps/Navigate/actions/NavigateToFax'; const google_message_day_span = 90; const google_message_record_count = 400; diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/FaxAction/Checkpoint/RCI-4318.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/FaxAction/Checkpoint/RCI-4318.test.tsx index c90660637a..01b677552c 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/FaxAction/Checkpoint/RCI-4318.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/FaxAction/Checkpoint/RCI-4318.test.tsx @@ -11,7 +11,6 @@ * Entry point(/s): * > Messages tab > All/Fax >Click expand fax action menu ofthe fax */ - import { p2, it, @@ -24,6 +23,8 @@ import { title, When, } from '@ringcentral-integration/test-utils'; + +import { mockMessageListData } from '../../../../../../__mock__'; import type { StepProp } from '../../../../../../lib/step'; import { CommonLogin } from '../../../../../../steps/CommonLogin'; import { @@ -37,7 +38,6 @@ import { MockMessageSync, } from '../../../../../../steps/Mock'; import { NavigateToFax } from '../../../../../../steps/Navigate'; -import { mockMessageListData } from '../../../../../../__mock__'; @autorun(test.skip) @it diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/FaxAction/Checkpoint/RCI-4319.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/FaxAction/Checkpoint/RCI-4319.test.tsx index a0304ece36..14d1fdf9d9 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/FaxAction/Checkpoint/RCI-4319.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/FaxAction/Checkpoint/RCI-4319.test.tsx @@ -16,7 +16,6 @@ * Entry point(/s): * > Messages tab > All/Fax >Click expand fax action menu of{Fax} */ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { p2, @@ -31,6 +30,14 @@ import { When, And, } from '@ringcentral-integration/test-utils'; + +import { mockMessageListData } from '../../../../../../__mock__'; +import { CheckInContactDetailsPage } from '../../../../../../steps/ContactsView'; +import { + CheckFaxActionButton, + ClickActionButton, + ExpandTheActionMenu, +} from '../../../../../../steps/Messages'; import { MockExtensionsList, mockExtensionsListData, @@ -40,14 +47,7 @@ import { MockPresence, MockAddressBookSync, } from '../../../../../../steps/Mock'; -import { CheckInContactDetailsPage } from '../../../../../../steps/ContactsView'; -import { - CheckFaxActionButton, - ClickActionButton, - ExpandTheActionMenu, -} from '../../../../../../steps/Messages'; import { NavigateToFax } from '../../../../../../steps/Navigate'; -import { mockMessageListData } from '../../../../../../__mock__'; import { WaitForSpinner } from '../../../../../../steps/WaitForSpinner'; @autorun(test.skip) diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/FaxAction/Checkpoint/RCI-4320.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/FaxAction/Checkpoint/RCI-4320.test.tsx index ea4b765fb7..2a93d90715 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/FaxAction/Checkpoint/RCI-4320.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/FaxAction/Checkpoint/RCI-4320.test.tsx @@ -10,7 +10,6 @@ * Entry point(/s): * > Messages tab > All/Fax >Click expand fax action menu ofthe fax */ - import { p2, it, @@ -23,6 +22,8 @@ import { title, When, } from '@ringcentral-integration/test-utils'; + +import { mockMessageListData } from '../../../../../../__mock__'; import type { StepFunction } from '../../../../../../lib/step'; import { CommonLogin } from '../../../../../../steps/CommonLogin'; import { @@ -41,7 +42,6 @@ import { MockMessageSync, } from '../../../../../../steps/Mock'; import { NavigateToFax } from '../../../../../../steps/Navigate'; -import { mockMessageListData } from '../../../../../../__mock__'; @autorun(test.skip) @it diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/FaxAction/Checkpoint/RCI-973.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/FaxAction/Checkpoint/RCI-973.test.tsx index 8e96ff3632..dbf4c9e7cf 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/FaxAction/Checkpoint/RCI-973.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Fax/FaxAction/Checkpoint/RCI-973.test.tsx @@ -21,7 +21,6 @@ | Fax2 |[View, Download, Delete] | */ - import { p2, it, @@ -34,18 +33,21 @@ import { And, title, When, + common, } from '@ringcentral-integration/test-utils'; -import { MockMessageList, MockMessageSync } from '../../../../../../steps/Mock'; + +import { mockMessageListData } from '../../../../../../__mock__'; import { ExpandTheActionMenu } from '../../../../../../steps/Messages/actions'; import { CheckFaxActionButton, CheckFaxActionButtons, } from '../../../../../../steps/Messages/checks'; +import { MockMessageList, MockMessageSync } from '../../../../../../steps/Mock'; import { NavigateToFax } from '../../../../../../steps/Navigate'; -import { mockMessageListData } from '../../../../../../__mock__'; @autorun(test.skip) @it +@common @p2 @title('Action buttons for inbound and outbound fax') export class RCI973FaxActionButtons extends Step { diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/MessageList/Ui/RCI-986.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/MessageList/Ui/RCI-986.test.tsx index 225ce93d3b..a6d1257992 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/MessageList/Ui/RCI-986.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/MessageList/Ui/RCI-986.test.tsx @@ -6,7 +6,6 @@ * Entry point(/s): * */ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { p2, @@ -19,17 +18,18 @@ import { title, When, } from '@ringcentral-integration/test-utils'; + +import { + generateMessageRecords, + generatePhoneNumberList, + mockMessageListData, +} from '../../../../../__mock__/mockMessageListData'; import { CheckMessageItemHighlight } from '../../../../../steps/Messages/checks/CheckMessageItemHighlight'; import { CheckTab } from '../../../../../steps/Messages/checks/CheckTab'; import { CheckUnreadCounts } from '../../../../../steps/Messages/checks/CheckUnreadCounts'; import { CreateMock } from '../../../../../steps/Mock/CreateMock'; import { MockMessageSync } from '../../../../../steps/Mock/MockMessage/MockMessageSync'; import { NavigateToMessagesTab } from '../../../../../steps/Navigate/actions/NavigateToMessages'; -import { - generateMessageRecords, - generatePhoneNumberList, - mockMessageListData, -} from '../../../../../__mock__/mockMessageListData'; @autorun(test.skip) @it diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/N11/988/Checkpoint/RCI-5545.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/N11/988/Checkpoint/RCI-5545.test.tsx index 3b3fb30788..24a5430f60 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/N11/988/Checkpoint/RCI-5545.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/N11/988/Checkpoint/RCI-5545.test.tsx @@ -12,7 +12,6 @@ * > Login CTI with{Account} * > send a message to{Phone number} */ - import { p2, it, @@ -27,7 +26,10 @@ import { } from '@ringcentral-integration/test-utils'; import type { StepProp } from '../../../../../../lib/step'; - +import { CheckContainsAlertMessage } from '../../../../../../steps/Alert'; +import { CommonLogin } from '../../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../../steps/CreateInstance'; +import { SendSMS } from '../../../../../../steps/Messages'; import { CreateMock, MockNumberParserV2, @@ -36,16 +38,13 @@ import { MockPostSMS, MockGetPhoneNumber, } from '../../../../../../steps/Mock'; -import { CommonLogin } from '../../../../../../steps/CommonLogin'; -import { CreateInstance } from '../../../../../../steps/CreateInstance'; import { NavigateTo } from '../../../../../../steps/Router'; -import { SendSMS } from '../../../../../../steps/Messages'; -import { CheckContainsAlertMessage } from '../../../../../../steps/Alert'; -import extMock from './mocks/extResponseMock.json'; -import specialMock from './mocks/specialServiceResponseMock.json'; import specialErrorMock from './mocks/errorResponseMock.json'; import extErrorMock from './mocks/extErrorMock.json'; +import extMock from './mocks/extResponseMock.json'; +import specialMock from './mocks/specialServiceResponseMock.json'; + @autorun(test) @it @p2 diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/N11/988/Checkpoint/RCI-5546.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/N11/988/Checkpoint/RCI-5546.test.tsx index e0fcb36bfe..27946117e6 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/N11/988/Checkpoint/RCI-5546.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/N11/988/Checkpoint/RCI-5546.test.tsx @@ -11,7 +11,6 @@ * > Login CTI with{Account} * > Send a message to{Phone number} */ - import { p2, it, @@ -28,7 +27,9 @@ import { waitUntilTo } from '@ringcentral-integration/utils'; import { getNodeText, screen } from '@testing-library/react'; import type { StepProp } from '../../../../../../lib/step'; - +import { CommonLogin } from '../../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../../steps/CreateInstance'; +import { SendSMS } from '../../../../../../steps/Messages'; import { CreateMock, MockNumberParserV2, @@ -37,14 +38,11 @@ import { MockPostSMS, MockGetPhoneNumber, } from '../../../../../../steps/Mock'; -import { CommonLogin } from '../../../../../../steps/CommonLogin'; -import { CreateInstance } from '../../../../../../steps/CreateInstance'; import { NavigateTo } from '../../../../../../steps/Router'; -import { SendSMS } from '../../../../../../steps/Messages'; +import errorMock from './mocks/errorResponseMock.json'; import extMock from './mocks/extResponseMock.json'; import specialMock from './mocks/specialServiceResponseMock.json'; -import errorMock from './mocks/errorResponseMock.json'; @autorun(test) @it diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/SMS/Checkpoint/RCI-162.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/SMS/Checkpoint/RCI-162.test.tsx index df9a61b34f..fe8aab569d 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/SMS/Checkpoint/RCI-162.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/SMS/Checkpoint/RCI-162.test.tsx @@ -1,3 +1,4 @@ +import { mockMessageListData } from '../../../../../__mock__'; import type { StepFunction } from '../../../../../lib/step'; import { p2, @@ -20,7 +21,6 @@ import { } from '../../../../../steps/Messages'; import { MockMessageList, MockMessageSync } from '../../../../../steps/Mock'; import { NavigateToMessagesTab } from '../../../../../steps/Navigate'; -import { mockMessageListData } from '../../../../../__mock__'; @autorun(test.skip) @p2 diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/SMS/Checkpoint/RCI-445.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/SMS/Checkpoint/RCI-445.test.tsx index 20ee670d85..28289e8b07 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/SMS/Checkpoint/RCI-445.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/SMS/Checkpoint/RCI-445.test.tsx @@ -6,172 +6,153 @@ * Entry point(/s): * ntry point(/s):

> Go to the 'Setting'

> Click 'Region' option

*/ - -import type dialingPlanBody from '@ringcentral-integration/commons/integration-test/mock/data/dialingPlan.json'; import { waitForRenderReady } from '@ringcentral-integration/test-utils'; -import { Login as CommonLogin } from '../../../../../steps/Login'; + +import type { Context } from '../../../../../interfaces'; import type { StepFunction } from '../../../../../lib/step'; import { - p2, - it, - autorun, - examples, + And, Given, Scenario, Step, Then, - title, When, + autorun, + common, + examples, + it, + p2, + title, } from '../../../../../lib/step'; -import { - NavigateToSettings, - NavigateToRegionSettings, - NavigateToComposeText, -} from '../../../../../steps/Navigate'; -import { SelectCountryCode, SetAreaCode } from '../../../../../steps/Settings'; +import { Login as CommonLogin } from '../../../../../steps/Login'; import { SendSMS } from '../../../../../steps/Messages'; -import type { Context } from '../../../../../interfaces'; import { CreateMock as CommonCreateMock, + MockDialingPlan, MockGetPhoneNumber, + MockMessageSync, + MockNumberParserV2, } from '../../../../../steps/Mock'; +import { + NavigateToComposeText, + NavigateToRegionSettings, + NavigateToSettings, +} from '../../../../../steps/Navigate'; +import { SetAreaCode, SetCountryCode } from '../../../../../steps/Settings'; + +import numberParserV2_AU_1014037 from './mockData/numberParserV2_AU_1014037.json'; +import numberParserV2_UK_79121016 from './mockData/numberParserV2_UK_79121016.json'; -interface LocalNumberFormattingProps { - country: string; +const dialingPlansData = { + records: [ + { + uri: 'https://api-rcapps-labs_domain/restapi/v1.0/dictionary/country/15', + id: '15', + name: 'Australia', + isoCode: 'AU', + callingCode: '61', + }, + { + uri: 'https://api-rcapps-labs_domain/restapi/v1.0/dictionary/country/160', + id: '160', + name: 'New Zealand', + isoCode: 'NZ', + callingCode: '64', + }, + { + uri: 'https://api-rcapps-labs_domain/restapi/v1.0/dictionary/country/224', + id: '224', + name: 'United Kingdom', + isoCode: 'GB', + callingCode: '44', + }, + ], +}; + +interface ExampleItem { + countryCode: string; + countryName: string; + countryCallingCode: string; + areaCode: string; phoneNumber: string; } -@autorun(test.skip) +@autorun(test) +@common @it @p2 @title('Local number formatting') export class LocalNumberFormatting extends Step { - Login?: StepFunction< - { - mockParams: { dialingPlansData?: Partial }; - }, - any - >; - CreateMock: StepFunction = CommonCreateMock; + CreateMock?: StepFunction; + Login?: StepFunction; + @examples(` - | country | phoneNumber | - | '(+1) United States' | '4091529' | - | '(+1) Canada' | '6504091529' | + | countryCode | countryName | countryCallingCode | areaCode | phoneNumber | + | 'UK' | 'United Kingdom' | '44' | '8' | '79121016' | + | 'AU' | 'Australia' | '61' | '117' | '1014037' | `) run() { - const { Login = CommonLogin, CreateMock } = this; + const { + CreateMock = CommonCreateMock, + Login = , + } = this; return ( - - + [ , - (props: LocalNumberFormattingProps, { rcMock }: Context) => { - rcMock.defaultInitMocks.delete(rcMock.getMessageSync); - rcMock.defaultInitMocks.add(() => - rcMock.getMessageSync({ repeat: 2 }), - ); - rcMock.defaultInitMocks.add(() => - rcMock.postParerPhoneNumbers((mockData) => { - switch (props.country) { - case '(+1) Canada': - return { - ...mockData, - homeCountry: { - uri: 'https://platform.devtest.ringcentral.com/restapi/v1.0/dictionary/country/39', - id: '39', - name: 'Canada', - isoCode: 'CA', - callingCode: '1', - }, - phoneNumbers: [ - { - originalString: '+16504091529', - country: { - uri: 'https://platform.devtest.ringcentral.com/restapi/v1.0/dictionary/country/1', - id: '1', - name: 'United States', - isoCode: 'US', - callingCode: '1', - }, - areaCode: '650', - subscriberNumber: '4091529', - formattedNational: '(650) 409-1529', - formattedInternational: '+1 (650) 4091529', - dialable: '6504091529', - e164: '+16504091529', - special: false, - normalized: '16504091529', - tollFree: false, - }, - ], - }; - case '(+1) United States': - default: - return mockData; - } - }, 2), - ); - }, - , + { + if (countryCode == 'AU') { + return numberParserV2_AU_1014037; + } + if (countryCode === 'UK') { + return numberParserV2_UK_79121016; + } + return mockData; }} />, - NavigateToSettings, - NavigateToRegionSettings, + dialingPlansData.records} />, ]} /> - [ - , - , + + [ + NavigateToSettings, + NavigateToRegionSettings, + , + , ]} /> - [ NavigateToComposeText, - async ({ phoneNumber }: LocalNumberFormattingProps) => ( - - ), + , ]} /> { + action={async (_: ExampleItem, { rcMock }: Context) => { await waitForRenderReady(); expect( - context.rcMock.fetchMock.called( + rcMock.fetchMock.called( 'express:/restapi/v1.0/account/~/extension/~/sms', ), ).toBeTruthy(); diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/SMS/Checkpoint/RCI-585.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/SMS/Checkpoint/RCI-585.test.tsx index 8334d8fef0..425650228b 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/SMS/Checkpoint/RCI-585.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/SMS/Checkpoint/RCI-585.test.tsx @@ -2,7 +2,10 @@ * RCI-585: Search on the Messages list * https://test_it_domain/test-cases/RCI-585 */ -import { Login as CommonLogin } from '../../../../../steps/Login'; +import { + generateMessageRecords, + mockMessageListData, +} from '../../../../../__mock__'; import type { StepFunction } from '../../../../../lib/step'; import { p2, @@ -16,42 +19,43 @@ import { When, And, } from '../../../../../lib/step'; +import { CommonLogin } from '../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../steps/CreateInstance'; +import { + CheckActionsAfterSearch, + TypingWordingInSearch, +} from '../../../../../steps/Messages'; import { CreateMock as CommonCreateMock, + MockExtensionsList, MockMessageList, MockMessageSync, + mockExtensionsListData, } from '../../../../../steps/Mock'; import { NavigateToMessagesTab, NavigateToTypeTabUnderMessage, } from '../../../../../steps/Navigate'; -import { - generateMessageRecords, - mockMessageListData, -} from '../../../../../__mock__'; -import { - CheckActionsAfterSearch, - TypingWordingInSearch, -} from '../../../../../steps/Messages'; -@autorun(test.skip) +@autorun(test) @p2 @title('Search phone number on the Messages list') export class SearchMessagesListNumber extends Step { - CustomLogin?: StepFunction; - CustomCreateMock?: StepFunction; + CustomLogin?: StepFunction = (props) => ( + + ); + CustomCreateMock?: StepFunction = CommonCreateMock; @examples(` | tabType | searchText | isMatched | type | matched | - | 'Fax' | '866' | true | 'Fax' | ['+18662105111', '+18662105112', '+18662105113'] | + | 'Fax' | '866' | true | 'Fax' | ['(866) 210-5111', '(866) 210-5112', '(866) 210-5113'] | | 'All' | 'test00000000' | false | 'Fax' | [] | `) run() { const { type, searchText, isMatched, tabType, matched } = this.context.example; - const { CustomLogin = CommonLogin, CustomCreateMock = CommonCreateMock } = - this; + const { CustomLogin, CustomCreateMock } = this; const messageList = generateMessageRecords({ direction: 'Inbound', @@ -113,13 +117,14 @@ export class SearchMessagesListNumber extends Step { } } -@autorun(test.skip) +@autorun(test) @p2 @title('Search name on the Messages list') export class SearchMessagesListName extends Step { - CustomLogin?: StepFunction; - CustomCreateMock?: StepFunction; - + CustomLogin?: StepFunction = (props) => ( + + ); + CustomCreateMock?: StepFunction = CommonCreateMock; @examples(` | tabType | searchText | isMatched | type | matched | | 'Voice' | 'cer' | true | 'VoiceMail' | ['cerrie1','Test cerrie'] | @@ -128,8 +133,7 @@ export class SearchMessagesListName extends Step { const { type, searchText, isMatched, tabType, matched } = this.context.example; - const { CustomLogin = CommonLogin, CustomCreateMock = CommonCreateMock } = - this; + const { CustomLogin, CustomCreateMock } = this; const messageList = generateMessageRecords({ direction: 'Inbound', @@ -155,6 +159,31 @@ export class SearchMessagesListName extends Step { mockMessageListData(messageList)} />, + ({ + ...mockData, + ...mockExtensionsListData([ + { + phoneNumber: '+18662105111', + phoneNumberUsageType: 'ContactNumber', + firstName: 'cerrie1', + lastName: '', + }, + { + phoneNumber: '+18662105112', + phoneNumberUsageType: 'ContactNumber', + firstName: 'Test', + lastName: 'cerrie', + }, + { + phoneNumber: '+18662105113', + phoneNumberUsageType: 'ContactNumber', + firstName: 'Hello', + lastName: '', + }, + ]), + })} + />, ]} /> @@ -203,23 +232,23 @@ export class SearchMessagesListName extends Step { } } -@autorun(test.skip) +@autorun(test) @p2 @title('Search content on the Messages list') export class SearchMessagesListContent extends Step { - CustomLogin?: StepFunction; - CustomCreateMock?: StepFunction; - + CustomLogin?: StepFunction = (props) => ( + + ); + CustomCreateMock?: StepFunction = CommonCreateMock; @examples(` | tabType | searchText | isMatched | type | matched | - | 'Text' | 'test' | true | 'Text' | ["+18662105111","+18662105112", "+18662105113"] | + | 'Text' | 'test' | true | 'Text' | ["(866) 210-5111","(866) 210-5112", "(866) 210-5113"] | `) run() { const { type, searchText, isMatched, tabType, matched } = this.context.example; - const { CustomLogin = CommonLogin, CustomCreateMock = CommonCreateMock } = - this; + const { CustomLogin, CustomCreateMock } = this; return ( ; - MockContactSearch: StepFunction< - { mockEntity: { name: string; phoneNumber: string } }, - any - >; - - @examples(` - | pasteData | dataShowAsCard | showAtInputBox | searchSuccessful | - | '(866) 557-3245' | ['(866) 557-3245'] | null | false | - | 'Test' | [] | 'Test' | true | - | 'hello2 world3' | [] | 'hello2 world3' | false | - | 'hello2 world3, +1 (866) 557-3246, Test, 102' | ['+1 (866) 557-3246', '102'] | null | false | - `) - run() { - const { Login = CommonLogin, MockContactSearch } = this; - return ( - - , - ]} - /> - - - - ); - } -} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/SMS/SendMessage/Checkpoint/RCI-3690.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/SMS/SendMessage/Checkpoint/RCI-3690.test.tsx index 006ed3fb16..1c9a2f9f5c 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/SMS/SendMessage/Checkpoint/RCI-3690.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/SMS/SendMessage/Checkpoint/RCI-3690.test.tsx @@ -25,7 +25,6 @@ | 2 |Click the conversation with {Contacts} | */ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { autorun, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/SMS/SendMessage/Checkpoint/RCI-443.spec.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/SMS/SendMessage/Checkpoint/RCI-443.spec.tsx deleted file mode 100644 index d311e0f99b..0000000000 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/SMS/SendMessage/Checkpoint/RCI-443.spec.tsx +++ /dev/null @@ -1,201 +0,0 @@ -/** - * RCI-443: Prompt user to enter area code if it isn't set before sending SMS to local number - * https://test_it_domain/test-cases/RCI-443 - * Preconditions: - * User is logged in to the 3-rd party - * CTI app is installed - * Account_A and Account_B belong to below brand accounts - * US/CA is set as the dialing plan and the area code is not set - * Entry point(/s): - * - | Entry |Account type | - | 1 |Account_A | - | 2 |Account_B | - - * > LoginCTI APP with {Account type} - * > Go to 'Messages' tab - * > Click'Compose Text' icon - * > Enter 7 digit lcoal number(eg.858-7301) - * > Fill Text 'Test sms' into 'Type message' field - * > Click 'Send' - */ - -import { fireEvent, screen } from '@testing-library/react'; - -import type { StepFunction, StepProp } from '../../../../../../lib/step'; -import { - autorun, - common, - examples, - Given, - it, - p2, - Scenario, - Step, - Then, - title, - When, -} from '../../../../../../lib/step'; -import { - CheckAlertMessage, - CloseAlertMessage, -} from '../../../../../../steps/Alert'; -import { CommonLogin } from '../../../../../../steps/CommonLogin'; -import { SendSMS } from '../../../../../../steps/Messages'; -import { - MockDialingPlan, - MockExtensionInfo, - MockPermission, -} from '../../../../../../steps/Mock'; -import { NavigateToComposeText } from '../../../../../../steps/Navigate'; -import { - CheckUserAtRegionSettings, - SetAreaCode, -} from '../../../../../../steps/Settings'; - -@autorun(test.skip) -@it -@p2 -@common -@title( - "Prompt user to enter area code if it isn't set before sending SMS to local number", -) -export class PromptUserToEnterAreaCode extends Step { - CustomLogin: StepFunction | null = null; - CustomCreateMock: StepProp | null = null; - @examples([ - { - entry: 1, - dialingPlansData: { - records: [ - { - uri: 'https://platform.ringcentral.com/restapi/v1.0/dictionary/country/1', - id: '1', - name: 'United States', - isoCode: 'US', - callingCode: '1', - }, - ], - }, - permissionData: { - id: 'SMSSending', - available: true, - }, - message: 'Please set area code to use 7-digit local phone numbers.', - }, - { - entry: 2, - dialingPlansData: { - records: [ - { - uri: 'https://platform.ringcentral.com/restapi/v1.0/dictionary/country/224', - id: '224', - name: 'United Kingdom', - isoCode: 'GB', - callingCode: '44', - }, - ], - }, - permissionData: { - id: 'SMSSending', - available: false, - }, - message: - "You don't have permission to send messages to recipients outside of your organization.", - }, - ]) - run() { - const { CustomLogin = CommonLogin, CustomCreateMock } = this; - return ( - - { - return features - .filter((feature) => feature.id !== 'SMSSending') - .concat([this.context.example.permissionData]); - }} - />, - { - mockData.regionalSettings.homeCountry = { - ...mockData.regionalSettings.homeCountry, - isoCode: 'US', - }; - return mockData; - }} - />, - this.context.example.dialingPlansData.records} - />, - CustomLogin, - ]} - /> - - } - /> - - } - /> - { - if (this.context.example.entry === 2) { - return [ - , - , - ]; - } - return ; - }} - /> - { - if (this.context.example.entry === 1) { - fireEvent.click(screen.getByText('area code')); - } - }} - /> - { - if (this.context.example.entry === 1) { - return CheckUserAtRegionSettings; - } - }} - /> - { - if (this.context.example.entry === 1) { - return [, NavigateToComposeText]; - } - }} - /> - { - if (this.context.example.entry === 1) { - expect(screen.getByTestId('messageInput')).toHaveValue( - 'Test sms', - ); - } - }} - /> - - ); - } -} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/SMS/SendMessage/Checkpoint/RCI-574.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/SMS/SendMessage/Checkpoint/RCI-574.test.tsx index bc144133bc..787250ce81 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/SMS/SendMessage/Checkpoint/RCI-574.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/SMS/SendMessage/Checkpoint/RCI-574.test.tsx @@ -3,6 +3,7 @@ * https://test_it_domain/test-cases/RCI-574 */ import phoneNumberData from '@ringcentral-integration/mock/src/platform/data/phoneNumber.json'; + import type { StepFunction } from '../../../../../../lib/step'; import { p2, @@ -14,25 +15,30 @@ import { title, When, } from '../../../../../../lib/step'; -import { - NavigateToComposeText, - NavigateToMessagesTab, -} from '../../../../../../steps/Navigate'; +import { CommonLogin } from '../../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../../steps/CreateInstance'; import { CheckComposeTestButton, CheckComposeTestPage, CheckMessageInput, CheckRecipientsInput, + CheckRecipientsInputRemoveButton, EntryTheLongestCharacter, } from '../../../../../../steps/Messages'; -import { MockPhoneNumber } from '../../../../../../steps/Mock'; +import { CreateMock, MockPhoneNumber } from '../../../../../../steps/Mock'; +import { + NavigateToComposeText, + NavigateToMessagesTab, +} from '../../../../../../steps/Navigate'; -@autorun(test.skip) +@autorun(test) @p2 @title('Compose text page') export class ComposeTextPage extends Step { - CustomLogin?: StepFunction; - CustomCreateMock?: StepFunction; + CustomLogin?: StepFunction = (props) => ( + + ); + CustomCreateMock?: StepFunction = CreateMock; run() { return ( Login CTI app with the account which can send sms successfully > Compose Text + */ +import type { AddressBook } from '@ringcentral-integration/commons/modules/AddressBook'; +import type { StepFunction } from '@ringcentral-integration/test-utils'; +import { + And, + Given, + Scenario, + Step, + Then, + When, + autorun, + common, + examples, + it, + p2, + title, +} from '@ringcentral-integration/test-utils'; + +import { Context } from '../../../../../../interfaces'; +import { CommonLoginEntry } from '../../../../../../steps/CommonLogin'; +import { + CheckPastingAction, + PasteMessageRecipients, +} from '../../../../../../steps/Messages'; +import { NavigateToComposeText } from '../../../../../../steps/Navigate'; + +interface ExampleItem { + pasteData: string; + dataShowAsCard: string[]; + showAtInputBox: string | null; + searchSuccessful: boolean; +} + +export interface MockContactSearchProps { + mockEntity: { name: string; phoneNumber: string }; +} + +export const MockAddressBookData: StepFunction< + MockContactSearchProps, + Context +> = ({ mockEntity }, context) => { + const addressBook: AddressBook = context.phone.addressBook; + const records = addressBook.addressBookData.records ?? []; + records.push({ + id: Math.floor(Math.random() * 1000), + firstName: mockEntity.name, + homePhone: mockEntity.phoneNumber, + }); + addressBook.setAddressBookData({ + ...addressBook.addressBookData, + records, + }); +}; + +@autorun(test) +@common +@it +@p2 +@title('Support pasting multiple numbers in Compose Text recipient input') +export class PastingMultipleNumbersInComposeText extends Step { + Login: StepFunction = CommonLoginEntry; + MockContactSearch: StepFunction< + { mockEntity: { name: string; phoneNumber: string } }, + any + > = MockAddressBookData; + + @examples(` + | pasteData | dataShowAsCard | showAtInputBox | searchSuccessful | + | '(866) 557-3245' | ['(866) 557-3245'] | null | false | + | 'Test' | [] | 'Test' | true | + | 'hello2 world3' | [] | 'hello2 world3' | false | + | 'hello2 world3, +1 (866) 557-3246, Test, 102' | ['+1 (866) 557-3246', '102'] | null | false | + `) + run() { + const { Login, MockContactSearch } = this; + return ( + + [ + Login, + MockContactSearch && ( + + ), + ]} + /> + [NavigateToComposeText]} /> + [ + /* + * 1. Paste ${pasteData} to recipients box + */ + , + ]} + /> + [ + /* + * 2. Check ${dataShowAsCard} data show as card + * 3. Check ${showAtInputBox} data show at input box + * 4. Check dropdown menu show successfully: ${searchSuccessful} + */ + , + ]} + /> + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Text/SendMessage/Checkpoint/RCI-3689.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Text/SendMessage/Checkpoint/RCI-3689.test.tsx index c527cdecbc..5600542f64 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Text/SendMessage/Checkpoint/RCI-3689.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Text/SendMessage/Checkpoint/RCI-3689.test.tsx @@ -51,7 +51,6 @@ | 2 |{Contacts}has a conversation with UserA > Send a message |Update the new message to the old conversation | */ - import type { StepProp } from '@ringcentral-integration/test-utils'; import { p2, @@ -65,7 +64,14 @@ import { When, } from '@ringcentral-integration/test-utils'; +import { mockMessageListData } from '../../../../../../__mock__'; import { CommonLogin } from '../../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../../steps/CreateInstance'; +import { + SendSMS, + ClickMessageItem, + CheckConversationHistory, +} from '../../../../../../steps/Messages'; import { CreateMock, MockExtensionsList, @@ -73,14 +79,7 @@ import { mockExtensionsListData, MockMessageList, } from '../../../../../../steps/Mock'; -import { mockMessageListData } from '../../../../../../__mock__'; -import { CreateInstance } from '../../../../../../steps/CreateInstance'; -import { - SendSMS, - ClickMessageItem, - CheckConversationHistory, -} from '../../../../../../steps/Messages'; -import { NavigateTo } from '../../../../../../steps/Router/action'; +import { NavigateTo } from '../../../../../../steps/Router'; const conversationId = 6185046919640017000; const oldComposeText = 'old message'; diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Text/TextActionButtons/Checkpoint/RCI-3681.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Text/TextActionButtons/Checkpoint/RCI-3681.test.tsx index 70fb7381eb..661643a2a2 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Text/TextActionButtons/Checkpoint/RCI-3681.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/Text/TextActionButtons/Checkpoint/RCI-3681.test.tsx @@ -34,7 +34,6 @@ | conversation5 |false | */ - import { p2, it, @@ -50,25 +49,13 @@ import { When, } from '@ringcentral-integration/test-utils'; -import { CommonLogin } from '../../../../../../steps/CommonLogin'; -import { - CreateMock, - MockExtensionsList, - MockMessageSync, - mockExtensionsListData, - MockMessageList, - MockAddressBookSync, - MockGetPhoneNumber, - MockCallLogs, - MockGetTelephonyState, - MockPresence, -} from '../../../../../../steps/Mock'; import { mockMessageListData } from '../../../../../../__mock__'; import { CallButtonBehavior, CheckActiveCallExist, CheckCallControlPage, } from '../../../../../../steps/Call'; +import { CommonLogin } from '../../../../../../steps/CommonLogin'; import { CheckInContactDetailsPage } from '../../../../../../steps/ContactsView'; import { CreateInstance } from '../../../../../../steps/CreateInstance'; import { @@ -76,8 +63,20 @@ import { CheckActionMenu, ClickActionButton, } from '../../../../../../steps/Messages'; +import { + CreateMock, + MockExtensionsList, + MockMessageSync, + mockExtensionsListData, + MockMessageList, + MockAddressBookSync, + MockGetPhoneNumber, + MockCallLogs, + MockGetTelephonyState, + MockPresence, +} from '../../../../../../steps/Mock'; +import { NavigateTo } from '../../../../../../steps/Router'; import { WaitForSpinner } from '../../../../../../steps/WaitForSpinner'; -import { NavigateTo } from '../../../../../../steps/Router/action'; @autorun(test) @it @@ -148,13 +147,14 @@ export class TextActionButtonsForContacts extends Step { if (contactType === 'personal') { actions.push( { const firstPersonalUser = personalUsers[0]; firstPersonalUser.firstName = contactName; firstPersonalUser.middleName = ''; firstPersonalUser.lastName = ''; firstPersonalUser.homePhone = phoneNumber; - return personalUsers; + return [firstPersonalUser]; // take the first one only for avoid multiple match }} />, ); @@ -191,12 +191,11 @@ export class TextActionButtonsForContacts extends Step { [ + action={async ({ contactName, parsedNumber }: any) => [ CheckActiveCallExist, , ]} /> @@ -318,7 +317,7 @@ export class TextActionButtonsForUnknown extends Step { } // common has no 3rd party contacts, skip this -@autorun(test.skip) +// @autorun(test.skip) @it @p2 @title( diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/VoiceMessage/VoiceMailLogAction/Checkpoint/RCI-3566.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/VoiceMessage/VoiceMailLogAction/Checkpoint/RCI-3566.test.tsx index a988ece179..d8cae4e071 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/VoiceMessage/VoiceMailLogAction/Checkpoint/RCI-3566.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/VoiceMessage/VoiceMailLogAction/Checkpoint/RCI-3566.test.tsx @@ -10,7 +10,6 @@ * Entry point(/s): * Messages > All/Voice tab > Expand VM1 */ - import { autorun, Given, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/VoiceMessage/VoiceMailLogAction/Checkpoint/RCI-3567.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/VoiceMessage/VoiceMailLogAction/Checkpoint/RCI-3567.test.tsx index 5195e375a8..6bd78232f0 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/VoiceMessage/VoiceMailLogAction/Checkpoint/RCI-3567.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/VoiceMessage/VoiceMailLogAction/Checkpoint/RCI-3567.test.tsx @@ -10,7 +10,6 @@ * Entry point(/s): * Messages > All/Voice tab > Expand VM1 */ - import { autorun, Given, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/VoiceMessage/VoiceMailLogAction/Checkpoint/RCI-3573.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/VoiceMessage/VoiceMailLogAction/Checkpoint/RCI-3573.test.tsx index 4e719ec26c..d16f7eeb08 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/VoiceMessage/VoiceMailLogAction/Checkpoint/RCI-3573.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/VoiceMessage/VoiceMailLogAction/Checkpoint/RCI-3573.test.tsx @@ -15,7 +15,6 @@ * > User A go to Messages page > All/Voice tab */ - import type { StepProp } from '@ringcentral-integration/test-utils'; import { p2, @@ -30,8 +29,14 @@ import { When, } from '@ringcentral-integration/test-utils'; -import { CheckInContactDetailsPage } from '../../../../../../steps/ContactsView'; +import { mockMessageListData } from '../../../../../../__mock__'; import { CommonLogin } from '../../../../../../steps/CommonLogin'; +import { CheckInContactDetailsPage } from '../../../../../../steps/ContactsView'; +import { CreateInstance } from '../../../../../../steps/CreateInstance'; +import { + CheckActionMenu, + ClickActionButton, +} from '../../../../../../steps/Messages'; import { CreateMock, MockMessageList, @@ -41,12 +46,6 @@ import { MockCallLogs, MockGetTelephonyState, } from '../../../../../../steps/Mock'; -import { mockMessageListData } from '../../../../../../__mock__'; -import { CreateInstance } from '../../../../../../steps/CreateInstance'; -import { - CheckActionMenu, - ClickActionButton, -} from '../../../../../../steps/Messages'; import { NavigateToVoiceMail } from '../../../../../../steps/Navigate'; @autorun(test) diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/VoiceMessage/VoiceMailLogAction/Checkpoint/RCI-3596.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/VoiceMessage/VoiceMailLogAction/Checkpoint/RCI-3596.test.tsx index 637aa45d29..bd2ecdd2d7 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/VoiceMessage/VoiceMailLogAction/Checkpoint/RCI-3596.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Messages/VoiceMessage/VoiceMailLogAction/Checkpoint/RCI-3596.test.tsx @@ -45,7 +45,6 @@ * Mark as Unread:Click the read first so the read button can change to unread * > Go to 'Messages' tab> Go to 'Voice' tab> Open the dropdown for one item */ - import type { StepProp } from '@ringcentral-integration/test-utils'; import { autorun, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Others/Checkpoint/RCI-1872.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Others/Checkpoint/RCI-1872.test.tsx index c6f9e8a781..1e965fd626 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Others/Checkpoint/RCI-1872.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Others/Checkpoint/RCI-1872.test.tsx @@ -6,11 +6,11 @@ * Entry point(/s): * 1. The microphone setting in Site settings is 'Ask', and the microphone settings in Chrome is 'Ask' */ - import { mockDevice } from '@ringcentral-integration/commons/integration-test/utils/SimulateWindowObject'; import { connectionStatus } from '@ringcentral-integration/commons/modules/Webphone'; -import { fireEvent, screen } from '@testing-library/react'; import { brandConfig } from '@ringcentral-integration/widgets-demo/dev-server/brandConfig'; +import { fireEvent, screen } from '@testing-library/react'; + import type { StepFunction } from '../../../../lib/step'; import { p1, @@ -23,13 +23,16 @@ import { When, And, common, + StepProp, } from '../../../../lib/step'; +import { MockSipProvision } from '../../../../steps/Call/Webphone'; +import { Login as CommonLogin } from '../../../../steps/Login'; +import { MockMessageSync } from '../../../../steps/Mock'; +import { NavigateTo } from '../../../../steps/Router'; import { CheckCallButtonActive, CheckCallButtonDisabled, } from '../../../../steps/dialer'; -import { Login as CommonLogin } from '../../../../steps/Login'; -import { NavigateTo } from '../../../../steps/Router/action'; @autorun(test) @common @@ -46,14 +49,23 @@ export class CheckMicrophonePermission extends Step { any >; appName: string; + CheckCallBtnDisabled: StepProp; run() { - const { Login = CommonLogin, appName = brandConfig.appName } = this; + const { + Login = CommonLogin, + appName = brandConfig.appName, + CheckCallBtnDisabled = CheckCallButtonDisabled, + } = this; return ( } + action={() => [ + , + , + , + ]} /> , ]} + action={[, CheckCallBtnDisabled]} /> New Event > RingCentral Scheduler * Outlook appointment: Login to Outlook > CalendarNew Meeting > RingCentral for Outlook */ - import { autorun, it, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/RCVScheduleOnBehalf/Checkpoint/RCI-1970.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/RCVScheduleOnBehalf/Checkpoint/RCI-1970.test.tsx index a107cec319..b58adc72a2 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/RCVScheduleOnBehalf/Checkpoint/RCI-1970.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/RCVScheduleOnBehalf/Checkpoint/RCI-1970.test.tsx @@ -8,7 +8,6 @@ * Scheduler:Login to Outlook: Calendar -> New Event > RingCentral Scheduler * Outlook appointment: Login to Outlook > CalendarNew Meeting > RingCentral for Outlook */ - import { autorun, it, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/RCVScheduleOnBehalf/Checkpoint/RCI-1973.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/RCVScheduleOnBehalf/Checkpoint/RCI-1973.test.tsx index 875c20b376..e718fc76e5 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/RCVScheduleOnBehalf/Checkpoint/RCI-1973.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/RCVScheduleOnBehalf/Checkpoint/RCI-1973.test.tsx @@ -8,7 +8,6 @@ * Scheduler:Login to Outlook: Calendar -> New Event > RingCentral Scheduler>Select other values from the 'Schedule on behalf of' dropdown * Outlook appointment: Login to Outlook > CalendarNew Meeting > RingCentral for Outlook>Select other values from the 'Schedule on behalf of' dropdown */ - import { autorun, it, @@ -53,7 +52,7 @@ export class RCI1973 extends Step { desc="'Sorry, something went wrong on our end. Try again' is shown on the top of the screen [L10N]" action={() => { - expect(this.context.phone.alert.danger).toBeCalledWith({ + expect(this.context.phone.alert.danger).toHaveBeenCalledWith({ message: 'meetingStatus-internalError', }); }} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/RCVScheduleOnBehalf/Checkpoint/RCI-1974.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/RCVScheduleOnBehalf/Checkpoint/RCI-1974.test.tsx index 8254ea9166..75720f1bcc 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/RCVScheduleOnBehalf/Checkpoint/RCI-1974.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/RCVScheduleOnBehalf/Checkpoint/RCI-1974.test.tsx @@ -8,7 +8,6 @@ * Scheduler:Login to Outlook: Calendar -> New Event > RingCentral Scheduler * Outlook appointment: Login to Outlook > CalendarNew Meeting > RingCentral for Outlook */ - import { autorun, it, @@ -84,7 +83,7 @@ export class RCI1974 extends Step { [L10N]" action={[ CheckInjection, - , + , ]} /> diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/RCVScheduleOnBehalf/Checkpoint/RCI-2064.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/RCVScheduleOnBehalf/Checkpoint/RCI-2064.test.tsx index 03a5625dd4..6333a00dd9 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/RCVScheduleOnBehalf/Checkpoint/RCI-2064.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/RCVScheduleOnBehalf/Checkpoint/RCI-2064.test.tsx @@ -8,7 +8,6 @@ * Scheduler:Outlook > New Event > office add-in > meeting setting page > Mark on 'Use Personal Meeting ID XXX-XXX-XXX' > Click 'Change Personal Meeting settings' > show a pop-up window >Click'Change' * Outlook appointment:Login to Outlook: > New Meeting/Appointment > RingCentral for outlook> meeting setting page > Mark on 'Use Personal Meeting ID XXX-XXX-XXX'> Click 'Change Personal Meeting settings' > show a pop-up window>Click'Change' */ - import type { RcVideoAPI } from '@ringcentral-integration/commons/interfaces/Rcv.model'; import { autorun, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/RCVScheduleOnBehalf/Checkpoint/RCI-4031.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/RCVScheduleOnBehalf/Checkpoint/RCI-4031.test.tsx index f8c8fce0f5..2b71805498 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/RCVScheduleOnBehalf/Checkpoint/RCI-4031.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/RCVScheduleOnBehalf/Checkpoint/RCI-4031.test.tsx @@ -15,7 +15,6 @@ * Scheduler:Login to Outlook: Calendar -> New Event > RingCentral Scheduler * Outlook appointment: Login to Outlook > CalendarNew Meeting > RingCentral for Outlook */ - import { autorun, it, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/E2EE/Checkpoint/RCI-3280.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/E2EE/Checkpoint/RCI-3280.test.tsx index 58b8a1fc5b..364f57c3c7 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/E2EE/Checkpoint/RCI-3280.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/E2EE/Checkpoint/RCI-3280.test.tsx @@ -16,7 +16,6 @@ * > Set 'Use end-to-end encryption by default for new meetings' option as in SW Extension * > Office Add-in > New Event > Schedule with RingCentral */ - import { autorun, examples, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/E2EE/Checkpoint/RCI-3281.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/E2EE/Checkpoint/RCI-3281.test.tsx index a1f39f8cd1..dd4fbe1511 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/E2EE/Checkpoint/RCI-3281.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/E2EE/Checkpoint/RCI-3281.test.tsx @@ -10,7 +10,6 @@ * Entry point(/s): * Office Add-in > Edit Calendar Event with PMI meeting > Schedule with RingCentral */ - import { autorun, it, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/E2EE/Checkpoint/RCI-3284.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/E2EE/Checkpoint/RCI-3284.test.tsx index 21b3b778a3..77b43588fb 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/E2EE/Checkpoint/RCI-3284.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/E2EE/Checkpoint/RCI-3284.test.tsx @@ -56,7 +56,6 @@ * >Office Add-in > New Event > Schedule with RingCentral * Note(/s): */ - import { autorun, examples, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/E2EE/Checkpoint/RCI-3286.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/E2EE/Checkpoint/RCI-3286.test.tsx index 3d09503cd8..349f71117a 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/E2EE/Checkpoint/RCI-3286.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/E2EE/Checkpoint/RCI-3286.test.tsx @@ -11,7 +11,6 @@ * Entry point(/s): * Office Add-in > New Event > Schedule with RingCentral */ - import { autorun, examples, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/E2EE/Checkpoint/RCI-3393.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/E2EE/Checkpoint/RCI-3393.test.tsx index 70d1c56266..fc396a9342 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/E2EE/Checkpoint/RCI-3393.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/E2EE/Checkpoint/RCI-3393.test.tsx @@ -62,7 +62,6 @@ * >Office Add-in > New Event > Schedule with RingCentral * Note(/s): */ - import { autorun, examples, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/E2EE/Checkpoint/RCI-3412.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/E2EE/Checkpoint/RCI-3412.test.tsx index 4641e93158..a09a3662e8 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/E2EE/Checkpoint/RCI-3412.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/E2EE/Checkpoint/RCI-3412.test.tsx @@ -16,7 +16,6 @@ * > Office Add-in > Add meeting */ - import { autorun, examples, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/PMI/Checkpoint/RCI-1900.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/PMI/Checkpoint/RCI-1900.test.tsx index 26354ba56c..cac49171d9 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/PMI/Checkpoint/RCI-1900.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/PMI/Checkpoint/RCI-1900.test.tsx @@ -16,7 +16,6 @@ * Scheduler:Outlook > New Event > office add-in > meeting setting page > Mark on 'Use Personal Meeting ID XXX-XXX-XXX' > Click 'Change Personal Meeting settings' > show a pop-up window >Click'Change' * Outlook appointment:Login to Outlook: > New Meeting/Appointment > RingCentral for outlook> meeting setting page > Mark on 'Use Personal Meeting ID XXX-XXX-XXX'> Click 'Change Personal Meeting settings' > show a pop-up window>Click'Change' */ - import { p2, it, @@ -32,6 +31,8 @@ import { common, } from '@ringcentral-integration/test-utils'; +import type { StepProp } from '../../../../../../../lib/step'; +import { SelectOptionFromDropDown } from '../../../../../../../steps/Common'; import { Login as CommonLogin } from '../../../../../../../steps/Login'; import { TurnOnToggle, @@ -45,12 +46,10 @@ import { ChangeMeetingOptionToOtherValue, ClickScheduleButton, } from '../../../../../../../steps/Meeting'; -import { SelectOptionFromDropDown } from '../../../../../../../steps/Common'; import { CheckModalExist, ClickConfirmInModal, } from '../../../../../../../steps/Modal'; -import type { StepProp } from '../../../../../../../lib/step'; @autorun(test.skip) @common diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/PMI/Checkpoint/RCI-4008.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/PMI/Checkpoint/RCI-4008.test.tsx index 9b1fb3a82c..a3bb4692b5 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/PMI/Checkpoint/RCI-4008.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/PMI/Checkpoint/RCI-4008.test.tsx @@ -9,7 +9,6 @@
  1. User log in to RC CTI app > schedule video meeting page > Select 'Use Personal Meeting ID XXX-XXX-XXX' > lnvite with XXX Calendar
  2. Outlook > New Event > office add-in > User log in to RC CTI app > meeting setting page > Select 'Use Personal Meeting ID XXX-XXX-XXX' > Update meeting
*/ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { p2, @@ -24,14 +23,15 @@ import { And, } from '@ringcentral-integration/test-utils'; +import { Login as CommonLogin } from '../../../../../../../steps/Login'; import { CheckboxIsChecked, SwitchUsePersonalMeetingId, ClickScheduleButton, CheckRCVPageDisplay, CheckMeetingTitle, + CheckPatchMeetingParams, } from '../../../../../../../steps/Meeting'; -import { Login as CommonLogin } from '../../../../../../../steps/Login'; @autorun(test.skip) @it @@ -65,7 +65,7 @@ export class RCI4008 extends Step { } />
); diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/PMI/UI/RCI-4019.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/PMI/UI/RCI-4019.test.tsx index ddc605a4d8..d5c02b3523 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/PMI/UI/RCI-4019.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/PMI/UI/RCI-4019.test.tsx @@ -8,7 +8,6 @@ * Scheduler:Login to Outlook: Calendar -> New Event > RingCentral Scheduler Appmeeting setting page > Mark on 'Use Personal Meeting ID XXX-XXX-XXX' * Outlook appointment:Login to Outlook: > New Meeting/Appointment > RingCentral for outlook> meeting setting page > Mark on 'Use Personal Meeting ID XXX-XXX-XXX' */ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { p2, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/RCVMeetingInvitation/Checkpoint/RCI-4035.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/RCVMeetingInvitation/Checkpoint/RCI-4035.test.tsx index d4fca36154..3486fd83f4 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/RCVMeetingInvitation/Checkpoint/RCI-4035.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/ScheduleMeeting/RCVMeetingInvitation/Checkpoint/RCI-4035.test.tsx @@ -36,10 +36,6 @@ | Extension_LanguageLocaleCode |en-US | */ -import { - CheckInvitation, - CheckRCVPageDisplay, -} from '../../../../../../../steps/Meeting'; import type { StepFunction } from '../../../../../../../lib/step'; import { p1, @@ -53,6 +49,10 @@ import { And, } from '../../../../../../../lib/step'; import { Login as CommonLogin } from '../../../../../../../steps/Login'; +import { + CheckInvitation, + CheckRCVPageDisplay, +} from '../../../../../../../steps/Meeting'; @autorun(test.skip) @it diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-1554.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-1554.test.tsx index 678a890f2e..ff53d5193b 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-1554.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-1554.test.tsx @@ -10,7 +10,6 @@ * SfB: Login > Meeting * Office Add-in: Login */ - import type { StepFunction } from '../../../../../../lib/step'; import { p2, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-1821.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-1821.test.tsx index 4823fd4b4a..dfc04b798d 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-1821.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-1821.test.tsx @@ -9,7 +9,6 @@ * Salesforce/RingCentral for Google/O365 Settings:More Menu > schedule video meeting > Security * LTI:Click the Schedule' button on the Home/Recents meeting list page */ - import type { StepFunction } from '../../../../../../lib/step'; import { p2, @@ -22,6 +21,10 @@ import { When, common, } from '../../../../../../lib/step'; +import { + SelectOptionFromDropDown, + CheckDropDownList, +} from '../../../../../../steps/Common'; import { Login as CommonLogin } from '../../../../../../steps/Login'; import { TurnOnToggle, @@ -30,10 +33,6 @@ import { CheckPatchMeetingParams, CheckRCVPageDisplay, } from '../../../../../../steps/Meeting'; -import { - SelectOptionFromDropDown, - CheckDropDownList, -} from '../../../../../../steps/Common'; @autorun(test.skip) @common diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-1822.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-1822.test.tsx index 66dec9cc53..057120d5e9 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-1822.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-1822.test.tsx @@ -13,12 +13,6 @@ | Off | */ -import { - CheckPostMeetingParams, - ClickScheduleButton, - CheckRCVPageDisplay, - SwitchToggleTo, -} from '../../../../../../steps/Meeting'; import { p2, it, @@ -33,6 +27,12 @@ import { And, } from '../../../../../../lib/step'; import { Login as CommonLogin } from '../../../../../../steps/Login'; +import { + CheckPostMeetingParams, + ClickScheduleButton, + CheckRCVPageDisplay, + SwitchToggleTo, +} from '../../../../../../steps/Meeting'; @autorun(test.skip) @it diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-3525.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-3525.test.tsx index 3e49ca2fd2..6646728638 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-3525.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-3525.test.tsx @@ -13,12 +13,6 @@ | Off |On | */ -import { - CheckPostMeetingParams, - ClickScheduleButton, - CheckRCVPageDisplay, - SwitchToggleTo, -} from '../../../../../../steps/Meeting'; import type { StepFunction } from '../../../../../../lib/step'; import { common, @@ -33,6 +27,12 @@ import { And, } from '../../../../../../lib/step'; import { Login as CommonLogin } from '../../../../../../steps/Login'; +import { + CheckPostMeetingParams, + ClickScheduleButton, + CheckRCVPageDisplay, + SwitchToggleTo, +} from '../../../../../../steps/Meeting'; @autorun(test.skip) @common diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-3876.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-3876.test.tsx index f278138865..ef19f5e4c6 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-3876.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-3876.test.tsx @@ -13,13 +13,6 @@ | Of | */ - -import { - CheckPostMeetingParams, - ClickScheduleButton, - CheckRCVPageDisplay, - SwitchToggleTo, -} from '../../../../../../steps/Meeting'; import type { StepFunction } from '../../../../../../lib/step'; import { common, @@ -34,6 +27,12 @@ import { p1, } from '../../../../../../lib/step'; import { Login as CommonLogin } from '../../../../../../steps/Login'; +import { + CheckPostMeetingParams, + ClickScheduleButton, + CheckRCVPageDisplay, + SwitchToggleTo, +} from '../../../../../../steps/Meeting'; @autorun(test.skip) @common diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-4177.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-4177.test.tsx index 7155055a58..134ae15069 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-4177.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-4177.test.tsx @@ -10,14 +10,6 @@ * SfB: Login > Meeting * Office Add-in: Login */ - -import { - CheckPostMeetingParams, - ClickScheduleButton, - CheckRCVPageDisplay, - TurnOffToggle, - CheckboxIsChecked, -} from '../../../../../../steps/Meeting'; import type { StepFunction } from '../../../../../../lib/step'; import { common, @@ -31,6 +23,13 @@ import { p2, } from '../../../../../../lib/step'; import { Login as CommonLogin } from '../../../../../../steps/Login'; +import { + CheckPostMeetingParams, + ClickScheduleButton, + CheckRCVPageDisplay, + TurnOffToggle, + CheckboxIsChecked, +} from '../../../../../../steps/Meeting'; import { CheckPasswordInputNotExist } from '../../../../../../steps/Meeting/Check/CheckPasswordField'; @autorun(test.skip) diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-4178.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-4178.test.tsx index 7131b8650a..8241d77144 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-4178.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/Checkpoint/RCI-4178.test.tsx @@ -10,14 +10,6 @@ * SfB: Login > Meeting * Office Add-in: Login */ - -import { - CheckPostMeetingParams, - ClickScheduleButton, - CheckRCVPageDisplay, - TurnOnToggle, - CheckPatchMeetingParams, -} from '../../../../../../steps/Meeting'; import type { StepFunction } from '../../../../../../lib/step'; import { common, @@ -31,6 +23,13 @@ import { p2, } from '../../../../../../lib/step'; import { Login as CommonLogin } from '../../../../../../steps/Login'; +import { + CheckPostMeetingParams, + ClickScheduleButton, + CheckRCVPageDisplay, + TurnOnToggle, + CheckPatchMeetingParams, +} from '../../../../../../steps/Meeting'; import { EnterPassword } from '../../../../../../steps/Meeting/Operate/OperatePasswordField'; @autorun(test.skip) diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/NonPMIWaitingRoom/Checkpoint/RCI-2739.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/NonPMIWaitingRoom/Checkpoint/RCI-2739.test.tsx index 0caccd6b7a..25ed58b84f 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/NonPMIWaitingRoom/Checkpoint/RCI-2739.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/NonPMIWaitingRoom/Checkpoint/RCI-2739.test.tsx @@ -17,7 +17,6 @@ * > Select{Waiting room option in SW} * > Then lock 'Require participants to enter a waiting room before joining the meeting' */ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { p2, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/NonPMIWaitingRoom/RCI-1975.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/NonPMIWaitingRoom/RCI-1975.test.tsx index d9cbd65690..0fde222beb 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/NonPMIWaitingRoom/RCI-1975.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/NonPMIWaitingRoom/RCI-1975.test.tsx @@ -10,7 +10,6 @@ * 1.Login to Outlook: Calendar -> New Event > RCV Add-in App * 2.Microsoft 365/Google:More>Schedule Video Meeting>Security>'Enable wating room ' */ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { p2, @@ -24,6 +23,10 @@ import { When, } from '@ringcentral-integration/test-utils'; +import { + SelectOptionFromDropDown, + CheckDropDownList, +} from '../../../../../../steps/Common'; import { Login as CommonLogin } from '../../../../../../steps/Login'; import { CheckItemLabel, @@ -32,10 +35,6 @@ import { TurnOffToggle, TurnOnToggle, } from '../../../../../../steps/Meeting'; -import { - SelectOptionFromDropDown, - CheckDropDownList, -} from '../../../../../../steps/Common'; @autorun(test.skip) @it diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/NonPMIWaitingRoom/RCI-1977.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/NonPMIWaitingRoom/RCI-1977.test.tsx index 33cc25dc6e..9411d4aa02 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/NonPMIWaitingRoom/RCI-1977.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/NonPMIWaitingRoom/RCI-1977.test.tsx @@ -11,7 +11,6 @@ * 1.Login to Outlook: Calendar -> New Event > RCV Add-in App * 2.Microsoft 365/Google:More>Schedule Video Meeting>Security>'Enable wating room ' */ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { p2, @@ -25,6 +24,7 @@ import { When, } from '@ringcentral-integration/test-utils'; +import { SelectOptionFromDropDown } from '../../../../../../steps/Common'; import { Login as CommonLogin } from '../../../../../../steps/Login'; import { CheckboxIsChecked, @@ -35,7 +35,6 @@ import { CheckDropDownStatus, TurnOnToggle, } from '../../../../../../steps/Meeting'; -import { SelectOptionFromDropDown } from '../../../../../../steps/Common'; @autorun(test.skip) @it diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/PMIWaitingRoom/RCI-2740.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/PMIWaitingRoom/RCI-2740.test.tsx index 1bd301be01..4f6adc9f2c 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/PMIWaitingRoom/RCI-2740.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/PMIWaitingRoom/RCI-2740.test.tsx @@ -19,7 +19,6 @@ * > Select{Waiting room option in SW} * > Then lock 'Require participants to enter a waiting room before joining the meeting' */ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { p2, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/PMIWaitingRoom/RCI-2741.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/PMIWaitingRoom/RCI-2741.test.tsx index a793f10ac4..26c79e90be 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/PMIWaitingRoom/RCI-2741.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/PMIWaitingRoom/RCI-2741.test.tsx @@ -12,7 +12,6 @@ * 2.Security settings in SW : SWAdmin Portal > Meetings ->Security settings * Settings: */ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { p2, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/PMIWaitingRoom/RCI-2743.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/PMIWaitingRoom/RCI-2743.test.tsx index b0f163d723..d46ba4e6b3 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/PMIWaitingRoom/RCI-2743.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/PMIWaitingRoom/RCI-2743.test.tsx @@ -21,7 +21,6 @@ * Note(/s): * RCV web:https://v.ringcentral.com/welcome/join/ */ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { p2, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/PMIWaitingRoom/RCI-2744.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/PMIWaitingRoom/RCI-2744.test.tsx index 29911f913d..cce9351930 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/PMIWaitingRoom/RCI-2744.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/ScheduleMeeting/RCV/Settings/PMIWaitingRoom/RCI-2744.test.tsx @@ -26,7 +26,6 @@ * Note(/s): * RCV web:https://v.ringcentral.com/welcome/join/ */ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { p2, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/Calling/RCI-4502.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/Calling/RCI-4502.test.tsx index 4900af285f..232bb87f66 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/Calling/RCI-4502.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/Calling/RCI-4502.test.tsx @@ -27,24 +27,28 @@ export class CheckCallWithSoftphoneOption extends Step { CustomCreateMock?: StepFunction; @examples(` - | brandId | brandName | spartanName | showSoftphoneOption | - | '1210' | 'rc' | 'RingCentral Phone' | true | - | '3420' | 'att' | 'Office@Hand Phone' | true | - | '7710' | 'bt' | 'BT Cloud Work Phone' | true | - | '7310' | 'telus' | 'TELUS Business Connect Phone' | true | - | '6010' | 'avaya' | 'Avaya Cloud Office Phone' | true | - | '2020' | 'atos' | 'Unify Office Phone' | true | - | '2110' | 'rainbow' | 'Rainbow Office Phone' | true | - | '2210' | 'verizon' | 'RingCentral Phone' | true | - | '7010' | 'vodafone' | 'RingCentral Phone' | true | - | '4210' | 'ecotel' | 'N.A.' | false | - | '4810' | 'mcm' | 'N.A.' | false | - | '4610' | 'eastlink' | 'N.A.' | false | - | '4710' | 'versatel' | 'N.A.' | false | - | '4910' | 'frontier' | 'N.A.' | false | - | '2030' | 'dttelekom' | 'N.A.' | false | - | '2040' | 'dtatos' | 'N.A.' | false | - | '2050' | 'sunrise' | 'N.A.' | false | + | brandId | subBrandId | brandName | spartanName | showSoftphoneOption | + | '1210' | undefined | 'rc' | 'RingCentral Phone' | true | + | '3420' | undefined | 'att' | 'AT&T Office@Hand Phone' | true | + | '7710' | undefined | 'bt' | 'BT Cloud Work Phone' | true | + | '7310' | undefined | 'telus' | 'TELUS Business Connect Phone' | true | + | '6010' | undefined | 'avaya' | 'Avaya Cloud Office Phone' | true | + | '2020' | undefined | 'atos' | 'Unify Office Phone' | true | + | '2110' | undefined | 'rainbow' | 'Rainbow Office Phone' | true | + | '2210' | undefined | 'verizon' | 'RingCentral Phone' | true | + | '7010' | undefined | 'vodafone' | 'RingCentral Phone' | true | + | '4210' | undefined | 'ecotel' | 'N.A.' | false | + | '4810' | undefined | 'mcm' | 'N.A.' | false | + | '4610' | undefined | 'eastlink' | 'N.A.' | false | + | '4710' | undefined | 'versatel' | 'N.A.' | false | + | '4910' | undefined | 'frontier' | 'N.A.' | false | + | '2030' | undefined | 'dttelekom' | 'N.A.' | false | + | '2040' | undefined | 'dtatos' | 'N.A.' | false | + | '2050' | undefined | 'sunrise' | 'N.A.' | false | + | '2000' | '2000.Optus' | 'sunrise' | 'N.A.' | false | + | '3000' | '3000.Brightspeed' | 'sunrise' | 'N.A.' | false | + | '3000' | '3000.NWNC' | 'sunrise' | 'N.A.' | false | + | '3000' | '3000.Zayo' | 'sunrise' | 'N.A.' | false | `) run() { return ( @@ -52,10 +56,21 @@ export class CheckCallWithSoftphoneOption extends Step { desc="Verify ${brandName} brand call with softphone option on calling settings" action={[ this.CustomCreateMock, - ({ brandId }: { brandId: string }) => ( + ({ + brandId, + subBrandId, + }: { + brandId: string; + subBrandId: string; + }) => ( { mockData.serviceInfo.brand.id = brandId; + if (subBrandId !== undefined) { + mockData.serviceInfo.uBrand = { + id: subBrandId, + }; + } return mockData; }} /> diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/Calling/RCI-803.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/Calling/RCI-803.test.tsx index 129693bcbf..d13afe1231 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/Calling/RCI-803.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/Calling/RCI-803.test.tsx @@ -1,106 +1,160 @@ -import type { StepFunction } from '../../../../lib/step'; +/** + * RCI-803: Calling settings(Tooltips) + * https://test_it_domain/test-cases/RCI-803 + * Preconditions: + * 1. User is logged into 3rd party + * 2. User has installed and logged into CTI app + * RC: RingCentral Phone + * AT&T: Office@Hand Phone + * BT: -BT Cloud Work + * TELUS: TELUS Business Connect Phone + * Entry point(/s): + * + */ import { - autorun, - title, - Scenario, - Given, - When, And, - Then, + Given, + Scenario, Step, - it, + Then, + When, + autorun, common, -} from '../../../../lib/step'; -import { NavigateTo } from '../../../../steps/Router/action'; -import { ClickLogoutButton } from '../../../../steps/Settings'; + examples, + it, + p2, + title, +} from '@ringcentral-integration/test-utils'; + +import type { Context } from '../../../../interfaces'; +import type { StepFunction } from '../../../../lib/step'; +import { Login as CommonLogin } from '../../../../steps/Login'; +import { NavigateTo } from '../../../../steps/Router'; import { - ExpandDropdown, + CheckCallSettingPage, + ExpandCallingSettingDropdown, SelectCallingSetting, -} from '../../../../steps/Settings/actions/SetCallSetting'; -import { CheckInfoTooltip } from '../../../../steps/Settings/checks/CheckCallSettingPage'; -import { Login as CommonLogin } from '../../../../steps/Login'; +} from '../../../../steps/Settings'; + +interface ExampleItem { + brand: string; + jupiterAppName: string; + softphoneAppName: string; +} @autorun(test) -@common @it -@title('Check Calling Settings tooltip') +@p2 +@title('Calling settings(Tooltips)') +@common export class CheckCallingSettingsTooltip extends Step { - expectedJupiterName?: string; - expectedPhoneName?: string; Login?: StepFunction; + @examples(` + | brand | jupiterAppName | softphoneAppName | + | 'att' | 'AT&T Office@Hand App' | 'AT&T Office@Hand Phone' | + | 'bt' | 'BT Cloud Work App' | 'BT Cloud Work Phone' | + | 'rc' | 'RingCentral App' | 'RingCentral Phone' | + | 'telus' | 'TELUS Business Connect App' | 'TELUS Business Connect Phone' | + `) run() { - const { - expectedJupiterName = 'RingCentral App', - expectedPhoneName = 'RingCentral Phone', - Login = CommonLogin, - } = this; + const { Login = CommonLogin } = this; return ( - + - } - /> { + // Set locale to en-US + await phone.locale.setLocale('en-US'); + // Mock names + phone.brand.setDynamicConfig({ + ...phone.brand.brandConfig, + callWithJupiter: { + ...phone.brand.brandConfig.callWithJupiter, + name: jupiterAppName, + }, + callWithSoftphone: { + ...phone.brand.brandConfig.callWithSoftphone, + name: softphoneAppName, + }, + }); + }} /> - } + } /> + } /> - } + desc="Select 'Browser' in 'Make my calls with', then clicks on the 'i' button(Not applicable to Zendesk 2.0)" + action={[ + ExpandCallingSettingDropdown, + , + ]} /> + } /> - } + desc="Select '{BrandName} App' in 'Make my calls with', then clicks on the 'i' button + (Only RC brand)" + action={({ jupiterAppName }: ExampleItem) => [ + ExpandCallingSettingDropdown, + , + ]} /> - } + desc="The tooltip should read 'Use this option to make and receive calls using your{BrandName} App.' + [L10N]" + action={({ jupiterAppName }: ExampleItem) => [ + , + ]} /> - [ + ExpandCallingSettingDropdown, + , + ]} + /> + [ + , + ]} /> } + desc="Select 'RingOut' in 'Make my calls with', then clicks on the 'i' button" + action={[ + ExpandCallingSettingDropdown, + , + ]} /> , - , + , + , ]} /> diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/Checkpoint/RCI-541.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/Checkpoint/RCI-541.test.tsx new file mode 100644 index 0000000000..46cb5b5b95 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/Checkpoint/RCI-541.test.tsx @@ -0,0 +1,121 @@ +/** + * RCI-541: Default page after successful login + * https://test_it_domain/test-cases/RCI-541 + * Preconditions: + * The user has login 3rd party + * CTI app is integrated + * There are 2 kinds of accounts which are the first-time login to the RC CTI app( clear browser history and local storage) + AccountA + * AccountB + * AccountA + * AccountB + * Entry point(/s): + * + | App |Default page | + | Google |Dialer page | + | Firefox |Dialer page | + | Outlook |Dialer page | + | Salesforce |Settings page | + | Dynamics |Settings page | + + */ +import { + p2, + it, + autorun, + StepProp, + Scenario, + Step, + Then, + title, + When, + WaitForRenderReady, + common, +} from '@ringcentral-integration/test-utils'; + +import { Login as CommonLogin } from '../../../../steps/Login'; +import { CreateMock, MockExtensionInfo } from '../../../../steps/Mock'; +import { NavigateTo } from '../../../../steps/Router'; +import { ClickLogoutButton } from '../../../../steps/Settings'; +import { CheckInDialPage } from '../../../../steps/dialer'; + +@autorun(test) +@common +@it +@p2 +@title('Default page after successful login') +export class DefaultPage extends Step { + Login = CommonLogin; + CreateMock: StepProp = CreateMock; + ReOpenInNewTab: StepProp = () => ({}); + CheckDefaultPage: StepProp = CheckInDialPage; + run() { + const { CreateMock, Login, ReOpenInNewTab, CheckDefaultPage } = this; + return ( + + { + mockData.id = '208594111'; + mockData.extensionNumber = '101'; + return mockData; + }} + />, + { + mockData.id = '208594222'; + mockData.extensionNumber = '102'; + return mockData; + }} + />, + Login, + ]} + /> + + + + , + ClickLogoutButton, + WaitForRenderReady, + , + WaitForRenderReady, + ]} + /> + + , + ClickLogoutButton, + WaitForRenderReady, + , + WaitForRenderReady, + ]} + /> + + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/Presence/RCI-5655.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/Presence/RCI-5655.test.tsx index 5a87e5d99d..493e077f16 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/Presence/RCI-5655.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/Presence/RCI-5655.test.tsx @@ -10,7 +10,6 @@ * Entry point(/s): * > Go to Settings */ - import { p2, it, @@ -24,20 +23,21 @@ import { When, WaitForRenderReady, } from '@ringcentral-integration/test-utils'; + import type { StepFunction } from '../../../../lib/step'; import { Login as CommonLogin } from '../../../../steps/Login'; -import { NavigateTo } from '../../../../steps/Router/action'; import { CreateMock as CommonCreateMock, MockMessageSync, MockPresence, } from '../../../../steps/Mock'; +import { NavigateTo } from '../../../../steps/Router'; import { ClickLogoutButton, SetPresenceStatus, SetAcceptCallQueueCalls, -} from '../../../../steps/Settings/actions'; -import { CheckAcceptCallQueueCalls } from '../../../../steps/Settings'; + CheckAcceptCallQueueCalls, +} from '../../../../steps/Settings'; @autorun(test) @it diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/RegionSetting/CheckPoint/RCI-4551.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/RegionSetting/CheckPoint/RCI-4551.test.tsx index d748d1849c..05ea6a3734 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/RegionSetting/CheckPoint/RCI-4551.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/RegionSetting/CheckPoint/RCI-4551.test.tsx @@ -12,16 +12,17 @@ import { When, common, } from '@ringcentral-integration/test-utils'; + +import { generateDialPlanData } from '../../../../../__mock__/generateDialPlanData'; import type { StepFunction } from '../../../../../lib/step'; +import { Login } from '../../../../../steps/Login'; import { MockDialingPlan, CreateMock as CommonCreateMock, } from '../../../../../steps/Mock'; -import { generateDialPlanData } from '../../../../../__mock__/generateDialPlanData'; import { NavigateToRegionSettings } from '../../../../../steps/Navigate'; -import { CheckAreaCodeField, SetAreaCode } from '../../../../../steps/Settings'; -import { Login } from '../../../../../steps/Login'; import { NavigateTo } from '../../../../../steps/Router'; +import { CheckAreaCodeField, SetAreaCode } from '../../../../../steps/Settings'; @autorun(test.skip) @common diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/RegionSetting/CheckPoint/RCI-516.spec.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/RegionSetting/CheckPoint/RCI-516.spec.tsx deleted file mode 100644 index b62a87e756..0000000000 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/RegionSetting/CheckPoint/RCI-516.spec.tsx +++ /dev/null @@ -1,197 +0,0 @@ -/** - * RCI-516: Region setting for only one US/CA dialing plan - * https://test_it_domain/test-cases/RCI-516 - * Preconditions: - * Account 1:no DL on the company account - * Account 2: only Headquarter (the same country as company account) DL on the company account (eg. RC US account only has US DL(s), RC CA account only has CA DL(s)) - * 3. The user has never logged in RC app - * Entry point(/s): - * - */ - -import type { StepFunction } from '@ringcentral-integration/test-utils'; -import { - autorun, - Scenario, - Step, - title, - When, - Given, - Then, - p2, - examples, -} from '@ringcentral-integration/test-utils'; -import { screen, getNodeText } from '@testing-library/react'; -import devicesBody from '@ringcentral-integration/commons/integration-test/mock/data/device.json'; -import { brandConfig } from '@ringcentral-integration/widgets-demo/dev-server/brandConfig'; -import { - format, - formatTypes, - isE164, -} from '@ringcentral-integration/phone-number'; -import { - NavigateToHistory, - NavigateToSettings, -} from '../../../../../steps/Navigate'; -import { NavigateToRegionSettings } from '../../../../../steps/Navigate/actions/NavigateToRegionSettings'; -import { CommonLogin } from '../../../../../steps/CommonLogin'; -import { CheckAreaCodeField, SetAreaCode } from '../../../../../steps/Settings'; -import type { Context } from '../../../../../interfaces'; - -@p2 -@autorun(test.skip) -@title('Region setting for only one CA dialing plan') -export class RegionSettingForOnlyOneDialingPlan extends Step { - Login?: StepFunction< - { - dialingPlansData?: any; - }, - any - >; - appName: string; - @examples([ - { - mockParams: { - dialingPlansData: { - records: [ - { - uri: 'https://platform.ringcentral.com/restapi/v1.0/dictionary/country/39', - id: '39', - name: 'Canada', - isoCode: 'CA', - callingCode: '1', - }, - ], - }, - deviceData: { records: devicesBody.records }, - }, - country: '(+1) Canada', - countryCode: 'CA', - }, - ]) - run() { - const { Login = CommonLogin, appName = brandConfig.appName as string } = - this; - return ( - - - - { - expect( - screen.getByText( - 'Please set your area code. This will be used for local dialing.', - ), - ).toBeInTheDocument(); - expect( - screen.getByText(context.example.country), - ).toBeInTheDocument(); - return ; - }} - /> - , NavigateToHistory]} - /> - { - const phoneNumber = getNodeText( - screen.queryAllByTestId('currentName')[0], - ); - if ( - appName.includes('Dynamics') || - appName.includes('Zendesk') || - appName.includes('HubSpot') - ) { - // E164 - expect(isE164(phoneNumber)).toBe(true); - } else { - // local format - expect( - format({ - phoneNumber, - countryCode: context.example.countryCode, - type: formatTypes.local, - }), - ).toBe(phoneNumber); - } - }} - /> - - ); - } -} - -@p2 -@autorun(test.skip) -@title('Region setting for only one US dialing plan') -export class RegionSettingForOnlyUSDialingPlan extends Step { - Login?: StepFunction< - { - dialingPlansData?: any; - }, - any - >; - appName: string; - @examples([ - { - mockParams: { - dialingPlansData: { - records: [ - { - uri: 'https://platform.ringcentral.com/restapi/v1.0/dictionary/country/1', - id: '1', - name: 'United States', - isoCode: 'US', - callingCode: '1', - }, - ], - }, - deviceData: { records: [] }, - }, - country: '(+1) United States', - countryCode: 'US', - }, - ]) - run() { - const { Login = CommonLogin, appName = brandConfig.appName as string } = - this; - return ( - - - - { - expect( - screen.getByText( - 'Please set your area code. This will be used for local dialing.', - ), - ).toBeInTheDocument(); - expect( - screen.getByText(context.example.country), - ).toBeInTheDocument(); - return ; - }} - /> - - ); - } -} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/RegionSetting/CheckPoint/RCI-519.spec.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/RegionSetting/CheckPoint/RCI-519.spec.tsx index 805f212562..5ea28e831c 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/RegionSetting/CheckPoint/RCI-519.spec.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/RegionSetting/CheckPoint/RCI-519.spec.tsx @@ -6,37 +6,38 @@ * Entry point(/s): * */ +import type dialingPlanBody from '@ringcentral-integration/commons/integration-test/mock/data/dialingPlan.json'; import type { StepFunction } from '@ringcentral-integration/test-utils'; import { - autorun, + Given, Scenario, Step, - title, - When, - Given, Then, - examples, + When, + autorun, + common, p3, + title, } from '@ringcentral-integration/test-utils'; -import { screen, getNodeText, fireEvent, act } from '@testing-library/react'; -import type dialingPlanBody from '@ringcentral-integration/commons/integration-test/mock/data/dialingPlan.json'; + +import { ClickBackButton } from '../../../../../steps/Common'; +import { CommonLogin } from '../../../../../steps/CommonLogin'; import { CheckRouterNavigation, NavigateToSettings, } from '../../../../../steps/Navigate'; import { NavigateToRegionSettings } from '../../../../../steps/Navigate/actions/NavigateToRegionSettings'; -import { CommonLogin } from '../../../../../steps/CommonLogin'; -import { SelectCountryCode } from '../../../../../steps/Settings/actions/SelectCountryCode'; import { CheckAreaCodeField, CheckCountryCodeField, + SetAreaCode, + SetCountryCode, } from '../../../../../steps/Settings'; -import { ClickBackButton } from '../../../../../steps/Common'; const dialingPlansData = { records: [ { - uri: 'https://api-rcapps-xmnup.rclabenv.com/restapi/v1.0/dictionary/country/75', + uri: 'https://api-rcapps-xmnuplabs_domain/restapi/v1.0/dictionary/country/75', id: '75', name: 'France', isoCode: 'FR', @@ -53,15 +54,15 @@ const dialingPlansData = { }; @autorun(test.skip) +@common @p3 @title('Region - back button') export class RegionBackButton extends Step { Login?: StepFunction< - { - mockParams: { dialingPlansData?: Partial }; - }, + { mockParams: { dialingPlansData?: Partial } }, any >; + run() { const { Login = CommonLogin } = this; return ( @@ -83,19 +84,17 @@ export class RegionBackButton extends Step { action={[ NavigateToSettings, NavigateToRegionSettings, - , - , - () => { - act(() => { - fireEvent.change(screen.getByTestId('areaCodeInputField'), { - target: { value: '666' }, - }); - }); - - expect(screen.getByTestId('areaCodeInputField')).toHaveValue( - '666', - ); - }, + // (+33) France + , + , + , ]} /> @@ -111,7 +110,11 @@ export class RegionBackButton extends Step { , + // (+33) France + , , ]} /> diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/RegionSettings/Checkpoint/RCI-516.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/RegionSettings/Checkpoint/RCI-516.test.tsx new file mode 100644 index 0000000000..3ebfdd32f0 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/RegionSettings/Checkpoint/RCI-516.test.tsx @@ -0,0 +1,130 @@ +/** + * RCI-516: Region setting for only one dialing plan + * https://test_it_domain/test-cases/RCI-516 + * Preconditions: + * + | Accounts |DL type(Dialing plan) |showAreaCode | + | 1 |Non US/CA/PR |True | + | 2 |RC US/RC CA |False | + + * 3. The user has never logged in RC app + * Entry point(/s): + * > Login CTI app with {Accounts} + */ +import { + Given, + Scenario, + Step, + Then, + When, + autorun, + common, + examples, + it, + p2, + title, + type StepProp, +} from '@ringcentral-integration/test-utils'; + +import { generateDialPlanData } from '../../../../../__mock__/generateDialPlanData'; +import { CommonLogin } from '../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../steps/CreateInstance'; +import { + CreateMock as CommonCreateMock, + MockDialingPlan, + MockExtensionInfo, +} from '../../../../../steps/Mock'; +import { NavigateToSettings } from '../../../../../steps/Navigate'; +import { NavigateToRegionSettings } from '../../../../../steps/Navigate/actions/NavigateToRegionSettings'; +import { + CheckAreaCodeField, + CheckCountryCodeField, + CheckCountryCodeHint, +} from '../../../../../steps/Settings'; + +// An improvement is created to adjust the country hint message +// RCINT-38284 + +interface ExampleItem { + countryId: string; + countryCode: string; + countryName: string; + countryCallingCode: string; + showAreaCode: boolean; + countryHint: string; +} + +@autorun(test) +@it +@p2 +@title('Region setting for only one dialing plan') +@common +export class RegionSettingForOneDialingPlan extends Step { + Login?: StepProp; + CreateMock?: StepProp; + + @examples(` + | countryId | countryCode | countryName | countryCallingCode | showAreaCode | countryHint | + | '1' | 'US' | 'United States' | '1' | false | 'Please set your area code. This will be used for local dialing.' | + | '39' | 'GB' | 'United Kingdom' | '44' | true | '' | + `) + run() { + const { + CreateMock = CommonCreateMock, + Login = , + } = this; + return ( + [ + CreateMock, + { + const country = mockData.regionalSettings.homeCountry; + country.id = example.countryId; + country.isoCode = example.countryCode; + country.name = example.countryName; + country.callingCode = example.countryCallingCode; + return mockData; + }} + />, + [ + generateDialPlanData( + example.countryCallingCode, + example.countryId, + example.countryName, + example.countryCode, + ), + ]} + />, + ]} + > + + + [ + , + , + , + ]} + /> + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/RegionSettings/Checkpoint/RCI-517.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/RegionSettings/Checkpoint/RCI-517.test.tsx index b5f608a12a..007576cfbb 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/RegionSettings/Checkpoint/RCI-517.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/Settings/RegionSettings/Checkpoint/RCI-517.test.tsx @@ -15,7 +15,6 @@ * > Go to the 'Settings' page * > Clickthe 'Region' setting option */ - import type { StepProp } from '@ringcentral-integration/test-utils'; import { p2, @@ -29,28 +28,28 @@ import { When, } from '@ringcentral-integration/test-utils'; +import { generateDialPlanData } from '../../../../../__mock__/generateDialPlanData'; +import { + CheckActiveCallExist, + MakeCall, + CheckCallControlPage, +} from '../../../../../steps/Call'; import { CommonLogin } from '../../../../../steps/CommonLogin'; +import { CreateInstance } from '../../../../../steps/CreateInstance'; import { CreateMock, MockExtensionInfo, MockGetPhoneNumber, MockDialingPlan, } from '../../../../../steps/Mock'; -import { CreateInstance } from '../../../../../steps/CreateInstance'; +import { NavigateTo } from '../../../../../steps/Router'; import { ClickSaveButton, CheckCountryCodeField, CheckCountryCodeHint, CheckAreaCodeField, - SelectCountryCode, + SetCountryCode, } from '../../../../../steps/Settings'; -import { - CheckActiveCallExist, - MakeCall, - CheckCallControlPage, -} from '../../../../../steps/Call'; -import { NavigateTo } from '../../../../../steps/Router/action'; -import { generateDialPlanData } from '../../../../../__mock__/generateDialPlanData'; @autorun(test) @it @@ -99,15 +98,17 @@ export class RegionSettingForMultipleDialingPlan extends Step { , , , ]} /> ( - )} /> @@ -115,7 +116,8 @@ export class RegionSettingForMultipleDialingPlan extends Step { desc="The country name and country code show up" action={async ({ callingMode, countryName }: any) => [ , , ]} diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/EDP/RCI-4176.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/EDP/RCI-4176.test.tsx index 240d2afbc4..14cb90bf3e 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/EDP/RCI-4176.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/EDP/RCI-4176.test.tsx @@ -1,6 +1,7 @@ import { And, autorun, + common, examples, Given, it, @@ -12,6 +13,7 @@ import { When, } from '@ringcentral-integration/test-utils'; +import { generateDialPlanData } from '../../../../__mock__/generateDialPlanData'; import type { StepProp } from '../../../../lib/step'; import { CheckParseApiCalledWithParams, @@ -19,13 +21,17 @@ import { } from '../../../../steps/Call'; import { CommonLogin } from '../../../../steps/CommonLogin'; import { SendSMS } from '../../../../steps/Messages/actions'; -import { MockAccountInfo, MockDialingPlan, MockExtensionInfo } from '../../../../steps/Mock'; -import { NavigateTo } from '../../../../steps/Router/action'; +import { + MockAccountInfo, + MockDialingPlan, + MockExtensionInfo, +} from '../../../../steps/Mock'; +import { NavigateTo } from '../../../../steps/Router'; import { SetAreaCode } from '../../../../steps/Settings'; -import { generateDialPlanData } from '../../../../__mock__/generateDialPlanData'; @autorun(test.skip) @it +@common @p2 @title('EDP:Send requests to the API when making a call/sms') export class RCI4176 extends Step { diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/EDP/RCI-4187.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/EDP/RCI-4187.test.tsx index 9ac8ae3c50..57661e99ee 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/EDP/RCI-4187.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/EDP/RCI-4187.test.tsx @@ -17,7 +17,6 @@ * Entry point(/s): * User log in to CTI app > Getdefault area code from api as{Area code by API} > Make a call/Send a message to {originalStrings} */ - import { And, autorun, @@ -32,6 +31,7 @@ import { When, } from '@ringcentral-integration/test-utils'; +import { generateDialPlanData } from '../../../../__mock__/generateDialPlanData'; import type { StepProp } from '../../../../lib/step'; import { CheckPassAreaCode, MakeCall } from '../../../../steps/Call'; import { CommonLogin } from '../../../../steps/CommonLogin'; @@ -41,9 +41,8 @@ import { MockNumberParserV2, MockPermission, } from '../../../../steps/Mock'; -import { NavigateTo } from '../../../../steps/Router/action'; +import { NavigateTo } from '../../../../steps/Router'; import { SetAreaCode } from '../../../../steps/Settings'; -import { generateDialPlanData } from '../../../../__mock__/generateDialPlanData'; @autorun(test.skip) @it diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/OCP/RCI-4103.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/OCP/RCI-4103.test.tsx index a2139b24b1..d190bd912c 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/OCP/RCI-4103.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/OCP/RCI-4103.test.tsx @@ -12,6 +12,7 @@ import { When, } from '@ringcentral-integration/test-utils'; +import { generateDialPlanData } from '../../../../__mock__/generateDialPlanData'; import type { StepProp } from '../../../../lib/step'; import { CheckParseApiCalledWithParams, @@ -25,9 +26,8 @@ import { MockExtensionInfo, MockPermission, } from '../../../../steps/Mock'; -import { NavigateTo } from '../../../../steps/Router/action'; +import { NavigateTo } from '../../../../steps/Router'; import { SetAreaCode } from '../../../../steps/Settings'; -import { generateDialPlanData } from '../../../../__mock__/generateDialPlanData'; @autorun(test.skip) @it diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/OCP/RCI-4105.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/OCP/RCI-4105.test.tsx index 50d2ca123c..6436a5dc34 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/OCP/RCI-4105.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/OCP/RCI-4105.test.tsx @@ -27,7 +27,6 @@ | 9 |9911 |911 | */ - import { Category } from '@ringcentral-integration/commons/interfaces/NumberParserResponse.interface'; import { autorun, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/OCP/RCI-4107.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/OCP/RCI-4107.test.tsx index d33c1e4316..bbabdd5a20 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/OCP/RCI-4107.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/OCP/RCI-4107.test.tsx @@ -58,7 +58,6 @@ | 7 |72230003 |(30) 223-0003 |- | */ - import { Category } from '@ringcentral-integration/commons/interfaces/NumberParserResponse.interface'; import { autorun, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/RCI-4358.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/RCI-4358.test.tsx index 767b3da3d1..0b133badea 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/RCI-4358.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/RCI-4358.test.tsx @@ -21,7 +21,6 @@ | RC-UK |7 |7 |2334567 |Not Match extNot MatchPSTN |20 |2234567 | Incorrect extension |2234567 | */ - import { Category } from '@ringcentral-integration/commons/interfaces/NumberParserResponse.interface'; import { And, @@ -50,7 +49,7 @@ import { MockNumberParserV2, MockPermission, } from '../../../steps/Mock'; -import { NavigateTo } from '../../../steps/Router/action'; +import { NavigateTo } from '../../../steps/Router'; import { SetAreaCode } from '../../../steps/Settings'; // country code should in RegionSettings.availableCountries, diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/RCI-4362.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/RCI-4362.test.tsx index d8deec63d8..0659160ea0 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/RCI-4362.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/RCI-4362.test.tsx @@ -15,7 +15,6 @@ * Entry point(/s): * Login SW by admin account */ - import { Category } from '@ringcentral-integration/commons/interfaces/NumberParserResponse.interface'; import { And, @@ -31,6 +30,7 @@ import { When, } from '@ringcentral-integration/test-utils'; +import { generateDialPlanData } from '../../../__mock__/generateDialPlanData'; import type { StepProp } from '../../../lib/step'; import { CheckCallControlPage, MakeCall } from '../../../steps/Call'; import { CommonLogin } from '../../../steps/CommonLogin'; @@ -46,9 +46,8 @@ import { MockPermission, MockPresence, } from '../../../steps/Mock'; -import { NavigateTo } from '../../../steps/Router/action'; +import { NavigateTo } from '../../../steps/Router'; import { SetAreaCode } from '../../../steps/Settings'; -import { generateDialPlanData } from '../../../__mock__/generateDialPlanData'; @autorun(test.skip) @it diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/RCI-4366.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/RCI-4366.test.tsx index be4ccf8810..c30474617f 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/RCI-4366.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/SmartDialPlan/RCI-4366.test.tsx @@ -21,7 +21,6 @@ | RC-UK |7 |7 |2234567 |Not Match extNot MatchPSTN |20 |2234567 | Incorrect extension | */ - import { Category } from '@ringcentral-integration/commons/interfaces/NumberParserResponse.interface'; import { And, @@ -37,6 +36,7 @@ import { When, } from '@ringcentral-integration/test-utils'; +import { generateDialPlanData } from '../../../__mock__/generateDialPlanData'; import type { StepProp } from '../../../lib/step'; import { CloseAlertMessage } from '../../../steps/Alert'; import { CommonLogin } from '../../../steps/CommonLogin'; @@ -50,9 +50,8 @@ import { MockNumberParserV2, MockPermission, } from '../../../steps/Mock'; -import { NavigateTo } from '../../../steps/Router/action'; +import { NavigateTo } from '../../../steps/Router'; import { SetAreaCode } from '../../../steps/Settings'; -import { generateDialPlanData } from '../../../__mock__/generateDialPlanData'; @autorun(test.skip) @it diff --git a/packages/ringcentral-widgets-test/test/features/CommonWidgets/WebPhoneRegistration/JapanEmergencyNotification/RCI-4046.test.tsx b/packages/ringcentral-widgets-test/test/features/CommonWidgets/WebPhoneRegistration/JapanEmergencyNotification/RCI-4046.test.tsx index a4a2cf157d..aff5f6ca0d 100644 --- a/packages/ringcentral-widgets-test/test/features/CommonWidgets/WebPhoneRegistration/JapanEmergencyNotification/RCI-4046.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CommonWidgets/WebPhoneRegistration/JapanEmergencyNotification/RCI-4046.test.tsx @@ -1,5 +1,5 @@ -import { sleep } from '@ringcentral-integration/commons/utils'; import { callingSettingsMessages } from '@ringcentral-integration/commons/modules/CallingSettings'; +import { sleep } from '@ringcentral-integration/commons/utils'; import { waitFor } from '@testing-library/react'; import { mockPhoneNumberData } from '../../../../__mock__/data'; @@ -25,12 +25,12 @@ import { SpyOnAlertWithMessages, } from '../../../../steps/Alert'; import { Login as CommonLogin } from '../../../../steps/Login'; -import { NavigateTo } from '../../../../steps/Router/action'; +import { NavigateTo } from '../../../../steps/Router'; import { ClickSaveButton, - ExpandDropdown, + ExpandCallingSettingDropdown, SelectCallingSetting, -} from '../../../../steps/Settings/actions/SetCallSetting'; +} from '../../../../steps/Settings'; // TODO: skip this widget test since webphone mock has some errors, only run in other projects // User story: @@ -178,7 +178,7 @@ export class TestJapanEmergencyServiceNotification extends Step { <> - + diff --git a/packages/ringcentral-widgets-test/test/features/Commons/CheckAuth.test.tsx b/packages/ringcentral-widgets-test/test/features/Commons/CheckAuth.test.tsx new file mode 100644 index 0000000000..507bd2457a --- /dev/null +++ b/packages/ringcentral-widgets-test/test/features/Commons/CheckAuth.test.tsx @@ -0,0 +1,99 @@ +import { + autorun, + Scenario, + Step, + Then, + title, + When, + StepFunction, + And, +} from '@ringcentral-integration/test-utils'; + +import { CommonLogin } from '../../steps/CommonLogin'; +import { CreateInstance } from '../../steps/CreateInstance'; +import { CreateMock } from '../../steps/Mock'; + +@autorun(test) +@title('check Auth module') +export class CheckAuthModule extends Step { + run() { + return ( + { + rcMock.defaultInitMocks.delete(rcMock.postOauthToken); + rcMock.defaultInitMocks.add(() => { + rcMock.postOauthToken({ failure: true, failureCode: 403 }); + }); + }) as StepFunction, + , + ]} + > + { + const { auth } = phone; + payload.loggedInFn = jest.fn(); + payload.loggedInFn1 = jest.fn(); + auth.addAfterLoggedInHandler(payload.loggedInFn); + const loggedInFnCallback1 = auth.addAfterLoggedInHandler( + payload.loggedInFn1, + ); + expect(typeof loggedInFnCallback1 === 'function').toBeTruthy(); + loggedInFnCallback1(); + + payload.refreshErrorFn = jest.fn(); + payload.refreshErrorFn1 = jest.fn(); + auth.addRefreshErrorHandler(payload.refreshErrorFn); + const refreshErrorFnCallback1 = auth.addRefreshErrorHandler( + payload.refreshErrorFn1, + ); + expect( + typeof refreshErrorFnCallback1 === 'function', + ).toBeTruthy(); + refreshErrorFnCallback1(); + }) as StepFunction + } + /> + {}} />} + /> + { + expect(payload.loggedInFn).toHaveBeenCalled(); + expect(payload.loggedInFn1).not.toHaveBeenCalled(); + }) as StepFunction + } + /> + { + const { auth } = phone; + try { + await auth.refreshToken(); + } catch (err) { + // + } + }) as StepFunction + } + /> + { + expect(payload.refreshErrorFn).toHaveBeenCalled(); + expect(payload.refreshErrorFn1).not.toHaveBeenCalled(); + }) as StepFunction + } + /> + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/Commons/CheckMessage.test.tsx b/packages/ringcentral-widgets-test/test/features/Commons/CheckMessage.test.tsx new file mode 100644 index 0000000000..f3bb977abc --- /dev/null +++ b/packages/ringcentral-widgets-test/test/features/Commons/CheckMessage.test.tsx @@ -0,0 +1,66 @@ +import type { StepFunction } from '@ringcentral-integration/test-utils'; +import { + p2, + it, + autorun, + examples, + Given, + Scenario, + Step, + Then, + title, + When, +} from '@ringcentral-integration/test-utils'; + +import { Login as CommonLogin } from '../../steps/Login'; +import { + CreateMock as CommonCreateMock, + MockMessageError, +} from '../../steps/Mock'; + +@autorun(test) +@it +@p2 +@title('Check message syncToken invalid') +export class MessageSyncTokenInvalid extends Step { + Login = CommonLogin as StepFunction; + CreateMock = CommonCreateMock as StepFunction; + + @examples(` + | errorCode | message | fetchTimes | + | 'CMN-403' | 'In order to call this API endpoint, application needs to have [xxx] permission' | 1 | + | 'CMN-101' | 'Parameter [syncToken] value is invalid.' | 2 | + | 'CMN-101' | 'Parameter [syncToken] value is xxxx.' | 2 | + | 'MSG-333' | 'Parameter [syncToken] is invalid' | 2 | + | 'MSG-333' | 'Parameter [syncToken] is xxxx' | 2 | + `) + run() { + return ( + + , + ]} + /> + } /> + { + expect(rcMock.fetchMock).toHaveFetchedTimes( + this.example.fetchTimes, + (url) => + url.indexOf( + 'restapi/v1.0/account/~/extension/~/message-sync?recordCountPerConversation=15&syncType=FSync', + ) > -1, + ); + }} + /> + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/Commons/CheckOAuth.test.tsx b/packages/ringcentral-widgets-test/test/features/Commons/CheckOAuth.test.tsx index 1fea07d94b..410bc87f4a 100644 --- a/packages/ringcentral-widgets-test/test/features/Commons/CheckOAuth.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/Commons/CheckOAuth.test.tsx @@ -8,8 +8,8 @@ import { Step, common, } from '../../lib/step'; -import { OAuthCheck } from '../../steps/OAuthCheck'; import { Login as CommonLogin } from '../../steps/Login'; +import { OAuthCheck } from '../../steps/OAuthCheck'; @autorun(test) @common diff --git a/packages/ringcentral-widgets-test/test/features/ContactList/ContactListUI.test.tsx b/packages/ringcentral-widgets-test/test/features/ContactList/ContactListUI.test.tsx index cbbf9dfe55..08671335ec 100644 --- a/packages/ringcentral-widgets-test/test/features/ContactList/ContactListUI.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/ContactList/ContactListUI.test.tsx @@ -1,3 +1,5 @@ +import { AllContactSourceName } from '@ringcentral-integration/commons/lib/contactHelper'; +import { mockModuleGenerator } from '@ringcentral-integration/commons/test/lib/mockModule'; import { autorun, title, @@ -8,8 +10,6 @@ import { Step, examples, } from '@ringcentral-integration/test-utils'; -import { AllContactSourceName } from '@ringcentral-integration/commons/lib/contactHelper'; -import { mockModuleGenerator } from '@ringcentral-integration/commons/test/lib/mockModule'; import { ContactListUI } from '@ringcentral-integration/widgets/modules/ContactListUI'; const getMockModule = () => diff --git a/packages/ringcentral-widgets-test/test/features/CrmCommon/CallControl/RCI-1836.test.tsx b/packages/ringcentral-widgets-test/test/features/CrmCommon/CallControl/RCI-1836.test.tsx index 15188363b9..34d3e8a78a 100644 --- a/packages/ringcentral-widgets-test/test/features/CrmCommon/CallControl/RCI-1836.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/CrmCommon/CallControl/RCI-1836.test.tsx @@ -17,7 +17,8 @@ * 2. Make an inbound/outbound call and answer * 3. Initial warm transfer(Only for Salesforce) */ - +import { callDirection } from '@ringcentral-integration/commons/enums/callDirections'; +import { PartyStatusCode } from '@ringcentral-integration/commons/integration-test/mock/telephonySessionBuilder'; import type { StepFunction } from '@ringcentral-integration/test-utils'; import { p2, @@ -29,8 +30,7 @@ import { title, When, } from '@ringcentral-integration/test-utils'; -import { PartyStatusCode } from '@ringcentral-integration/commons/integration-test/mock/telephonySessionBuilder'; -import callDirection from '@ringcentral-integration/commons/enums/callDirections'; + import { CheckContainsAlertMessage, CloseAlertMessage, @@ -45,11 +45,11 @@ import { import { CheckInCallLogPage } from '../../../steps/CallLog'; import type { LoginProps } from '../../../steps/CommonLogin'; import { CommonLogin } from '../../../steps/CommonLogin'; -import { ClickCallButton, InputToField } from '../../../steps/dialer'; -import { NavigateToDialer } from '../../../steps/Navigate'; -import { MockUnHoldCallFail } from '../../../steps/Mock/MockUnHoldCallFail'; import { MockMuteFail } from '../../../steps/Mock'; +import { MockUnHoldCallFail } from '../../../steps/Mock/MockUnHoldCallFail'; +import { NavigateToDialer } from '../../../steps/Navigate'; import { NetworkOff } from '../../../steps/Network'; +import { ClickCallButton, InputToField } from '../../../steps/dialer'; @autorun(test.skip) @it diff --git a/packages/ringcentral-widgets-test/test/features/CrmCommon/OutboundCall/RCI-4492.test.tsx b/packages/ringcentral-widgets-test/test/features/CrmCommon/OutboundCall/RCI-4492.test.tsx new file mode 100644 index 0000000000..80641c9f4f --- /dev/null +++ b/packages/ringcentral-widgets-test/test/features/CrmCommon/OutboundCall/RCI-4492.test.tsx @@ -0,0 +1,346 @@ +/** + * RCI-4492: Outbound call - from ${BrandName} App(Jupiter) + * https://test_it_domain/test-cases/RCI-4492 + * Preconditions: + * User is logged-in to 3rd party + * CTI app is integrated, + * The user has logged in CTI app + * User has installed {BrandNameApp} + * Entry point(/s): + * + | Brand |Jupiter app name |Link | + | RC |'RingCentral App' |'rcapp://r/call?number={phoneNumber}' | + | AT&T |'AT&T Office@Hand App' |'officeathand://r/call?number=' | + | BT |'BT Cloud Work App' |'com.bt.cloudwork.app://r/call?number=' | + | Telus |'TELUS Business Connect App' |'rctelus://r/call?number=' | + | Atos |'Unify Office App' |'unifyoffice://r/call?number=' | + | Rainbow |'Rainbow Office App' |'com.rainbowoffice.app://r/call?number=' | + | Vodafone |'Vodafone Business with RingCentral' |'com.ringcentral.vodafonebusiness.app://r/call?number=' | + + * Settings > Calling > Make my calls with > {Jupiter app name} + */ +import { callingOptions } from '@ringcentral-integration/commons/modules/CallingSettings'; +import { + p0, + it, + autorun, + examples, + Given, + Scenario, + Step, + Then, + title, + When, + StepProp, + And, + common, + type StepFunction, +} from '@ringcentral-integration/test-utils'; + +import { CloseAlertMessage } from '../../../steps/Alert'; +import { + CheckCallWithJupiterLink, + MakeOutboundCall, +} from '../../../steps/Call'; +import { CommonLogin } from '../../../steps/CommonLogin'; +import { CreateInstance } from '../../../steps/CreateInstance'; +import { + CreateMock, + MockNumberParserV2, + MockGetPhoneNumber, + MockMessageSync, + MockCallLogs, + MockCallLogSync, + generateCallLogData, + MockAccountInfo, +} from '../../../steps/Mock'; +import { NavigateToDialer } from '../../../steps/Navigate'; +import { NavigateTo } from '../../../steps/Router'; +import { + ClickSaveButton, + ExpandCallingSettingDropdown, + SelectCallingSetting, +} from '../../../steps/Settings'; + +beforeEach(() => { + window.open = jest.fn(); +}); + +afterEach(() => { + jest.resetAllMocks(); +}); + +const brandData = [ + { + brand: 'rc', + brandId: '1210', + brandName: 'RingCentral App', + link: /rcapp:\/\/r\/call\?number=.+/, + }, + { + brand: 'att', + brandId: '3420', + brandName: 'AT&T Office@Hand App', + link: /officeathand:\/\/r\/call\?number=.+/, + }, + { + brand: 'bt', + brandId: '7710', + brandName: 'BT Cloud Work App', + link: /com.bt.cloudwork.app:\/\/r\/call\?number=.+/, + }, + { + brand: 'telus', + brandId: '7310', + brandName: 'TELUS Business Connect App', + link: /rctelus:\/\/r\/call\?number=.+/, + }, + { + brand: 'atos', + brandId: '2020', + brandName: 'Unify Office App', + link: /unifyoffice:\/\/r\/call\?number=.+/, + }, +]; + +export const phoneNumberData = [ + { + areaCode: '650', + type: 'localNumber', + phoneNumber: '1234567', + }, + { + type: 'companyWithExt', + phoneNumber: '+16501234567*102', + }, + { + type: 'international', + phoneNumber: '+441234567890', + }, + { + type: 'did', + phoneNumber: '6501234567', + }, + { + type: 'dl', + phoneNumber: '6501234567', + }, + { + type: 'ext', + phoneNumber: '102', + }, +]; + +const exampleData: typeof brandData & typeof phoneNumberData = []; +brandData.forEach((item, index) => { + if (index === 0) { + exampleData.push({ ...item, ...phoneNumberData[index] }); + } + exampleData.push({ ...item, ...phoneNumberData[index + 1] }); +}); + +@autorun(test) +@it +@p0 +@common +@title('Outbound call - from {BrandName} App(Jupiter)') +export class RCI4492 extends Step { + Login: StepProp = () => { + this.context.example = { + ...this.context.example, + brandName: 'RingCentral App', + link: /https:\/\/app\.ringcentral\.com\/r\/call\?number=.+/, + }; + return ; + }; + CreateMock: StepProp = CreateMock; + MakeOutboundCall: StepProp = MakeOutboundCall; + GoToCallingSetting: StepProp = () => ; + @examples(exampleData) + run() { + const { Login, CreateMock, MakeOutboundCall, GoToCallingSetting } = this; + return ( + { + mockData.serviceInfo.brand.id = this.example.brandId; + return mockData; + }} + />, + , + ]} + > + + { + mockData.results[0].formats[0] = { + ...mockData.results[0].formats[0], + e164Extended: this.context.example.phoneNumber, + }; + return mockData; + }} + />, + (({ brandName }: { brandName: string }, { phone }) => { + if (phone?.callingSettings?.callWith === callingOptions.jupiter) { + return []; + } + return [ + GoToCallingSetting, + ExpandCallingSettingDropdown, + , + ClickSaveButton, + , + ]; + }) as StepFunction, + ]} + /> + + + + ); + } +} + +@autorun(test) +@it +@p0 +@common +@title('Outbound call - from {BrandName} App(Jupiter)') +export class RCI4492UniversalLink extends Step { + Login: StepProp = () => { + this.context.example = { + ...this.context.example, + brandName: 'RingCentral App', + link: /https:\/\/app\.ringcentral\.com\/r\/call\?number=.+/, + }; + return ; + }; + CreateMock: StepProp = CreateMock; + GoToCallingSetting: StepProp = () => ; + IDBCallWithJupiterLink: string = 'https://app.ringcentral.com'; + CheckCallWithJupiter: StepProp = CheckCallWithJupiterLink; + MakeOutboundCall: StepProp = MakeOutboundCall; + @examples([exampleData[0]]) + run() { + const { + Login, + CreateMock, + MakeOutboundCall, + GoToCallingSetting, + IDBCallWithJupiterLink, + CheckCallWithJupiter, + } = this; + return ( + { + mockData.serviceInfo.brand.id = this.example.brandId; + return mockData; + }} + />, + , + ]} + > + + { + const originalBrandConfig = phone.brand.brandConfig; + phone.brand.setDynamicConfig({ + ...originalBrandConfig, + allowJupiterUniversalLink: true, + callWithJupiter: { + ...originalBrandConfig.callWithJupiter, + // make sure the link has no trailing slash similar to BSS' appMainDomain property + link: IDBCallWithJupiterLink, + }, + }); + }, + { + mockData.results[0].formats[0] = { + ...mockData.results[0].formats[0], + e164Extended: this.context.example.phoneNumber, + }; + return mockData; + }} + />, + (({ brandName }: { brandName: string }, { phone }) => { + if (phone?.callingSettings?.callWith === callingOptions.jupiter) { + return []; + } + return [ + GoToCallingSetting, + ExpandCallingSettingDropdown, + , + ClickSaveButton, + , + ]; + }) as StepFunction, + ]} + /> + + + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/Dialpad/RCI-4071.spec.tsx b/packages/ringcentral-widgets-test/test/features/Dialpad/RCI-4071.spec.tsx index 8ff4e2b2bc..b27644cd01 100644 --- a/packages/ringcentral-widgets-test/test/features/Dialpad/RCI-4071.spec.tsx +++ b/packages/ringcentral-widgets-test/test/features/Dialpad/RCI-4071.spec.tsx @@ -1,4 +1,5 @@ import { Login } from '../../steps/Login'; + import { CheckHitEnterInputMoreThanThreeSpaces } from './index'; CheckHitEnterInputMoreThanThreeSpaces({ Login }); diff --git a/packages/ringcentral-widgets-test/test/features/Messages/RCI-3563.test.tsx b/packages/ringcentral-widgets-test/test/features/Messages/RCI-3563.test.tsx index 38e145c0dc..931d3abbf8 100644 --- a/packages/ringcentral-widgets-test/test/features/Messages/RCI-3563.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/Messages/RCI-3563.test.tsx @@ -11,26 +11,34 @@ import { title, When, } from '@ringcentral-integration/test-utils'; +import { screen, waitFor } from '@testing-library/react'; + import { mockMessageListData } from '../../__mock__'; +import { ClickBackButton } from '../../steps/Call'; +import { CommonLogin } from '../../steps/CommonLogin'; +import { CreateInstance } from '../../steps/CreateInstance'; import { ExpandTheActionMenu, CheckClickToCallButton, CheckClickToSmsButton, } from '../../steps/Messages'; -import { MockMessageList, MockMessageSync } from '../../steps/Mock'; +import { CreateMock, MockMessageList, MockMessageSync } from '../../steps/Mock'; import { NavigateToMessagesTab } from '../../steps/Navigate/actions/NavigateToMessages'; interface IVoicemailProps { CustomLogin: StepFunction; CustomCreateMock: StepFunction; } -@autorun(test.skip) +@autorun(test) @it @p2 @title('Verify make call and send SMS from voicemail action button') export class VoicemailCallAndSmsAction extends Step { - CustomLogin: StepFunction | null = null; - CustomCreateMock: StepFunction | null = null; + CustomLogin: StepFunction = (props) => ( + + ); + CustomCreateMock: StepFunction = CreateMock; + @examples(` | user | number | | 'UserB' | '18662105111' | @@ -85,7 +93,16 @@ export class VoicemailCallAndSmsAction extends Step { desc="> Go to the Entry point > Check the voicemail from UserB > Expand the voicemail" - action={[NavigateToMessagesTab, ExpandTheActionMenu]} + action={[ + async () => { + await waitFor(() => { + expect(screen.queryByTestId('backButton')).toBeInTheDocument(); + }); + }, + ClickBackButton, + NavigateToMessagesTab, + ExpandTheActionMenu, + ]} /> + + + + ); + } +} diff --git a/packages/ringcentral-widgets-test/test/features/OutlookScheduler/UpdateMeeting/Checkpoint/RCI-2368.test.tsx b/packages/ringcentral-widgets-test/test/features/OutlookScheduler/UpdateMeeting/Checkpoint/RCI-2368.test.tsx index a3551e625d..8e59982f59 100644 --- a/packages/ringcentral-widgets-test/test/features/OutlookScheduler/UpdateMeeting/Checkpoint/RCI-2368.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/OutlookScheduler/UpdateMeeting/Checkpoint/RCI-2368.test.tsx @@ -11,7 +11,6 @@ * Entry point(/s): * Scheduler:Login to Outlook: Calendar -> New Event > RingCentral Scheduler AppOutlook appointment :Login to Outlook: > New Meeting/Appointment > RingCentral for outlook */ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { p2, @@ -27,6 +26,7 @@ import { When, } from '@ringcentral-integration/test-utils'; +import { SelectOptionFromDropDown } from '../../../../steps/Common'; import { Login as CommonLogin } from '../../../../steps/Login'; import { CheckRCVPageDisplay, @@ -36,7 +36,6 @@ import { CheckScheduleButton, CheckRemoveButton, } from '../../../../steps/Meeting'; -import { SelectOptionFromDropDown } from '../../../../steps/Common'; // could not mock relogin in single case, will separate into 2 cases. @autorun(test.skip) diff --git a/packages/ringcentral-widgets-test/test/features/PresenceSettingSection/PresenceSettingSection.test.tsx b/packages/ringcentral-widgets-test/test/features/PresenceSettingSection/PresenceSettingSection.test.tsx index 38fa443f78..54a8a1b703 100644 --- a/packages/ringcentral-widgets-test/test/features/PresenceSettingSection/PresenceSettingSection.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/PresenceSettingSection/PresenceSettingSection.test.tsx @@ -10,6 +10,7 @@ import { Then, } from '@ringcentral-integration/test-utils'; import { PresenceSetting } from '@ringcentral-integration/widgets/test/components/PresenceSettingSection/PresenceSettingSection'; + import { Login } from '../../steps/Login'; const NavigateTo: StepFunction<{ path: string }> = ({ path }, { phone }) => { diff --git a/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/QuicklySchedule/Checkpoint/ErrorHandling/RCI-2432.test.tsx b/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/QuicklySchedule/Checkpoint/ErrorHandling/RCI-2432.test.tsx index b797a92fa6..f55431db9b 100644 --- a/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/QuicklySchedule/Checkpoint/ErrorHandling/RCI-2432.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/QuicklySchedule/Checkpoint/ErrorHandling/RCI-2432.test.tsx @@ -11,7 +11,6 @@ * Note: * Failed to add a meeting Some scenarios such as poor network connection */ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { p2, @@ -27,9 +26,9 @@ import { examples, } from '@ringcentral-integration/test-utils'; +import { CheckAlertToBeCallWith } from '../../../../../steps/Alert'; import { Login as CommonLogin } from '../../../../../steps/Login'; import { CheckRCVPageDisplay } from '../../../../../steps/Meeting'; -import { CheckAlertToBeCallWith } from '../../../../../steps/Alert'; @autorun(test.skip) @it diff --git a/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/QuicklySchedule/Checkpoint/RCI-2424.test.tsx b/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/QuicklySchedule/Checkpoint/RCI-2424.test.tsx index b3c3eb9d12..18943c02fb 100644 --- a/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/QuicklySchedule/Checkpoint/RCI-2424.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/QuicklySchedule/Checkpoint/RCI-2424.test.tsx @@ -9,29 +9,30 @@ * Outlook > Calendar >New Event >Click'RingCentral Scheduler' / 'Schedule with RingCentral' * Outlook > Calendar >Click 'New Appointment' or 'New Meeting' in the menu bar > Click {Brand name} for Outlook */ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { - p1, - it, - autorun, And, + Given, Scenario, Step, - Given, Then, - title, When, + autorun, + common, examples, + it, + p1, + title, } from '@ringcentral-integration/test-utils'; +import { CheckAlertToBeCallWith } from '../../../../steps/Alert'; import { Login as CommonLogin } from '../../../../steps/Login'; import { CheckRCVPageDisplay } from '../../../../steps/Meeting'; -import { CheckAlertToBeCallWith } from '../../../../steps/Alert'; @autorun(test.skip) @it @p1 +@common @title('Meeting information should be injected into Outlook after add meeting') export class RCI2424 extends Step { Login: StepFunction = CommonLogin; @@ -42,7 +43,7 @@ export class RCI2424 extends Step { | 'bt' | 'BT Cloud Work Video Meeting' | 'Cloud Work' | | 'telus' | 'TELUS Business Connect Video Meeting' | 'Business Connect' | | 'avaya' | 'Avaya Cloud Office Video Meeting' | 'Avaya Cloud Office' | - | 'att' | 'Office@Hand Meetings Meeting' | 'Office@Hand' | + | 'att' | 'AT&T Office@Hand Meetings Meeting' | 'AT&T Office@Hand' | `) run() { const { Login, CheckInjection } = this; diff --git a/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/QuicklySchedule/Checkpoint/RCI-3089.test.tsx b/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/QuicklySchedule/Checkpoint/RCI-3089.test.tsx index 07f9bbb350..86f1e8155f 100644 --- a/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/QuicklySchedule/Checkpoint/RCI-3089.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/QuicklySchedule/Checkpoint/RCI-3089.test.tsx @@ -8,7 +8,6 @@ * ntry point(/s):

Outlook > Calendar > Edit the event1> Click 'RingCentral Scheduler' / 'Schedule with RingCentral'

Outlook > Calendar > Edit the event1> Click 'New Appointment' or 'New Meeting' in the menu bar > Click {Brand name} for Outlook

*/ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { p1, diff --git a/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/Schedule/Checkpoint/RCI-1749.test.tsx b/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/Schedule/Checkpoint/RCI-1749.test.tsx index a82a37ec41..d8bf993319 100644 --- a/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/Schedule/Checkpoint/RCI-1749.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/Schedule/Checkpoint/RCI-1749.test.tsx @@ -8,7 +8,6 @@ * Outlook > Calendar > New Event >Click 'RingCentral Scheduler' * Outlook > Calendar > Click 'New Appointment' or 'New Meeting' in the menu bar */ - import { trackEvents } from '@ringcentral-integration/commons/enums/trackEvents'; import type { StepFunction } from '@ringcentral-integration/test-utils'; import { diff --git a/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/Schedule/RemoveMeeting/Checkpoint/RCI-2378.test.tsx b/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/Schedule/RemoveMeeting/Checkpoint/RCI-2378.test.tsx index f1e79af479..92bea10dbf 100644 --- a/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/Schedule/RemoveMeeting/Checkpoint/RCI-2378.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/Schedule/RemoveMeeting/Checkpoint/RCI-2378.test.tsx @@ -10,7 +10,7 @@ * Outlook > Calendar > Edit the event1>Click'RingCentral Scheduler' / 'Schedule with RingCentral' * Outlook > Calendar >Edit the event1>Click 'New Appointment' or 'New Meeting' in the menu bar > Click {Brand name} for Outlook */ - +import rcvMeetingSettingsBody from '@ringcentral-integration/commons/integration-test/mock/data/rcvMeetingSettings.json'; import type { StepFunction } from '@ringcentral-integration/test-utils'; import { p2, @@ -24,8 +24,8 @@ import { title, When, } from '@ringcentral-integration/test-utils'; -import rcvMeetingSettingsBody from '@ringcentral-integration/commons/integration-test/mock/data/rcvMeetingSettings.json'; +import { CheckAlertToBeCallWith } from '../../../../../steps/Alert'; import { Login as CommonLogin } from '../../../../../steps/Login'; import { CheckRCVPageDisplay, @@ -33,12 +33,10 @@ import { CheckScheduleButton, ClickRemoveButton, } from '../../../../../steps/Meeting'; - import { CheckRemoveMeetingModal, ClickConfirmInModal, } from '../../../../../steps/Modal'; -import { CheckAlertToBeCallWith } from '../../../../../steps/Alert'; @autorun(test.skip) @it diff --git a/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/Schedule/RemoveMeeting/Checkpoint/RCI-3527.test.tsx b/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/Schedule/RemoveMeeting/Checkpoint/RCI-3527.test.tsx index ae5c6ed76e..bd5d40e717 100644 --- a/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/Schedule/RemoveMeeting/Checkpoint/RCI-3527.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/Schedule/RemoveMeeting/Checkpoint/RCI-3527.test.tsx @@ -10,7 +10,6 @@ * Outlook > Calendar > Edit the event1>Click'RingCentral Scheduler' / 'Schedule with RingCentral' * Outlook > Calendar >Edit the event1>Click 'New Appointment' or 'New Meeting' in the menu bar > Click {Brand name} for Outlook */ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { p2, diff --git a/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/Schedule/UpdateMeeting/RCI-1858.test.tsx b/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/Schedule/UpdateMeeting/RCI-1858.test.tsx index e8b1b437d9..a4a29ecb87 100644 --- a/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/Schedule/UpdateMeeting/RCI-1858.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/Schedule/UpdateMeeting/RCI-1858.test.tsx @@ -8,7 +8,6 @@ * Windows Outlook * Mac Outlook */ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { And, diff --git a/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/Schedule/UpdateMeeting/RCI-2367.test.tsx b/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/Schedule/UpdateMeeting/RCI-2367.test.tsx index 79a9a6bb8e..f3acd64748 100644 --- a/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/Schedule/UpdateMeeting/RCI-2367.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/RingCentralOfficeAddIn/Schedule/UpdateMeeting/RCI-2367.test.tsx @@ -8,7 +8,6 @@ * Entry point(/s): * Login to Outlook: Calendar -> New Event > RingCentral Scheduler App */ - import type { StepFunction } from '@ringcentral-integration/test-utils'; import { p2, @@ -22,6 +21,7 @@ import { title, When, } from '@ringcentral-integration/test-utils'; + import { Login as CommonLogin } from '../../../../steps/Login'; import { CheckboxIsChecked, diff --git a/packages/ringcentral-widgets-test/test/features/Transfer/CheckTransferCall.tsx b/packages/ringcentral-widgets-test/test/features/Transfer/CheckTransferCall.tsx index 15216d1878..a21985bb90 100644 --- a/packages/ringcentral-widgets-test/test/features/Transfer/CheckTransferCall.tsx +++ b/packages/ringcentral-widgets-test/test/features/Transfer/CheckTransferCall.tsx @@ -13,7 +13,6 @@ * 2. Make an inbound/outbound call, and go to 'All Calls' page * 3. Make an inbound/outbound call > back to 'All Calls' page > click the left section on the call item */ - import { p2, it, @@ -25,6 +24,7 @@ import { title, When, } from '@ringcentral-integration/test-utils'; + import type { StepFunction } from '../../lib/step'; import { AnswerCall, diff --git a/packages/ringcentral-widgets-test/test/features/WebPhone/Webphoneregistration/RCI-831.test.tsx b/packages/ringcentral-widgets-test/test/features/WebPhone/Webphoneregistration/RCI-831.test.tsx index 199adebf83..55d317fe25 100644 --- a/packages/ringcentral-widgets-test/test/features/WebPhone/Webphoneregistration/RCI-831.test.tsx +++ b/packages/ringcentral-widgets-test/test/features/WebPhone/Webphoneregistration/RCI-831.test.tsx @@ -10,7 +10,6 @@ * Note(/s): * Calling Settings:Settings -> Calling -> Make my calls with */ - import { connectionStatus } from '@ringcentral-integration/commons/modules/Webphone'; import type { Context } from '../../../interfaces'; @@ -26,13 +25,13 @@ import { title, When, } from '../../../lib/step'; +import { Login } from '../../../steps/Login'; import { CheckCallButtonActive, CheckCallButtonDisabled, CheckDialerNotShowSpinner, CheckDialerShowSpinner, } from '../../../steps/dialer'; -import { Login } from '../../../steps/Login'; @autorun(test) @common diff --git a/packages/ringcentral-widgets-test/test/steps/AddCall/checks/CheckAddCallPageDisplay.tsx b/packages/ringcentral-widgets-test/test/steps/AddCall/checks/CheckAddCallPageDisplay.tsx new file mode 100644 index 0000000000..e334f4279b --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/AddCall/checks/CheckAddCallPageDisplay.tsx @@ -0,0 +1,14 @@ +import { screen, waitFor, getByTestId } from '@testing-library/react'; + +import type { StepFunction } from '../../../lib/step'; + +export const CheckAddCallPageDisplay: StepFunction = async () => { + await waitFor(() => { + const container = screen.getByTestId('addCallPage'); + expect(getByTestId(container, 'backButton')).toBeInTheDocument(); + expect(screen.getByText('New call')).toBeInTheDocument(); + expect(getByTestId(container, 'recipientsInput')).toBeInTheDocument(); + expect(getByTestId(container, 'dialPad')).toBeInTheDocument(); + expect(getByTestId(container, 'callButton')).toBeInTheDocument(); + }); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/AddCall/checks/CheckIsAddCallPage.tsx b/packages/ringcentral-widgets-test/test/steps/AddCall/checks/CheckIsAddCallPage.tsx new file mode 100644 index 0000000000..8d1294378d --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/AddCall/checks/CheckIsAddCallPage.tsx @@ -0,0 +1,20 @@ +import { screen, waitFor } from '@testing-library/react'; + +import type { StepFunction } from '../../../lib/step'; + +interface CheckIsAddCallPageParams { + inPage?: boolean; +} + +export const CheckIsAddCallPage: StepFunction< + CheckIsAddCallPageParams +> = async ({ inPage }) => { + await waitFor(() => { + const container = screen.queryByTestId('addCallPage'); + if (inPage) { + expect(container).toBeInTheDocument(); + } else { + expect(container).not.toBeTruthy(); + } + }); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/AddCall/checks/index.ts b/packages/ringcentral-widgets-test/test/steps/AddCall/checks/index.ts new file mode 100644 index 0000000000..aaa29fa2fe --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/AddCall/checks/index.ts @@ -0,0 +1,2 @@ +export * from './CheckAddCallPageDisplay'; +export * from './CheckIsAddCallPage'; diff --git a/packages/ringcentral-widgets-test/test/steps/AddCall/index.ts b/packages/ringcentral-widgets-test/test/steps/AddCall/index.ts new file mode 100644 index 0000000000..bf12f3a45b --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/AddCall/index.ts @@ -0,0 +1 @@ +export * from './checks'; diff --git a/packages/ringcentral-widgets-test/test/steps/AddressBook/actions/TriggerAddressBookSync.tsx b/packages/ringcentral-widgets-test/test/steps/AddressBook/actions/TriggerAddressBookSync.tsx new file mode 100644 index 0000000000..333a6a9f9b --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/AddressBook/actions/TriggerAddressBookSync.tsx @@ -0,0 +1,14 @@ +import type { Context } from '../../../interfaces'; +import type { StepFunction } from '../../../lib/step'; + +/** + * Trigger syncing data of AddressBook + */ +export const TriggerAddressBookSync: StepFunction = async ( + _: unknown, + context: Context, +) => { + await context.phone.dataFetcherV2.fetchData( + context.phone.addressBook._source, + ); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/AddressBook/actions/index.ts b/packages/ringcentral-widgets-test/test/steps/AddressBook/actions/index.ts new file mode 100644 index 0000000000..bb4e72d722 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/AddressBook/actions/index.ts @@ -0,0 +1 @@ +export * from './TriggerAddressBookSync'; diff --git a/packages/ringcentral-widgets-test/test/steps/AddressBook/checks/CheckAddressBookData.tsx b/packages/ringcentral-widgets-test/test/steps/AddressBook/checks/CheckAddressBookData.tsx new file mode 100644 index 0000000000..1b03fe7ceb --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/AddressBook/checks/CheckAddressBookData.tsx @@ -0,0 +1,18 @@ +import { waitUntilTo } from '@ringcentral-integration/commons/utils'; + +import type { StepFunction } from '../../../lib/step'; + +interface MockAddressBookSyncProps { + page?: number; +} + +export const CheckAddressBookData: StepFunction = async ( + props, + { phone, payload }, +) => { + await waitUntilTo(() => { + expect( + phone.addressBook.rawContacts.map(({ id }: { id: string }) => id), + ).toEqual(payload.records); + }); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/AddressBook/checks/index.ts b/packages/ringcentral-widgets-test/test/steps/AddressBook/checks/index.ts new file mode 100644 index 0000000000..a17aa1dee2 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/AddressBook/checks/index.ts @@ -0,0 +1 @@ +export * from './CheckAddressBookData'; diff --git a/packages/ringcentral-widgets-test/test/steps/AddressBook/index.ts b/packages/ringcentral-widgets-test/test/steps/AddressBook/index.ts new file mode 100644 index 0000000000..2b782a6470 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/AddressBook/index.ts @@ -0,0 +1,3 @@ +export * from './actions'; +export * from './checks'; +export * from './mocks'; diff --git a/packages/ringcentral-widgets-test/test/steps/AddressBook/mocks/MockAddressBookSync.tsx b/packages/ringcentral-widgets-test/test/steps/AddressBook/mocks/MockAddressBookSync.tsx new file mode 100644 index 0000000000..fc68745871 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/AddressBook/mocks/MockAddressBookSync.tsx @@ -0,0 +1,51 @@ +import type { PersonalContactResource } from '@ringcentral-integration/mock'; +import type { ArraySchemaObject } from '@ringcentral-integration/mock/src/interface'; +import type { StepFunction } from '@ringcentral-integration/test-utils'; + +interface MockAddressBookSyncProps { + handler?: ( + personalUsers: PersonalContactResource[], + ) => PersonalContactResource[]; + page?: number; +} + +export const MockAddressBookSync: StepFunction = ( + { page = 2, handler }, + { rcMock, payload }, +) => { + const mock = () => { + payload.records = []; + rcMock.get( + '/restapi/v1.0/account/:accountId/extension/:extensionId/address-book-sync', + 200, + { + repeat: page, + schema: (schema) => { + ( + schema.mockData.properties.records as ArraySchemaObject + ).items.properties.availability.examples = ['Alive']; + return schema; + }, + response: ({ params, mockData }) => { + mockData.syncInfo.syncTime = new Date(Date.now()).toISOString(); + mockData.syncInfo.syncType = page ? 'ISync' : 'FSync'; + page--; + mockData.nextPageId = page; + mockData.records.forEach((item) => { + item.id = Math.random(); + }); + mockData.records = handler?.(mockData.records) ?? mockData.records; + payload.records.push(...mockData.records.map((item) => item.id)); + return { body: mockData }; + }, + }, + ); + }; + + if (rcMock.initialized) { + mock(); + } else { + rcMock.defaultInitMocks.delete(rcMock.getAddressBookSync); + rcMock.defaultInitMocks.add(mock); + } +}; diff --git a/packages/ringcentral-widgets-test/test/steps/AddressBook/mocks/MockAddressBookSyncFail.tsx b/packages/ringcentral-widgets-test/test/steps/AddressBook/mocks/MockAddressBookSyncFail.tsx new file mode 100644 index 0000000000..994387990a --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/AddressBook/mocks/MockAddressBookSyncFail.tsx @@ -0,0 +1,36 @@ +import type { StepFunction } from '@ringcentral-integration/test-utils'; + +export interface ResponseErrorInfo { + errorCode: string; + message: string; + additionalInfo?: string; +} + +export interface MockAddressBookSyncFailProps { + handler: () => { errors: ResponseErrorInfo[] }; + status: number; + repeat?: number; +} + +export const MockAddressBookSyncFail: StepFunction< + MockAddressBookSyncFailProps +> = ({ handler, status, repeat }, { rcMock, payload }) => { + const mock = () => { + rcMock.get( + '/restapi/v1.0/account/:accountId/extension/:extensionId/address-book-sync', + status as any, + { + repeat, + response: () => { + return { body: handler() }; + }, + }, + ); + }; + if (rcMock.initialized) { + mock(); + } else { + rcMock.defaultInitMocks.delete(rcMock.getAddressBookSync); + rcMock.defaultInitMocks.add(mock); + } +}; diff --git a/packages/ringcentral-widgets-test/test/steps/AddressBook/mocks/index.ts b/packages/ringcentral-widgets-test/test/steps/AddressBook/mocks/index.ts new file mode 100644 index 0000000000..cd955ae510 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/AddressBook/mocks/index.ts @@ -0,0 +1,2 @@ +export * from './MockAddressBookSync'; +export * from './MockAddressBookSyncFail'; diff --git a/packages/ringcentral-widgets-test/test/steps/Alert/actions/CloseAlertMessage.tsx b/packages/ringcentral-widgets-test/test/steps/Alert/actions/CloseAlertMessage.tsx index 8fe31f3b1c..87634e7078 100644 --- a/packages/ringcentral-widgets-test/test/steps/Alert/actions/CloseAlertMessage.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Alert/actions/CloseAlertMessage.tsx @@ -6,6 +6,7 @@ import { queryByTestId, prettyDOM, } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const CloseAlertMessage: StepFunction<{ diff --git a/packages/ringcentral-widgets-test/test/steps/Alert/checks/CheckAlertAutoDismiss.tsx b/packages/ringcentral-widgets-test/test/steps/Alert/checks/CheckAlertAutoDismiss.tsx index a419b4f05d..5290f6234b 100644 --- a/packages/ringcentral-widgets-test/test/steps/Alert/checks/CheckAlertAutoDismiss.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Alert/checks/CheckAlertAutoDismiss.tsx @@ -3,6 +3,7 @@ import { waitFor, waitForElementToBeRemoved, } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; // TODO: find better implementation diff --git a/packages/ringcentral-widgets-test/test/steps/Alert/checks/CheckAlertMessage.tsx b/packages/ringcentral-widgets-test/test/steps/Alert/checks/CheckAlertMessage.tsx index c015bc330d..2dc64b2b8f 100644 --- a/packages/ringcentral-widgets-test/test/steps/Alert/checks/CheckAlertMessage.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Alert/checks/CheckAlertMessage.tsx @@ -1,31 +1,29 @@ -import { waitFor, screen } from '@testing-library/react'; +import { whenStateOrTimerChange } from '@ringcentral-integration/core/test'; +import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const CheckAlertMessage: StepFunction<{ dataSign?: string; message: string; }> = async ({ dataSign = 'alert', message }) => { - await waitFor( - () => { - const alert = screen.queryByTestId(dataSign); - expect(alert).toBeInTheDocument(); - expect(alert).toHaveTextContent(message); - }, - { timeout: 3000 }, - ); + await whenStateOrTimerChange(() => { + const [alert] = screen + .queryAllByTestId(dataSign) + .filter((i) => i.textContent === message); + expect(alert).toBeInTheDocument(); + expect(alert).toHaveTextContent(message); + }); }; export const CheckContainsAlertMessage: StepFunction<{ message: string; }> = async ({ message }) => { - await waitFor( - () => { - const alerts = screen.queryAllByTestId('alert'); - const myAlert = alerts.find((alert) => - alert.textContent?.includes(message), - ); - expect(myAlert).toHaveTextContent(message); - }, - { timeout: 3000 }, - ); + await whenStateOrTimerChange(() => { + const alerts = screen.queryAllByTestId('alert'); + const myAlert = alerts.find((alert) => + alert.textContent?.includes(message), + ); + expect(myAlert).toHaveTextContent(message); + }); }; diff --git a/packages/ringcentral-widgets-test/test/steps/Alert/checks/CheckAlertToBeCallWith.tsx b/packages/ringcentral-widgets-test/test/steps/Alert/checks/CheckAlertToBeCallWith.tsx index 22f519d175..735f0411f7 100644 --- a/packages/ringcentral-widgets-test/test/steps/Alert/checks/CheckAlertToBeCallWith.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Alert/checks/CheckAlertToBeCallWith.tsx @@ -1,8 +1,8 @@ import type { - Options, AlertLevelType, + Options, } from '@ringcentral-integration/commons/modules/Alert'; -import { waitForRenderReady } from '@ringcentral-integration/test-utils'; +import { whenStateOrTimerChange } from '@ringcentral-integration/core/test'; import type { StepFunction } from '../../../lib/step'; @@ -13,10 +13,11 @@ export const CheckAlertToBeCallWith: StepFunction = async ( const { children, level, ...data } = props; jest.useFakeTimers(); jest.advanceTimersByTime(500); - await waitForRenderReady(); jest.useRealTimers(); - expect(phone.alert[level as AlertLevelType]).toBeCalledWith( - expect.objectContaining(data), - ); + await whenStateOrTimerChange(() => { + expect(phone.alert[level as AlertLevelType]).toHaveBeenCalledWith( + expect.objectContaining(data), + ); + }); }; diff --git a/packages/ringcentral-widgets-test/test/steps/Alert/checks/CheckJapanAlertMessage.tsx b/packages/ringcentral-widgets-test/test/steps/Alert/checks/CheckJapanAlertMessage.tsx index c78ba9553c..dbd4111596 100644 --- a/packages/ringcentral-widgets-test/test/steps/Alert/checks/CheckJapanAlertMessage.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Alert/checks/CheckJapanAlertMessage.tsx @@ -1,16 +1,18 @@ import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; interface CheckJapanAlertMessageProps { show: boolean; } -export const CheckJapanAlertMessage: StepFunction = - async ({ show }) => { - const alerts = screen.queryAllByTestId('alert'); - const hasJapanAlert = !!alerts.find( - (item) => - item.textContent === 'Emergency service is not available in Japan.', - ); - expect(hasJapanAlert).toBe(show); - }; +export const CheckJapanAlertMessage: StepFunction< + CheckJapanAlertMessageProps +> = async ({ show }) => { + const alerts = screen.queryAllByTestId('alert'); + const hasJapanAlert = !!alerts.find( + (item) => + item.textContent === 'Emergency service is not available in Japan.', + ); + expect(hasJapanAlert).toBe(show); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Alert/checks/CheckNoAnyAlerts.tsx b/packages/ringcentral-widgets-test/test/steps/Alert/checks/CheckNoAnyAlerts.tsx index d528e5c46f..0b2dccff93 100644 --- a/packages/ringcentral-widgets-test/test/steps/Alert/checks/CheckNoAnyAlerts.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Alert/checks/CheckNoAnyAlerts.tsx @@ -1,4 +1,5 @@ import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const CheckNoAnyAlerts: StepFunction = () => { diff --git a/packages/ringcentral-widgets-test/test/steps/Alert/checks/CheckToastMessage.tsx b/packages/ringcentral-widgets-test/test/steps/Alert/checks/CheckToastMessage.tsx new file mode 100644 index 0000000000..0c3bc3d64a --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Alert/checks/CheckToastMessage.tsx @@ -0,0 +1,35 @@ +import { whenStateOrTimerChange } from '@ringcentral-integration/core/test'; +import { screen } from '@testing-library/react'; + +import type { StepFunction } from '../../../lib/step'; + +export const CheckToastMessage: StepFunction<{ + message: string; + noExist?: boolean; +}> = async ({ message, noExist }) => { + if (noExist) { + await whenStateOrTimerChange(() => { + const alert = screen.queryByText(message); + expect(alert).not.toBeInTheDocument(); + }); + return; + } + + await whenStateOrTimerChange(() => { + const alert = screen.queryByTestId('notification'); + expect(alert).toBeInTheDocument(); + expect(alert).toHaveTextContent(message); + }); +}; + +export const CheckContainsToastMessage: StepFunction<{ + message: string; +}> = async ({ message }) => { + await whenStateOrTimerChange(() => { + const alerts = screen.queryAllByTestId('notification'); + const myToast = alerts.find((alert) => + alert.textContent?.includes(message), + ); + expect(myToast).toHaveTextContent(message); + }); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Alert/checks/index.ts b/packages/ringcentral-widgets-test/test/steps/Alert/checks/index.ts index 9eaf9f78dc..00ddae7700 100644 --- a/packages/ringcentral-widgets-test/test/steps/Alert/checks/index.ts +++ b/packages/ringcentral-widgets-test/test/steps/Alert/checks/index.ts @@ -4,3 +4,4 @@ export * from './CheckJapanAlertMessage'; export * from './CheckAlertAutoDismiss'; export * from './CheckAlertNoAutoDismiss'; export * from './CheckAlertToBeCallWith'; +export * from './CheckToastMessage'; diff --git a/packages/ringcentral-widgets-test/test/steps/Alert/mocks/SpyOnAlert.tsx b/packages/ringcentral-widgets-test/test/steps/Alert/mocks/SpyOnAlert.tsx index 1dd0085dc3..e563ca4c85 100644 --- a/packages/ringcentral-widgets-test/test/steps/Alert/mocks/SpyOnAlert.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Alert/mocks/SpyOnAlert.tsx @@ -31,22 +31,23 @@ export interface SpyOnAlertWithMessagesProps { /** * spy on alert method, make alert can inject some other method before or after trigger */ -export const SpyOnAlertWithMessages: StepFunction = - async ({ messages, onShowMessage }) => { - const originalAlert = Alert.prototype.alert; +export const SpyOnAlertWithMessages: StepFunction< + SpyOnAlertWithMessagesProps +> = async ({ messages, onShowMessage }) => { + const originalAlert = Alert.prototype.alert; - spyOnAlert = jest - .spyOn(Alert.prototype, 'alert') - // eslint-disable-next-line func-names - .mockImplementation(function (this: Alert, option) { - const { message } = option; + spyOnAlert = jest + .spyOn(Alert.prototype, 'alert') + // eslint-disable-next-line func-names + .mockImplementation(function (this: Alert, option) { + const { message } = option; - const exec = () => originalAlert.call(this, option); + const exec = () => originalAlert.call(this, option); - if (messages.includes(message)) { - return onShowMessage(option, exec); - } + if (messages.includes(message)) { + return onShowMessage(option, exec); + } - return exec(); - }); - }; + return exec(); + }); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/AudioSettings/actions/SetVolumeSlider.tsx b/packages/ringcentral-widgets-test/test/steps/AudioSettings/actions/SetVolumeSlider.tsx new file mode 100644 index 0000000000..5e5fca5d1f --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/AudioSettings/actions/SetVolumeSlider.tsx @@ -0,0 +1,37 @@ +import { StepFunction, userEvent } from '@ringcentral-integration/test-utils'; +import { screen, within } from '@testing-library/react'; + +export const SetVolumeSlider: StepFunction<{ + value: string; + containerDataSign: string; +}> = async ({ value, containerDataSign }) => { + const sliderRoot = within(screen.getByTestId(containerDataSign)).getByTestId( + 'slider', + ); + const volumeSlider = within(sliderRoot).getByRole('slider'); + + expect(volumeSlider).toBeInTheDocument(); + + sliderRoot.getBoundingClientRect = jest.fn(() => { + return { + width: 100, + height: 10, + bottom: 10, + left: 0, + x: 0, + y: 0, + right: 0, + top: 0, + toJSON() {}, + }; + }); + + // slider: |----|----|----|----|----|----|----|----|----|----| + // values: 0 10 20 30 40 50 60 70 80 90 100 + // value: ↑ + // mouse: ↑ + + // Clicking the volume slider to the ${value}% range + userEvent.click(sliderRoot, { clientX: Number(value) }); + expect(volumeSlider.getAttribute('aria-valuenow')).toBe(value); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/AudioSettings/actions/index.ts b/packages/ringcentral-widgets-test/test/steps/AudioSettings/actions/index.ts new file mode 100644 index 0000000000..96c94517f4 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/AudioSettings/actions/index.ts @@ -0,0 +1 @@ +export * from './SetVolumeSlider'; diff --git a/packages/ringcentral-widgets-test/test/steps/AudioSettings/checks/CheckInputSection.tsx b/packages/ringcentral-widgets-test/test/steps/AudioSettings/checks/CheckInputSection.tsx new file mode 100644 index 0000000000..a4d8a1bd99 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/AudioSettings/checks/CheckInputSection.tsx @@ -0,0 +1,28 @@ +import { screen, within } from '@testing-library/react'; + +import type { StepFunction } from '../../../lib/step'; + +export const CheckInputSection: StepFunction<{ + selectedItem?: string; + isEnabled: boolean; +}> = async ({ isEnabled, selectedItem }) => { + const inputDeviceSection = screen.getByTestId('inputDeviceSection'); + expect(within(inputDeviceSection).getByText('Input')).toBeInTheDocument(); + expect( + within(inputDeviceSection).getByText('Microphone'), + ).toBeInTheDocument(); + + const inputSourceDropdown = screen.getByTestId('microphoneDeviceSelect'); + + if (isEnabled) { + expect(inputSourceDropdown).not.toHaveClass('Mui-disabled'); + } else { + expect(inputSourceDropdown).toHaveClass('Mui-disabled'); + } + + if (selectedItem) { + expect( + within(inputSourceDropdown).getByText(selectedItem), + ).toBeInTheDocument(); + } +}; diff --git a/packages/ringcentral-widgets-test/test/steps/AudioSettings/checks/CheckOutputSection.tsx b/packages/ringcentral-widgets-test/test/steps/AudioSettings/checks/CheckOutputSection.tsx new file mode 100644 index 0000000000..a6edb503f2 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/AudioSettings/checks/CheckOutputSection.tsx @@ -0,0 +1,26 @@ +import { screen, within } from '@testing-library/react'; + +import type { StepFunction } from '../../../lib/step'; + +export const CheckOutputSection: StepFunction<{ + selectedItem: string; + isEnabled: boolean; +}> = async ({ isEnabled, selectedItem }) => { + const outputDeviceSection = screen.getByTestId('outputDeviceSection'); + expect(within(outputDeviceSection).getByText('Output')).toBeInTheDocument(); + expect( + within(outputDeviceSection).getByText('Speaker source'), + ).toBeInTheDocument(); + + const outputSourceDropdown = screen.getByTestId('speakerDeviceSelect'); + + if (isEnabled) { + expect(outputSourceDropdown).not.toHaveClass('Mui-disabled'); + } else { + expect(outputSourceDropdown).toHaveClass('Mui-disabled'); + } + + expect( + within(outputSourceDropdown).getByText(selectedItem), + ).toBeInTheDocument(); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/AudioSettings/checks/CheckRingtoneSection.tsx b/packages/ringcentral-widgets-test/test/steps/AudioSettings/checks/CheckRingtoneSection.tsx new file mode 100644 index 0000000000..e8e36c438d --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/AudioSettings/checks/CheckRingtoneSection.tsx @@ -0,0 +1,23 @@ +import { screen, within } from '@testing-library/react'; + +import type { StepFunction } from '../../../lib/step'; + +export const CheckRingtoneSection: StepFunction<{ + selectedItem: string; + isEnabled: boolean; +}> = async ({ isEnabled, selectedItem }) => { + const outputDeviceSection = screen.getByTestId('outputDeviceSection'); + expect( + within(outputDeviceSection).getByText('Ringtone and notification source'), + ).toBeInTheDocument(); + + const dropdown = screen.getByTestId('ringtoneDeviceSelect'); + + if (isEnabled) { + expect(dropdown).not.toHaveClass('Mui-disabled'); + } else { + expect(dropdown).toHaveClass('Mui-disabled'); + } + + expect(within(dropdown).getByText(selectedItem)).toBeInTheDocument(); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/AudioSettings/checks/CheckVolumeSlider.tsx b/packages/ringcentral-widgets-test/test/steps/AudioSettings/checks/CheckVolumeSlider.tsx new file mode 100644 index 0000000000..5ffb720ae9 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/AudioSettings/checks/CheckVolumeSlider.tsx @@ -0,0 +1,19 @@ +import { screen, within } from '@testing-library/react'; + +import type { StepFunction } from '../../../lib/step'; + +export const CheckVolumeSlider: StepFunction<{ + value: string; + label?: string; + dataSign: string; +}> = async ({ value, label, dataSign }) => { + const sliderSection = screen.getByTestId(dataSign); + const volumeSlider = within(sliderSection).getByRole('slider'); + expect(volumeSlider).toBeInTheDocument(); + + if (label) { + expect(within(sliderSection).getByTestId('label')).toHaveTextContent(label); + } + + expect(volumeSlider.getAttribute('aria-valuenow')).toBe(value); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/AudioSettings/checks/index.ts b/packages/ringcentral-widgets-test/test/steps/AudioSettings/checks/index.ts new file mode 100644 index 0000000000..d4083c3467 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/AudioSettings/checks/index.ts @@ -0,0 +1,4 @@ +export * from './CheckInputSection'; +export * from './CheckOutputSection'; +export * from './CheckRingtoneSection'; +export * from './CheckVolumeSlider'; diff --git a/packages/ringcentral-widgets-test/test/steps/AudioSettings/index.ts b/packages/ringcentral-widgets-test/test/steps/AudioSettings/index.ts new file mode 100644 index 0000000000..e6ffa57916 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/AudioSettings/index.ts @@ -0,0 +1,2 @@ +export * from './actions'; +export * from './checks'; diff --git a/packages/ringcentral-widgets-test/test/steps/Badge/actions/CheckLimitedModeBadge.tsx b/packages/ringcentral-widgets-test/test/steps/Badge/actions/CheckLimitedModeBadge.tsx new file mode 100644 index 0000000000..fda7b64dbe --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Badge/actions/CheckLimitedModeBadge.tsx @@ -0,0 +1,14 @@ +import { screen, waitFor } from '@testing-library/react'; + +import type { StepFunction } from '../../../lib/step'; + +export const CheckLimitedModeBadge: StepFunction<{ + show?: boolean; +}> = async ({ show = true }) => { + const badge = screen.queryByText('Limited Mode'); + if (show) { + expect(badge).toBeInTheDocument(); + } else { + expect(badge).not.toBeInTheDocument(); + } +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Badge/actions/CheckVoipOnlyBadge.tsx b/packages/ringcentral-widgets-test/test/steps/Badge/actions/CheckVoipOnlyBadge.tsx index da9cba2dd5..4756636f1e 100644 --- a/packages/ringcentral-widgets-test/test/steps/Badge/actions/CheckVoipOnlyBadge.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Badge/actions/CheckVoipOnlyBadge.tsx @@ -1,32 +1,15 @@ -import { - screen, - waitFor, -} from '@testing-library/react'; import { waitForRenderReady } from '@ringcentral-integration/test-utils'; +import { screen, waitFor } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const CheckVoipOnlyBadge: StepFunction<{ show?: boolean; }> = async ({ show = true }) => { - jest.useFakeTimers(); - jest.advanceTimersByTime(1000); - await waitForRenderReady(); - jest.useRealTimers(); + const badge = screen.queryByText('VoIP Only'); if (show) { - await waitFor( - () => { - const badge = screen.queryByText('VoIP Only'); - expect(badge).toBeInTheDocument(); - }, - { timeout: 3000 }, - ); + expect(badge).toBeInTheDocument(); } else { - await waitFor( - () => { - const badge = screen.queryByText('VoIP Only'); - expect(badge).not.toBeInTheDocument(); - }, - { timeout: 3000 }, - ); + expect(badge).not.toBeInTheDocument(); } }; diff --git a/packages/ringcentral-widgets-test/test/steps/Badge/checks/CheckGetStatusApi.tsx b/packages/ringcentral-widgets-test/test/steps/Badge/checks/CheckGetStatusApi.tsx new file mode 100644 index 0000000000..77ad4e5bb2 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Badge/checks/CheckGetStatusApi.tsx @@ -0,0 +1,11 @@ +import type { StepFunction } from '../../../lib/step'; + +export const CheckGetStatusApi: StepFunction<{ + length: number; +}> = async ({ length }, { rcMock }) => { + const getStatusApi = rcMock.fetchMock.calls( + new RegExp(`.*/restapi/v1.0/status`), + { method: 'get' }, + ); + expect(getStatusApi.length).toBe(length); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/AnswerAndHoldCall.tsx b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/AnswerAndHoldCall.tsx new file mode 100644 index 0000000000..c05077aa25 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/AnswerAndHoldCall.tsx @@ -0,0 +1,10 @@ +import type { StepFunction } from '../../../../lib/step'; + +import { CallButtonBehavior } from './CallButtonBehavior'; + +export const AnswerAndHoldCall: StepFunction = async (_, context) => { + await CallButtonBehavior( + { callButtonBehaviorType: 'answerAndHold' }, + context, + ); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/AnswerCall.tsx b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/AnswerCall.tsx index 18f0c42afc..19635b0f90 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/AnswerCall.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/AnswerCall.tsx @@ -1,4 +1,5 @@ import type { StepFunction } from '../../../../lib/step'; + import { CallButtonBehavior } from './CallButtonBehavior'; export const AnswerCall: StepFunction = async (props, context) => { diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/AnswerCallAtOtherDevice.tsx b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/AnswerCallAtOtherDevice.tsx index 39991fea07..842b61c408 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/AnswerCallAtOtherDevice.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/AnswerCallAtOtherDevice.tsx @@ -1,5 +1,7 @@ import type { ICurrentDeviceCallsMap } from '@ringcentral-integration/commons/modules/ActiveCallControl'; + import type { StepFunction } from '../../../../lib/step'; + import { CallButtonBehavior } from './CallButtonBehavior'; export const AnswerCallAtOtherDevice: StepFunction = async (props, context) => { diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/CheckConferenceCallControl.ts b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/CheckConferenceCallControl.ts index 59536fa598..8e388976c5 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/CheckConferenceCallControl.ts +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/CheckConferenceCallControl.ts @@ -1,4 +1,5 @@ import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../../lib/step'; interface OperationProps { @@ -14,16 +15,17 @@ function getPhoneNumberRegExp(parsedNumber: string) { ); } -export const CheckConferenceCallControlPage: StepFunction = - async ({ parsedNumber, name }) => { - expect(screen.getByTestId('activeCallPanel')).toBeInTheDocument(); - if (parsedNumber) { - const numberRegEx = getPhoneNumberRegExp(parsedNumber); - expect(screen.getByTestId('activeCalleeName')).toHaveTextContent( - numberRegEx, - ); - } - if (name) { - expect(screen.getByTestId('activeCalleeName')).toHaveTextContent(name); - } - }; +export const CheckConferenceCallControlPage: StepFunction< + OperationProps +> = async ({ parsedNumber, name }) => { + expect(screen.getByTestId('activeCallPanel')).toBeInTheDocument(); + if (parsedNumber) { + const numberRegEx = getPhoneNumberRegExp(parsedNumber); + expect(screen.getByTestId('activeCalleeName')).toHaveTextContent( + numberRegEx, + ); + } + if (name) { + expect(screen.getByTestId('activeCalleeName')).toHaveTextContent(name); + } +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ClickAddCall.ts b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ClickAddCall.ts index 42b5bb00b0..a14f3ca302 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ClickAddCall.ts +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ClickAddCall.ts @@ -1,5 +1,6 @@ import { waitUntilTo } from '@ringcentral-integration/utils'; import { fireEvent, screen } from '@testing-library/react'; + import type { StepFunction } from '../../../../lib/step'; export const ClickAddButton: StepFunction = async () => { diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ClickCircleButton.tsx b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ClickCircleButton.tsx new file mode 100644 index 0000000000..55558372cc --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ClickCircleButton.tsx @@ -0,0 +1,9 @@ +import type { StepFunction } from '@ringcentral-integration/test-utils'; +import { fireEvent, screen } from '@testing-library/react'; + +export const ClickCircleButton: StepFunction<{ + dataSign: string; +}> = ({ dataSign }) => { + const muteBtn = screen.getByTestId(dataSign).querySelector('circle'); + fireEvent.click(muteBtn!); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ClickMoreButton.tsx b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ClickMoreButton.tsx index 28db9d89b1..4bf557690a 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ClickMoreButton.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ClickMoreButton.tsx @@ -2,11 +2,9 @@ import { fireEvent, screen } from '@testing-library/react'; import type { StepFunction } from '../../../../lib/step'; -const ClickMoreButton: StepFunction = async () => { +export const ClickMoreButton: StepFunction = async (_, context) => { const selector = document.querySelector('[data-sign="more"] circle'); expect(selector).not.toBeNull(); - fireEvent.click(selector); + fireEvent.click(selector!); }; - -export { ClickMoreButton }; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ClickMuteButton.tsx b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ClickMuteButton.tsx index 5a1fefe5ca..f7f28ce049 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ClickMuteButton.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ClickMuteButton.tsx @@ -1,4 +1,5 @@ import { fireEvent, screen, getByTestId } from '@testing-library/react'; + import type { StepFunction } from '../../../../lib/step'; export const ClickMuteButton: StepFunction = () => { diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ClickToCurrentCallControlPage.ts b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ClickToCurrentCallControlPage.ts index 8e5df73014..ce7036b6ee 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ClickToCurrentCallControlPage.ts +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ClickToCurrentCallControlPage.ts @@ -1,4 +1,5 @@ import { fireEvent, screen } from '@testing-library/react'; + import type { StepFunction } from '../../../../lib/step'; export const ClickToCurrentCallControlPage: StepFunction = () => { diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ClickUnHoldButton.tsx b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ClickUnHoldButton.tsx index 317b121b1e..0576e1ebcf 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ClickUnHoldButton.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ClickUnHoldButton.tsx @@ -1,4 +1,5 @@ import { fireEvent, screen } from '@testing-library/react'; + import type { StepFunction } from '../../../../lib/step'; export const ClickUnHoldButton: StepFunction<{ mock: boolean }> = () => { diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/CustomForwardCall.tsx b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/CustomForwardCall.tsx index a56936c59c..ecdc8e9c33 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/CustomForwardCall.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/CustomForwardCall.tsx @@ -1,11 +1,15 @@ import { waitUntilTo } from '@ringcentral-integration/commons/utils'; import { fireEvent, screen } from '@testing-library/react'; + import type { StepFunction } from '../../../../lib/step'; + import { CallButtonBehavior } from './CallButtonBehavior'; -export const CustomForwardCall: StepFunction = async (props, context) => { +export const CustomForwardCall: StepFunction<{ + customSelector?: string; +}> = async ({ customSelector = 'custom' }, context) => { await CallButtonBehavior({ callButtonBehaviorType: 'forward' }, context); - const customEle = screen.getByTestId('custom'); + const customEle = screen.getByTestId(customSelector); fireEvent.click(customEle); await waitUntilTo(() => { expect(screen.getByTestId('forwardPage')).toBeInTheDocument(); diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ForwardCall.tsx b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ForwardCall.tsx index 621993eb97..99dba9be65 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ForwardCall.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ForwardCall.tsx @@ -1,5 +1,7 @@ import { fireEvent, screen } from '@testing-library/react'; + import type { StepFunction } from '../../../../lib/step'; + import { CallButtonBehavior } from './CallButtonBehavior'; export const ForwardCall: StepFunction = async (props, context) => { diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/HangupCall.tsx b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/HangupCall.tsx index 79049292e9..279daa41a2 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/HangupCall.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/HangupCall.tsx @@ -1,4 +1,5 @@ import { fireEvent, screen, waitFor } from '@testing-library/react'; + import type { StepFunction } from '../../../../lib/step'; export const HangupCall: StepFunction = async () => { diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/IgnoreCall.tsx b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/IgnoreCall.tsx index 8a0641d14b..1c69eb152b 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/IgnoreCall.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/IgnoreCall.tsx @@ -1,4 +1,5 @@ import type { StepFunction } from '../../../../lib/step'; + import { CallButtonBehavior } from './CallButtonBehavior'; export const IgnoreCall: StepFunction = async (props, context) => { diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/MakeForwardCall.tsx b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/MakeForwardCall.tsx index 747b63721d..2285a031a6 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/MakeForwardCall.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/MakeForwardCall.tsx @@ -1,6 +1,8 @@ import { waitUntilTo } from '@ringcentral-integration/utils'; import { fireEvent, screen } from '@testing-library/react'; + import type { StepFunction } from '../../../../lib/step'; + import { CallButtonBehavior } from './CallButtonBehavior'; export const MakeForwardCall: StepFunction<{ diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/OperateCall.tsx b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/OperateCall.tsx index 65dc1538f6..5a8a0dca8f 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/OperateCall.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/OperateCall.tsx @@ -1,4 +1,5 @@ import { fireEvent, screen, getByTestId } from '@testing-library/react'; + import type { StepFunction } from '../../../../lib/step'; interface OperateCallProps { diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/RecordCall.tsx b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/RecordCall.tsx index 00da11ab2a..674a62c0ed 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/RecordCall.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/RecordCall.tsx @@ -1,4 +1,5 @@ import { fireEvent, screen } from '@testing-library/react'; + import type { StepFunction } from '../../../../lib/step'; export const RecordCall: StepFunction = () => { diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/StopRecordCall.tsx b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/StopRecordCall.tsx index 3aff04e9b4..e1323247b0 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/StopRecordCall.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/StopRecordCall.tsx @@ -1,4 +1,5 @@ import { fireEvent, screen } from '@testing-library/react'; + import type { StepFunction } from '../../../../lib/step'; export const StopRecordCall: StepFunction = () => { diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ToVoiceMail.tsx b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ToVoiceMail.tsx index 756a6eb663..98f2e4ef71 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ToVoiceMail.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/ToVoiceMail.tsx @@ -1,4 +1,5 @@ import type { StepFunction } from '../../../../lib/step'; + import { CallButtonBehavior } from './CallButtonBehavior'; export const ToVoiceMail: StepFunction = async (props, context) => { diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/TransferCall.tsx b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/TransferCall.tsx index f9a197ea51..add4614167 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/TransferCall.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/TransferCall.tsx @@ -1,6 +1,8 @@ import type { StepFunction } from '@ringcentral-integration/test-utils'; import { fireEvent, screen, waitFor } from '@testing-library/react'; + import { ClickDialNumberButton } from '../../actions/ClickDialNumberButton'; + import { CallButtonBehavior } from './CallButtonBehavior'; export const GoToTransferPage: StepFunction = () => { diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/TypeInvalidForwardNumber.tsx b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/TypeInvalidForwardNumber.tsx index 5c726add31..194412c30c 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/TypeInvalidForwardNumber.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/TypeInvalidForwardNumber.tsx @@ -1,4 +1,5 @@ import { fireEvent, screen } from '@testing-library/react'; + import type { StepFunction } from '../../../../lib/step'; export const TypeInvalidForwardNumber: StepFunction = async () => { diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/index.ts b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/index.ts index 40b268c50c..719643a16a 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/index.ts +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/actions/index.ts @@ -1,11 +1,13 @@ export * from './AnswerCallAtOtherDevice'; export * from './CallButtonBehavior'; +export * from './AnswerAndHoldCall'; export * from './AnswerCall'; export * from './CustomForwardCall'; export * from './ForwardCall'; export * from './IgnoreCall'; export * from './ClickMoreButton'; export * from './ToVoiceMail'; +export * from './ClickCircleButton'; export * from './TypeInvalidForwardNumber'; export * from './StopRecordCall'; export * from './HangupCall'; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/checks/CheckButtonExist.tsx b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/checks/CheckButtonExist.tsx index f79bb5360a..65d25d6d21 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/checks/CheckButtonExist.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/checks/CheckButtonExist.tsx @@ -1,11 +1,33 @@ -import { screen } from '@testing-library/react'; +import { screen, queryByTestId } from '@testing-library/react'; import type { StepFunction } from '../../../../lib/step'; export const CheckButtonExist: StepFunction<{ callButtonBehaviorType: string; + isExist?: boolean; }> = async (props) => { - const { callButtonBehaviorType } = props; - const callButton = screen.queryAllByTestId(callButtonBehaviorType)[0]; - expect(callButton).toBeInTheDocument(); + const { callButtonBehaviorType, isExist = true } = props; + const callButton = screen.queryByTestId(callButtonBehaviorType); + if (isExist) { + expect(callButton).toBeInTheDocument(); + } else { + expect(callButton).not.toBeInTheDocument(); + } +}; + +export const CheckCallLogButtonExist: StepFunction<{ + callButtonBehaviorType: string; + isExist?: boolean; +}> = async (props) => { + const smallControlControlPanel = screen.getByTestId('smallCallControl'); + const { callButtonBehaviorType, isExist = true } = props; + const callButton = queryByTestId( + smallControlControlPanel, + callButtonBehaviorType, + ); + if (isExist) { + expect(callButton).toBeInTheDocument(); + } else { + expect(callButton).not.toBeInTheDocument(); + } }; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/checks/CheckConferenceInfoPage.ts b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/checks/CheckConferenceInfoPage.ts index abc3c246ad..ad6d77daf4 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/checks/CheckConferenceInfoPage.ts +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/checks/CheckConferenceInfoPage.ts @@ -1,6 +1,6 @@ +import type { UserAgent } from '@ringcentral-integration/mock/src/webphone/Webphone'; import { waitUntilTo } from '@ringcentral-integration/utils'; import { screen } from '@testing-library/react'; -import type { UserAgent } from '@ringcentral-integration/mock/src/webphone/Webphone'; import type { StepFunction } from '../../../../lib/step'; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/checks/CheckSmallControl.tsx b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/checks/CheckSmallControl.tsx index d50359057e..6e1fa9b311 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/checks/CheckSmallControl.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/checks/CheckSmallControl.tsx @@ -1,4 +1,5 @@ import { screen, waitFor } from '@testing-library/react'; + import type { StepFunction } from '../../../../lib/step'; export const CheckSmallControlControlPanel: StepFunction<{ diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/checks/CheckSmallControlButton.tsx b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/checks/CheckSmallControlButton.tsx index c3364b0511..a2deca008e 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/checks/CheckSmallControlButton.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/checks/CheckSmallControlButton.tsx @@ -1,5 +1,7 @@ import { getByTestId, screen } from '@testing-library/react'; + import type { StepFunction } from '../../../../lib/step'; + type ButtonKey = | 'hold' | 'onHold' diff --git a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/checks/CheckStopRecordButtonExist.tsx b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/checks/CheckStopRecordButtonExist.tsx index f2abdd2431..9fc2afd314 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/checks/CheckStopRecordButtonExist.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/SmallCallControl/checks/CheckStopRecordButtonExist.tsx @@ -1,4 +1,5 @@ import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../../lib/step'; export const CheckStopRecordButtonExist: StepFunction = () => { diff --git a/packages/ringcentral-widgets-test/test/steps/Call/Webphone/actions/TriggerWebphoneEvent.tsx b/packages/ringcentral-widgets-test/test/steps/Call/Webphone/actions/TriggerWebphoneEvent.tsx new file mode 100644 index 0000000000..dd3d9dae68 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Call/Webphone/actions/TriggerWebphoneEvent.tsx @@ -0,0 +1,16 @@ +import type { StepFunction } from '../../../../lib/step'; + +interface TriggerWebphoneEventProps { + eventName: + | 'registered' + | 'unregistered' + | 'registrationFailed' + | 'provisionUpdate'; + args?: unknown[]; +} + +export const TriggerWebphoneEvent: StepFunction< + TriggerWebphoneEventProps +> = async ({ eventName, args = [] }, { phone }) => { + phone.webphone._webphone.userAgent.trigger(eventName, ...args); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/Webphone/actions/index.ts b/packages/ringcentral-widgets-test/test/steps/Call/Webphone/actions/index.ts index 3ca3ba6d76..8e1832d88b 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/Webphone/actions/index.ts +++ b/packages/ringcentral-widgets-test/test/steps/Call/Webphone/actions/index.ts @@ -1 +1,2 @@ export * from './ChangeConnectionStatus'; +export * from './TriggerWebphoneEvent'; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/Webphone/index.ts b/packages/ringcentral-widgets-test/test/steps/Call/Webphone/index.ts index e6ffa57916..2b782a6470 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/Webphone/index.ts +++ b/packages/ringcentral-widgets-test/test/steps/Call/Webphone/index.ts @@ -1,2 +1,3 @@ export * from './actions'; export * from './checks'; +export * from './mocks'; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/Webphone/mocks/MockSipProvision.tsx b/packages/ringcentral-widgets-test/test/steps/Call/Webphone/mocks/MockSipProvision.tsx new file mode 100644 index 0000000000..3772f36b5f --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Call/Webphone/mocks/MockSipProvision.tsx @@ -0,0 +1,23 @@ +import type { StepFunction } from '../../../../lib/step'; + +interface MockSipProvisionProps { + handler?: (mockData: unknown) => any; + repeat?: number; + status?: number; +} + +export const MockSipProvision: StepFunction = async ( + { handler, repeat, status }, + { rcMock }, +) => { + function mock() { + rcMock.postSipProvision(handler, repeat, status); + } + + if (rcMock.initialized) { + mock(); + } else { + rcMock.defaultInitMocks.delete(rcMock.postSipProvision); + rcMock.defaultInitMocks.add(mock); + } +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/Webphone/mocks/index.ts b/packages/ringcentral-widgets-test/test/steps/Call/Webphone/mocks/index.ts new file mode 100644 index 0000000000..b1a273b8e7 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Call/Webphone/mocks/index.ts @@ -0,0 +1 @@ +export * from './MockSipProvision'; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/actions/ClickBackButton.tsx b/packages/ringcentral-widgets-test/test/steps/Call/actions/ClickBackButton.tsx index c87926e4a3..fb56e62594 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/actions/ClickBackButton.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/actions/ClickBackButton.tsx @@ -1,5 +1,5 @@ -import { screen } from '@testing-library/react'; import { waitForRenderReady } from '@ringcentral-integration/test-utils'; +import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; export const ClickBackButton = async () => { diff --git a/packages/ringcentral-widgets-test/test/steps/Call/actions/ClickBackButtonOfIncomingCallPanel.tsx b/packages/ringcentral-widgets-test/test/steps/Call/actions/ClickBackButtonOfIncomingCallPanel.tsx new file mode 100644 index 0000000000..dee31a47f3 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Call/actions/ClickBackButtonOfIncomingCallPanel.tsx @@ -0,0 +1,10 @@ +import { waitForRenderReady } from '@ringcentral-integration/test-utils'; +import { screen, within } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +export const ClickBackButtonOfIncomingCallPanel = async () => { + const container = screen.getByTestId('IncomingCallPanel'); + const backButton = within(container).getByTestId('backButton'); + userEvent.click(backButton); + await waitForRenderReady(); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/actions/ClickButtonInAllCalls.tsx b/packages/ringcentral-widgets-test/test/steps/Call/actions/ClickButtonInAllCalls.tsx new file mode 100644 index 0000000000..ec0b80e3eb --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Call/actions/ClickButtonInAllCalls.tsx @@ -0,0 +1,23 @@ +import { within, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import { StepFunction } from '../../../lib/step'; + +interface ClickButtonInAllCallsProps { + callButton: string; + callSection: 'Call on Hold'; +} + +export const ClickButtonInAllCalls: StepFunction< + ClickButtonInAllCallsProps +> = ({ callButton, callSection }) => { + const callsList = screen.queryAllByTestId('callList'); + callsList.forEach((list) => { + if (within(list).queryByText(callSection)) { + const button = within(list) + .getByTestId(callButton) + .querySelector('circle')!; + userEvent.click(button); + } + }); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/actions/ClickCallItemByLabel.tsx b/packages/ringcentral-widgets-test/test/steps/Call/actions/ClickCallItemByLabel.tsx index 0ef106b4ff..896e985fd9 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/actions/ClickCallItemByLabel.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/actions/ClickCallItemByLabel.tsx @@ -1,15 +1,21 @@ -import { screen, within } from '@testing-library/react'; import { waitForRenderReady } from '@ringcentral-integration/test-utils'; +import { screen, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import type { StepFunction } from '../../../lib/step'; export const ClickCallItemByLabel: StepFunction<{ label: string; -}> = async ({ label }) => { + currentName?: string; +}> = async ({ label, currentName }) => { const callsList = screen.queryAllByTestId('callList'); callsList.forEach((list) => { - if (within(list).queryAllByLabelText(label)) { + if (within(list).queryByText(label)) { + if (currentName) { + expect(within(list).getByTestId('currentName')).toHaveTextContent( + currentName, + ); + } userEvent.click(within(list).getByTestId('currentName')); } }); diff --git a/packages/ringcentral-widgets-test/test/steps/Call/actions/ClickDialButton.tsx b/packages/ringcentral-widgets-test/test/steps/Call/actions/ClickDialButton.tsx index 9f783e4a39..aa3141206d 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/actions/ClickDialButton.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/actions/ClickDialButton.tsx @@ -1,7 +1,16 @@ -import { waitForRenderReady } from '@ringcentral-integration/test-utils'; -import userEvent from '@testing-library/user-event'; +import { whenStateChange } from '@ringcentral-integration/core/test'; +import { + screen, + userEvent, + waitForRenderReady, +} from '@ringcentral-integration/test-utils'; export const ClickDialButton = async () => { - userEvent.click(document.querySelector('[data-sign="callButton"] circle')); + const button = screen.getByTestId('callButton')!; + await whenStateChange(() => { + expect(button).toHaveAttribute('aria-disabled', 'false'); + }); + + userEvent.click(button); await waitForRenderReady(); }; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/actions/MakeCall.tsx b/packages/ringcentral-widgets-test/test/steps/Call/actions/MakeCall.tsx index c49340da8e..d9fc31bc4a 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/actions/MakeCall.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/actions/MakeCall.tsx @@ -2,6 +2,7 @@ import type { NumberData } from '@ringcentral-integration/commons/integration-te import type { StepFunction } from '../../../lib/step'; import { AnswerCall } from '../SmallCallControl'; + import { MakeInboundCall } from './MakeInboundCall'; import { MakeOutboundCall } from './MakeOutboundCall'; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/actions/MakeInboundCall.tsx b/packages/ringcentral-widgets-test/test/steps/Call/actions/MakeInboundCall.tsx index 26033ab047..b2356f74c8 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/actions/MakeInboundCall.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/actions/MakeInboundCall.tsx @@ -1,15 +1,12 @@ -import type callDirections from '@ringcentral-integration/commons/enums/callDirections'; +import type { CallDirection } from '@ringcentral-integration/commons/enums/callDirections'; import type { NumberData } from '@ringcentral-integration/commons/integration-test/mock/telephonySessionBuilder'; import type { StepFunction } from '../../../lib/step'; -type CallDirectionsKeys = keyof typeof callDirections; -type CallDirections = typeof callDirections[CallDirectionsKeys]; - export interface InitACallProps { phoneNumber?: string; isWebRTC?: boolean; - direction?: CallDirections; + direction?: CallDirection; telephonySessionId?: string; sessionId?: string; fromNumberData?: NumberData; @@ -22,14 +19,29 @@ export interface InitACallProps { } export const MakeInboundCall: StepFunction = async ( - { phoneNumber = '+18882556247', fromNumberData, startTime, ...props }, - { rcMock }, + { + phoneNumber = '+18882556247', + fromNumberData, + startTime, + isWebRTC = true, + ...props + }, + { rcMock, phone }, ) => { + // No inbound call when webphone register failed + if (isWebRTC) { + const isWebphoneRegisterFailed = !!phone.webphone.errorCode; + if (isWebphoneRegisterFailed) { + return; + } + } + await rcMock.makeCall({ phoneNumber, fromNumberData, direction: 'Inbound', startTime, + isWebRTC, ...props, }); }; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/actions/MakeOtherDeviceCall.tsx b/packages/ringcentral-widgets-test/test/steps/Call/actions/MakeOtherDeviceCall.tsx index 7bb8272fa2..e41f298666 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/actions/MakeOtherDeviceCall.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/actions/MakeOtherDeviceCall.tsx @@ -1,5 +1,7 @@ -import callDirection from '@ringcentral-integration/commons/enums/callDirections'; -import type { CallDirection } from '@ringcentral-integration/commons/modules/ConferenceCall'; +import { + callDirection, + type CallDirection, +} from '@ringcentral-integration/commons/enums/callDirections'; import { PartyStatusCode } from '@ringcentral-integration/commons/integration-test/mock/telephonySessionBuilder'; import type { StepFunction } from '../../../lib/step'; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/actions/MakeOutboundCall.tsx b/packages/ringcentral-widgets-test/test/steps/Call/actions/MakeOutboundCall.tsx index 51b3ed2981..b60897fe4d 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/actions/MakeOutboundCall.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/actions/MakeOutboundCall.tsx @@ -1,7 +1,8 @@ -import { screen } from '@testing-library/react'; import { waitForRenderReady } from '@ringcentral-integration/test-utils'; +import { fireEvent, screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; -import { ClickDialButton } from './ClickDialButton'; + import { ClickDialNumberButton } from './ClickDialNumberButton'; export const MakeOutboundCall: StepFunction<{ @@ -42,16 +43,15 @@ export const MakeOutboundCall: StepFunction<{ await ClickDialNumberButton(char); } expect(screen.getByTestId('recipientsInput')).toHaveValue(phoneNumber); - ClickDialButton(); - // await make call successful - const { - phone: { callMonitor }, - } = context; - await new Promise((resolve) => { - callMonitor.onNewCall(() => { + + const promise = new Promise((resolve) => { + context.phone.callMonitor.onNewCall(() => { resolve(true); }); }); + fireEvent.click(document.querySelector('.callBtn circle')!); + // await make call successful + await promise; await waitForRenderReady(); } }; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/actions/index.ts b/packages/ringcentral-widgets-test/test/steps/Call/actions/index.ts index fc6b992be2..4a6b193d9b 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/actions/index.ts +++ b/packages/ringcentral-widgets-test/test/steps/Call/actions/index.ts @@ -2,6 +2,8 @@ export * from '../SmallCallControl/actions'; export * from '../Webphone/actions'; export * from './MakeOtherDeviceCall'; export * from './ClickDialButton'; +export * from './ClickBackButtonOfIncomingCallPanel'; +export * from './ClickButtonInAllCalls'; export * from './MakeCall'; export * from './MakeC2DCall'; export * from './MakeInboundCall'; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckActiveCallDialPadExist.tsx b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckActiveCallDialPadExist.tsx index fa240f3470..3f725d236a 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckActiveCallDialPadExist.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckActiveCallDialPadExist.tsx @@ -1,4 +1,5 @@ import { screen, waitFor } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const CheckActiveCallDialPadExist: StepFunction = async () => { diff --git a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckActiveCallExist.tsx b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckActiveCallExist.tsx index be7b91c4d5..1322a2a8fc 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckActiveCallExist.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckActiveCallExist.tsx @@ -1,11 +1,10 @@ -import { screen, waitFor } from '@testing-library/react'; +import { whenStateOrTimerChange } from '@ringcentral-integration/core/test'; +import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const CheckActiveCallExist: StepFunction = async () => { - await waitFor( - () => { - expect(screen.getByTestId('activeCallPanel')).toBeInTheDocument(); - }, - { timeout: 3000 }, - ); + await whenStateOrTimerChange(() => { + expect(screen.getByTestId('activeCallPanel')).toBeInTheDocument(); + }); }; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckActiveCallHidden.tsx b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckActiveCallHidden.tsx index 2139e017c5..7ef4194b6e 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckActiveCallHidden.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckActiveCallHidden.tsx @@ -1,4 +1,5 @@ import { screen, waitFor } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const CheckActiveCallHidden: StepFunction = async () => { diff --git a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckAllCallsListPage.tsx b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckAllCallsListPage.tsx index ca8583fb78..994fd2c78a 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckAllCallsListPage.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckAllCallsListPage.tsx @@ -1,4 +1,5 @@ import { screen, waitFor, within } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; const CheckSpecificCallsItem = (title: string, length: number) => { diff --git a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckAnswerAndEndBehavior.tsx b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckAnswerAndEndBehavior.tsx index 240ea57d31..f335ef644c 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckAnswerAndEndBehavior.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckAnswerAndEndBehavior.tsx @@ -1,4 +1,5 @@ import { screen, waitFor } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; interface CheckAnswerAndEndProps { @@ -6,18 +7,19 @@ interface CheckAnswerAndEndProps { answerCallNumber: string; } -export const CheckAnswerAndEndBehavior: StepFunction = - async ({ hungUpCallId, answerCallNumber }, { phone }) => { - const secondCall = phone.webphone.sessions.find( - (item) => item.to === answerCallNumber, - ); - // The secondCall was answered - expect(phone.webphone.answer).toBeCalledWith(secondCall.id); - expect(phone.webphone._onAccepted).toBeCalledWith( - expect.objectContaining({ - id: secondCall?.id, - }), - ); - // The first call should be hangup - expect(phone.webphone.hangup).toBeCalledWith(hungUpCallId); - }; +export const CheckAnswerAndEndBehavior: StepFunction< + CheckAnswerAndEndProps +> = async ({ hungUpCallId, answerCallNumber }, { phone }) => { + const secondCall = phone.webphone.sessions.find( + (item) => item.to === answerCallNumber, + ); + // The secondCall was answered + expect(phone.webphone.answer).toHaveBeenCalledWith(secondCall.id); + expect(phone.webphone._onAccepted).toHaveBeenCalledWith( + expect.objectContaining({ + id: secondCall?.id, + }), + ); + // The first call should be hangup + expect(phone.webphone.hangup).toHaveBeenCalledWith(hungUpCallId); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckAnswerAndHoldBehavior.tsx b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckAnswerAndHoldBehavior.tsx index b58f9b0498..8fffa56828 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckAnswerAndHoldBehavior.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckAnswerAndHoldBehavior.tsx @@ -1,4 +1,5 @@ import { screen, waitFor } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; interface CheckAnswerAndHoldProps { @@ -7,24 +8,25 @@ interface CheckAnswerAndHoldProps { firstCallFinder?: (sessions: any) => {}; } -export const CheckAnswerAndHoldBehavior: StepFunction = - async ( - { firstCallPNumber, secondCallPNumber, firstCallFinder }, - { phone }, - ) => { - const firstCall = - firstCallFinder?.(phone.webphone.sessions) || - phone.webphone.sessions.find((item) => item.to === firstCallPNumber); - const secondCall = phone.webphone.sessions.find( - (item) => item.to === secondCallPNumber, - ); - // The secondCall was answered - expect(phone.webphone.answer).toBeCalledWith(secondCall.id); - expect(phone.webphone._onAccepted).toBeCalledWith( - expect.objectContaining({ - id: secondCall?.id, - }), - ); - // The first call should be held - expect(phone.webphone.hold).toBeCalledWith(firstCall?.id); - }; +export const CheckAnswerAndHoldBehavior: StepFunction< + CheckAnswerAndHoldProps +> = async ( + { firstCallPNumber, secondCallPNumber, firstCallFinder }, + { phone }, +) => { + const firstCall = + firstCallFinder?.(phone.webphone.sessions) || + phone.webphone.sessions.find((item) => item.to === firstCallPNumber); + const secondCall = phone.webphone.sessions.find( + (item) => item.to === secondCallPNumber, + ); + // The secondCall was answered + expect(phone.webphone.answer).toHaveBeenCalledWith(secondCall.id); + expect(phone.webphone._onAccepted).toHaveBeenCalledWith( + expect.objectContaining({ + id: secondCall?.id, + }), + ); + // The first call should be held + expect(phone.webphone.hold).toHaveBeenCalledWith(firstCall?.id); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckAnswerBehavior.tsx b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckAnswerBehavior.tsx index 588b805ca5..fb3a11e818 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckAnswerBehavior.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckAnswerBehavior.tsx @@ -5,20 +5,21 @@ interface CheckAnswerBehaviorProps { answerCallFinder?: (sessions: any) => {}; } -export const CheckAnswerBehavior: StepFunction = - async ({ answerCallPNumber, answerCallFinder }, { phone }) => { - const answerCallSession = - answerCallFinder?.(phone.webphone.sessions) || - phone.webphone.sessions.find((item) => item.to === answerCallPNumber); - // The call should be answered - expect(phone.webphone.answer).toBeCalledWith(answerCallSession.id); - expect(phone.webphone._onAccepted).toBeCalledWith( - expect.objectContaining({ - id: answerCallSession?.id, - }), - ); - // other call would be held - expect(phone.webphone._holdOtherSession).toBeCalledWith( - answerCallSession.id, - ); - }; +export const CheckAnswerBehavior: StepFunction< + CheckAnswerBehaviorProps +> = async ({ answerCallPNumber, answerCallFinder }, { phone }) => { + const answerCallSession = + answerCallFinder?.(phone.webphone.sessions) || + phone.webphone.sessions.find((item) => item.to === answerCallPNumber); + // The call should be answered + expect(phone.webphone.answer).toHaveBeenCalledWith(answerCallSession.id); + expect(phone.webphone._onAccepted).toHaveBeenCalledWith( + expect.objectContaining({ + id: answerCallSession?.id, + }), + ); + // other call would be held + expect(phone.webphone._holdOtherSession).toHaveBeenCalledWith( + answerCallSession.id, + ); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckBackButtonOfIncomingCallPanel.tsx b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckBackButtonOfIncomingCallPanel.tsx new file mode 100644 index 0000000000..d8b167dc43 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckBackButtonOfIncomingCallPanel.tsx @@ -0,0 +1,9 @@ +import { waitForRenderReady } from '@ringcentral-integration/test-utils'; +import { screen, within } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +export const CheckBackButtonOfIncomingCallPanel = async () => { + const container = screen.getByTestId('IncomingCallPanel'); + const backButton = within(container).getByTestId('backButton'); + expect(backButton).toBeInTheDocument(); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckCallButtonEnable.tsx b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckCallButtonEnable.tsx index 36cd944c6e..03b3349686 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckCallButtonEnable.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckCallButtonEnable.tsx @@ -1,6 +1,6 @@ -import { ClickToNavigatePage } from '../../Router/action/PageNavigation'; import type { Context } from '../../../interfaces'; import type { StepFunction } from '../../../lib/step'; +import { ClickToNavigatePage } from '../../Router'; export const CheckCallButtonEnable: StepFunction<{ enable: boolean; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckCallControlPage.tsx b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckCallControlPage.tsx index 0614976225..87f697902b 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckCallControlPage.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckCallControlPage.tsx @@ -1,63 +1,59 @@ -import { waitUntilTo } from '@ringcentral-integration/utils'; -import { screen, waitFor } from '@testing-library/react'; +import { whenStateOrTimerChange } from '@ringcentral-integration/core/test'; +import { screen, within } from '@testing-library/react'; import type { StepFunction } from '../../../lib/step'; -interface OperationProps { +interface CheckCallControlPageProps { parsedNumber: string; name?: string; - type?: 'company' | 'personal'; + // According to + // There are multiple ways to display components + contactDisplayType?: 'contact-multiple-match' | 'normal'; showDuration?: boolean; showCustomAvatar?: boolean; + durationDataSign?: string; } -export const CheckCallControlPage: StepFunction = async ({ +export const CheckCallControlPage: StepFunction< + CheckCallControlPageProps +> = async ({ parsedNumber, name = 'Unknown', - type = 'company', + contactDisplayType = 'normal', showDuration = false, showCustomAvatar = false, + durationDataSign = 'callDuration', }) => { - await waitUntilTo(() => { + await whenStateOrTimerChange(() => { expect(screen.getByTestId('activeCallPanel')).toBeInTheDocument(); - }); - expect(screen.getByTestId('userPhoneNumber')).toHaveTextContent( - new RegExp(parsedNumber.replace(/\(|\)|\+/g, (match) => `\\${match}`)), - ); - - if (showDuration) { - await waitFor( - () => { - expect(screen.getByTestId('callDuration')).toBeInTheDocument(); - }, - { timeout: 3000 }, + expect(screen.getByTestId('userPhoneNumber')).toHaveTextContent( + new RegExp(parsedNumber.replace(/\(|\)|\+/g, (match) => `\\${match}`)), ); - } - expect(screen.getByTestId('avatar')).toBeInTheDocument(); - if (showCustomAvatar) { - expect( - screen - .getByTestId('avatar') - ?.querySelector('svg') - ?.querySelector('image'), - ).toBeInTheDocument(); - } else { - expect( - screen - .getByTestId('avatar') - ?.querySelector('svg') - ?.querySelector('image'), - ).not.toBeInTheDocument(); - } - await waitUntilTo(() => { - if (type === 'personal') { + if (showDuration) { + expect(screen.getByTestId(durationDataSign)).toBeInTheDocument(); + } + + const avatarElement = screen.getByTestId('avatar'); + expect(avatarElement).toBeInTheDocument(); + + const avatarImage = avatarElement + .querySelector('svg') + ?.querySelector('image'); + if (showCustomAvatar) { + expect(avatarImage).toBeInTheDocument(); + } else { + expect(avatarImage).not.toBeInTheDocument(); + } + + const matchesMenuButton = screen.queryByTestId('menuButton'); + if (contactDisplayType === 'contact-multiple-match') { + expect(matchesMenuButton).toBeInTheDocument(); expect( - screen - .getByTestId('menuButton') - .querySelector('[data-sign="currentName"]'), + within(matchesMenuButton!).getByTestId('currentName'), ).toHaveTextContent(name); } else { + expect(matchesMenuButton).not.toBeInTheDocument(); expect(screen.getByTestId('currentName')).toHaveTextContent(name); } }); diff --git a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckCallCtrlButton.tsx b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckCallCtrlButton.tsx index 298e9a6e52..3fd6f448e3 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckCallCtrlButton.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckCallCtrlButton.tsx @@ -1,5 +1,6 @@ -import { screen } from '@testing-library/react'; import { waitForRenderReady } from '@ringcentral-integration/test-utils'; +import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const CheckCallCtrlButton: StepFunction<{ diff --git a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckCallCtrlButtonEnable.tsx b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckCallCtrlButtonEnable.tsx index 23e5e910c8..9f2888f4f2 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckCallCtrlButtonEnable.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckCallCtrlButtonEnable.tsx @@ -1,14 +1,24 @@ +import { whenStateChange } from '@ringcentral-integration/core/test'; import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const CheckCallCtrlButtonEnable: StepFunction<{ - callButtonBehaviorType: 'hold' | 'onHold' | 'record' | 'flip' | 'unmute'; + callButtonBehaviorType: + | 'hold' + | 'onHold' + | 'record' + | 'flip' + | 'unmute' + | 'transfer'; isDisabled?: boolean; }> = async ({ callButtonBehaviorType, isDisabled }) => { - const button = screen.getByTestId(callButtonBehaviorType); - if (isDisabled) { - expect(button.classList.value).toContain('buttonDisabled'); - } else { - expect(button.classList.value).not.toContain('buttonDisabled'); - } + await whenStateChange(() => { + const button = screen.getByTestId(callButtonBehaviorType); + if (isDisabled) { + expect(button.classList.value).toContain('buttonDisabled'); + } else { + expect(button.classList.value).not.toContain('buttonDisabled'); + } + }); }; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckCallStatus.tsx b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckCallStatus.tsx index e1e7671dcf..e20aba3b8d 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckCallStatus.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckCallStatus.tsx @@ -1,4 +1,5 @@ import { screen, waitFor } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const CheckCallStatus: StepFunction<{ status: string }> = ({ diff --git a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckCallWithJupiterLink.tsx b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckCallWithJupiterLink.tsx index 15687820e9..140d268cd2 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckCallWithJupiterLink.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckCallWithJupiterLink.tsx @@ -1,10 +1,11 @@ import { screen, waitFor } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; -export const CheckCallWithJupiterLink: StepFunction<{ content: string }> = ({ - link, - phoneNumber, -}: any) => { +export const CheckCallWithJupiterLink: StepFunction<{ + link: RegExp; + phoneNumber: string; +}> = ({ link, phoneNumber }: any) => { const callNumber = phoneNumber.replace(/\+|\*/g, (match) => { return `\\${match}`; }); diff --git a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckDialPadInput.tsx b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckDialPadInput.tsx index 8ea1883820..21bb420cf5 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckDialPadInput.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckDialPadInput.tsx @@ -1,4 +1,5 @@ import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const CheckDialPadInput: StepFunction<{ content: string }> = ({ diff --git a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckHoldBehavior.tsx b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckHoldBehavior.tsx index 74a77172e4..18aface3e5 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckHoldBehavior.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckHoldBehavior.tsx @@ -12,5 +12,5 @@ export const CheckHoldBehavior: StepFunction = async ( const currentCall = phone.webphone.sessions.find( (item) => item.id === callId, ); - expect(phone.webphone[type]).toBeCalledWith(currentCall?.id); + expect(phone.webphone[type]).toHaveBeenCalledWith(currentCall?.id); }; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckIgnoreButton.tsx b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckIgnoreButton.tsx index a8162c4bde..6a721d8cee 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckIgnoreButton.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckIgnoreButton.tsx @@ -1,4 +1,5 @@ import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const CheckIgnoreButton: StepFunction = async () => { diff --git a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckIncomingCallPageExist.tsx b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckIncomingCallPageExist.tsx index 9685aacbfa..1e84ee07be 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckIncomingCallPageExist.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckIncomingCallPageExist.tsx @@ -2,6 +2,17 @@ import { screen } from '@testing-library/react'; import type { StepFunction } from '../../../lib/step'; -export const CheckIncomingCallPageExist: StepFunction = async () => { - expect(screen.queryByTestId('IncomingCallPanel')).toBeInTheDocument(); +interface CheckIncomingCallPageExistProps { + exists?: boolean; +} + +export const CheckIncomingCallPageExist: StepFunction< + CheckIncomingCallPageExistProps +> = async ({ exists = true }) => { + const element = screen.queryByTestId('IncomingCallPanel'); + if (exists) { + expect(element).toBeInTheDocument(); + } else { + expect(element).not.toBeInTheDocument(); + } }; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckIncomingCallPageNotExist.tsx b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckIncomingCallPageNotExist.tsx new file mode 100644 index 0000000000..e36dd0e851 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckIncomingCallPageNotExist.tsx @@ -0,0 +1,7 @@ +import { screen } from '@testing-library/react'; + +import type { StepFunction } from '../../../lib/step'; + +export const CheckIncomingCallPageNotExist: StepFunction = async () => { + expect(screen.queryByTestId('IncomingCallPanel')).not.toBeInTheDocument(); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckMuteButton.tsx b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckMuteButton.tsx index 962a727258..648fb6598b 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckMuteButton.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckMuteButton.tsx @@ -1,5 +1,6 @@ -import { screen } from '@testing-library/react'; import { waitForRenderReady } from '@ringcentral-integration/test-utils'; +import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const CheckMuteButton: StepFunction = async () => { diff --git a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckOutboundAndHoldBehavior.tsx b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckOutboundAndHoldBehavior.tsx index 362ff2b380..066b145c4f 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckOutboundAndHoldBehavior.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckOutboundAndHoldBehavior.tsx @@ -1,4 +1,5 @@ import { screen, waitFor } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; interface CheckOutboundAndHoldProps { @@ -7,23 +8,24 @@ interface CheckOutboundAndHoldProps { firstCallFinder?: (sessions: any) => {}; } -export const CheckOutboundAndHoldBehavior: StepFunction = - async ( - { firstCallPNumber, secondCallPNumber, firstCallFinder }, - { phone }, - ) => { - const firstCall = - firstCallFinder?.(phone.webphone.sessions) || - phone.webphone.sessions.find((item) => item.to === firstCallPNumber); - const secondCall = phone.webphone.sessions.find( - (item) => item.to === secondCallPNumber, - ); - // The secondCall was answered - expect(phone.webphone._onAccepted).toBeCalledWith( - expect.objectContaining({ - id: secondCall?.id, - }), - ); - // The first call should be held - expect(firstCall.callStatus).toEqual('webphone-session-onHold'); - }; +export const CheckOutboundAndHoldBehavior: StepFunction< + CheckOutboundAndHoldProps +> = async ( + { firstCallPNumber, secondCallPNumber, firstCallFinder }, + { phone }, +) => { + const firstCall = + firstCallFinder?.(phone.webphone.sessions) || + phone.webphone.sessions.find((item) => item.to === firstCallPNumber); + const secondCall = phone.webphone.sessions.find( + (item) => item.to === secondCallPNumber, + ); + // The secondCall was answered + expect(phone.webphone._onAccepted).toHaveBeenCalledWith( + expect.objectContaining({ + id: secondCall?.id, + }), + ); + // The first call should be held + expect(firstCall.callStatus).toEqual('webphone-session-onHold'); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckParseApiCalledWithParams.tsx b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckParseApiCalledWithParams.tsx index 439c396b38..55846be5c3 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckParseApiCalledWithParams.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckParseApiCalledWithParams.tsx @@ -1,4 +1,5 @@ import { contextSourceOption } from '@ringcentral-integration/commons/modules/NumberValidate'; + import type { StepFunction } from '../../../lib/step'; export const CheckParseApiCalledWithParams: StepFunction<{ diff --git a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckToVoiceMailForInboundQueueCall.tsx b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckToVoiceMailForInboundQueueCall.tsx index 75fee6332c..71e16ec13b 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckToVoiceMailForInboundQueueCall.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckToVoiceMailForInboundQueueCall.tsx @@ -1,30 +1,32 @@ import type { RcMock } from '@ringcentral-integration/mock'; import userEvent from '@testing-library/user-event'; + import type { StepFunction } from '../../../lib/step'; import { ClickBackButton } from '../actions'; interface CheckToVoiceMailForInboundQueueCallProps { isGoToAllCallsPage?: boolean; } -export const CheckToVoiceMailForInboundQueueCall: StepFunction = - async ({ isGoToAllCallsPage = false }, context) => { - const { phone } = context; - const ignoreCall = jest.spyOn(phone.activeCallControl, 'ignore'); - const rcMock: RcMock = global.instance.rcMock; +export const CheckToVoiceMailForInboundQueueCall: StepFunction< + CheckToVoiceMailForInboundQueueCallProps +> = async ({ isGoToAllCallsPage = false }, context) => { + const { phone } = context; + const ignoreCall = jest.spyOn(phone.activeCallControl, 'ignore'); + const rcMock: RcMock = global.instance.rcMock; - await rcMock.makeCall({ - queueCall: true, - direction: 'Inbound', - }); + await rcMock.makeCall({ + queueCall: true, + direction: 'Inbound', + }); - if (isGoToAllCallsPage) { - ClickBackButton; - } + if (isGoToAllCallsPage) { + ClickBackButton; + } - const toVoiceMail = document.querySelector( - '[data-sign="toVoiceMail"] circle', - ); - if (toVoiceMail) userEvent.click(toVoiceMail); + const toVoiceMail = document.querySelector( + '[data-sign="toVoiceMail"] circle', + ); + if (toVoiceMail) userEvent.click(toVoiceMail); - expect(ignoreCall).toHaveBeenCalledTimes(1); - }; + expect(ignoreCall).toHaveBeenCalledTimes(1); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckUnmuteButton.tsx b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckUnmuteButton.tsx index 4acc08ea84..92ba1dc96b 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckUnmuteButton.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Call/checks/CheckUnmuteButton.tsx @@ -1,4 +1,5 @@ import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const CheckUnmuteButton: StepFunction = async () => { diff --git a/packages/ringcentral-widgets-test/test/steps/Call/checks/index.ts b/packages/ringcentral-widgets-test/test/steps/Call/checks/index.ts index 77ef595066..ff0f7c20db 100644 --- a/packages/ringcentral-widgets-test/test/steps/Call/checks/index.ts +++ b/packages/ringcentral-widgets-test/test/steps/Call/checks/index.ts @@ -3,6 +3,7 @@ export * from '../SmallCallControl'; export * from './CheckCallButtonEnable'; export * from './CheckCallStatus'; export * from './CheckIncomingCallPageExist'; +export * from './CheckIncomingCallPageNotExist'; export * from './CheckParseApiCalledWithParams'; export * from './CheckPassAreaCode'; export * from './CheckMuteButton'; @@ -24,3 +25,4 @@ export * from './CheckHoldBehavior'; export * from './CheckAnswerBehavior'; export * from './CheckToVoiceMailForInboundQueueCall'; export * from './CheckCallWithJupiterLink'; +export * from './CheckBackButtonOfIncomingCallPanel'; diff --git a/packages/ringcentral-widgets-test/test/steps/CallHistory/actions/ClickCurrentName.tsx b/packages/ringcentral-widgets-test/test/steps/CallHistory/actions/ClickCurrentName.tsx new file mode 100644 index 0000000000..95225a0dba --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/CallHistory/actions/ClickCurrentName.tsx @@ -0,0 +1,10 @@ +import { screen } from '@testing-library/react'; + +import type { StepFunction } from '../../../lib/step'; + +/** + * Click the first item in active call list + */ +export const ClickCurrentName: StepFunction = () => { + screen.getByTestId('currentName').click(); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/CallHistory/actions/GenerateCallHistory.tsx b/packages/ringcentral-widgets-test/test/steps/CallHistory/actions/GenerateCallHistory.tsx index 7fcafe2677..2b256a0e30 100644 --- a/packages/ringcentral-widgets-test/test/steps/CallHistory/actions/GenerateCallHistory.tsx +++ b/packages/ringcentral-widgets-test/test/steps/CallHistory/actions/GenerateCallHistory.tsx @@ -1,4 +1,4 @@ -import callLogBody from '@ringcentral-integration/commons/integration-test/mock/data/callLog.json'; +import OriginalCallLogData from '@ringcentral-integration/commons/integration-test/mock/data/callLog.json'; import type { CallLogRecord as BaseCallLogRecord } from '@ringcentral-integration/commons/modules/CallLog'; import { getISODateFrom } from '@ringcentral-integration/commons/modules/CallLog'; @@ -16,8 +16,9 @@ export const GenerateCallHistory = ({ }: { length?: number; expiredCall?: number; - mockRecords?: CallLogRecord[]; + mockRecords?: Partial[]; }) => { + const callLogBody = JSON.parse(JSON.stringify(OriginalCallLogData)); const { records } = callLogBody; const calls: CallLogRecords = []; const before8Days = getISODateFrom(DEFAULT_DAY_SPAN + 1); diff --git a/packages/ringcentral-widgets-test/test/steps/CallHistory/actions/OpenCallLog.tsx b/packages/ringcentral-widgets-test/test/steps/CallHistory/actions/OpenCallLog.tsx index 6afebcffda..aebad7e03e 100644 --- a/packages/ringcentral-widgets-test/test/steps/CallHistory/actions/OpenCallLog.tsx +++ b/packages/ringcentral-widgets-test/test/steps/CallHistory/actions/OpenCallLog.tsx @@ -1,9 +1,16 @@ +import { whenStateChange } from '@ringcentral-integration/core/test'; import { fireEvent, getByTestId, screen } from '@testing-library/react'; import type { StepFunction } from '../../../lib/step'; export const OpenCallLog: StepFunction = async () => { - const callItem = screen.queryAllByTestId('calls_item_wrapper')[0]; - const logButton = getByTestId(callItem, 'log'); + // make sure the log button is enabled + const logButton = await whenStateChange(() => { + const callItem = screen.queryAllByTestId('calls_item_wrapper')[0]; + const logButton = getByTestId(callItem, 'log'); + expect(logButton).toHaveAttribute('aria-disabled', 'false'); + + return logButton; + }); fireEvent.click(logButton); }; diff --git a/packages/ringcentral-widgets-test/test/steps/CallHistory/actions/index.ts b/packages/ringcentral-widgets-test/test/steps/CallHistory/actions/index.ts index a84aaf1571..171ede0080 100644 --- a/packages/ringcentral-widgets-test/test/steps/CallHistory/actions/index.ts +++ b/packages/ringcentral-widgets-test/test/steps/CallHistory/actions/index.ts @@ -1,3 +1,4 @@ export * from './ExpandTheActionMenu'; export * from './GenerateCallHistory'; export * from './OpenCallLog'; +export * from './ClickCurrentName'; diff --git a/packages/ringcentral-widgets-test/test/steps/CallLog/actions/SaveCallLog.ts b/packages/ringcentral-widgets-test/test/steps/CallLog/actions/SaveCallLog.ts index 3857dfb542..2306d51c7d 100644 --- a/packages/ringcentral-widgets-test/test/steps/CallLog/actions/SaveCallLog.ts +++ b/packages/ringcentral-widgets-test/test/steps/CallLog/actions/SaveCallLog.ts @@ -1,4 +1,5 @@ import { fireEvent, screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const SaveCallLog: StepFunction = () => { diff --git a/packages/ringcentral-widgets-test/test/steps/CallLog/checks/CheckCallLogBaseInfo.tsx b/packages/ringcentral-widgets-test/test/steps/CallLog/checks/CheckCallLogBaseInfo.tsx index 8f2e6b0cf8..be7d2cef27 100644 --- a/packages/ringcentral-widgets-test/test/steps/CallLog/checks/CheckCallLogBaseInfo.tsx +++ b/packages/ringcentral-widgets-test/test/steps/CallLog/checks/CheckCallLogBaseInfo.tsx @@ -16,54 +16,56 @@ interface ICheckLogBaseInfoActiveParams extends ICheckLogBaseInfoSubParams { phoneNumber?: string; } -export const CheckLogBaseInfoActive: StepFunction = - async ({ name, phoneNumber, status, hasDuration }) => { - await waitFor( - () => { - const section = screen.getByTestId('logSection'); - if (name) { - expect(getByTestId(section, 'logName').textContent).toBe(name); - } +export const CheckLogBaseInfoActive: StepFunction< + ICheckLogBaseInfoActiveParams +> = async ({ name, phoneNumber, status, hasDuration }) => { + await waitFor( + () => { + const section = screen.getByTestId('logSection'); + if (name) { + expect(getByTestId(section, 'logName').textContent).toBe(name); + } - if (phoneNumber) { - expect(getByTestId(section, 'phoneNumber').textContent).toBe( - phoneNumber, - ); - } + if (phoneNumber) { + expect(getByTestId(section, 'phoneNumber').textContent).toBe( + phoneNumber, + ); + } - if (status) { - expect(getByTestId(section, 'callStatus').textContent).toBe(status); - } + if (status) { + expect(getByTestId(section, 'callStatus').textContent).toBe(status); + } - if (hasDuration) { - expect(getByTestId(section, 'subCallDuration')).toBeInTheDocument(); - } - }, - { timeout: 3000 }, - ); - }; + if (hasDuration) { + expect(getByTestId(section, 'subCallDuration')).toBeInTheDocument(); + } + }, + { timeout: 3000 }, + ); +}; -export const CheckLogBaseInfoSub: StepFunction = - async ({ name, hasDuration, hasCallStatusIcon }) => { - await waitFor( - () => { - const section = screen.getByTestId('subLogSection'); +export const CheckLogBaseInfoSub: StepFunction< + ICheckLogBaseInfoSubParams +> = async ({ name, hasDuration, hasCallStatusIcon }) => { + await waitFor( + () => { + const section = screen.getByTestId('subLogSection'); - if (name) { - expect(getByTestId(section, 'subLogName').textContent).toBe(name); - } + if (name) { + expect(getByTestId(section, 'subLogName').textContent).toBe(name); + } - if (hasDuration) { - expect(getByTestId(section, 'subCallDuration')).toBeInTheDocument(); - } + if (hasDuration) { + expect(getByTestId(section, 'subCallDuration')).toBeInTheDocument(); + } - if (hasCallStatusIcon) { - expect(getByTestId(section, 'subInfoHoldIcon')).toBeInTheDocument(); - } - }, - { timeout: 3000 }, - ); - }; + if (hasCallStatusIcon) { + expect(getByTestId(section, 'subInfoHoldIcon')).toBeInTheDocument(); + } + }, + { timeout: 3000 }, + ); +}; export const CheckLogBaseInfoSubSectionNotExist: StepFunction = async () => { await waitFor( diff --git a/packages/ringcentral-widgets-test/test/steps/Common/actions/ClickItemByDataSign.tsx b/packages/ringcentral-widgets-test/test/steps/Common/actions/ClickItemByDataSign.tsx index fc98e9931e..e46920964d 100644 --- a/packages/ringcentral-widgets-test/test/steps/Common/actions/ClickItemByDataSign.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Common/actions/ClickItemByDataSign.tsx @@ -1,14 +1,28 @@ import { screen, fireEvent, waitFor } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; interface ClickItemByDataSignProps { dataSign: string; } -export const ClickItemByDataSign: StepFunction = - async ({ dataSign }) => { - await waitFor(() => { - expect(screen.getByTestId(dataSign)).toBeInTheDocument(); - fireEvent.click(screen.getByTestId(dataSign)); - }); - }; +export const ClickItemByDataSign: StepFunction< + ClickItemByDataSignProps +> = async ({ dataSign }, { app }) => { + await waitFor(() => { + expect(screen.getByTestId(dataSign)).toBeInTheDocument(); + }); + fireEvent.click(screen.getByTestId(dataSign)); +}; + +export const ClickDataSignItemByIndex: StepFunction< + ClickItemByDataSignProps & { index?: number } +> = async ({ dataSign, index = 0 }, { app }) => { + let elm: HTMLElement | null = null; + await waitFor(() => { + const elements = screen.getAllByTestId(dataSign); + elm = elements[index]; + expect(elm).toBeInTheDocument(); + }); + fireEvent.click(elm!); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Common/actions/ClickItemByTextContent.tsx b/packages/ringcentral-widgets-test/test/steps/Common/actions/ClickItemByTextContent.tsx new file mode 100644 index 0000000000..fa7f9c8aea --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Common/actions/ClickItemByTextContent.tsx @@ -0,0 +1,28 @@ +import { screen, fireEvent, waitFor, within } from '@testing-library/react'; + +import type { StepFunction } from '../../../lib/step'; + +interface ClickItemByTextContentProps { + text: string; + getByRole?: string; +} + +export const ClickItemByTextContent: StepFunction< + ClickItemByTextContentProps +> = async ({ text, getByRole }, { app }) => { + if (getByRole) { + await waitFor(() => { + const ele = screen.getByRole(getByRole); + expect(ele).toBeInTheDocument(); + expect(within(ele).getByText(text)).toBeInTheDocument(); + }); + + fireEvent.click(within(screen.getByRole(getByRole)).getByText(text)); + } else { + await waitFor(() => { + expect(screen.getByText(text)).toBeInTheDocument(); + }); + + fireEvent.click(screen.getByText(text)); + } +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Common/actions/RefreshToken.tsx b/packages/ringcentral-widgets-test/test/steps/Common/actions/RefreshToken.tsx new file mode 100644 index 0000000000..e6a18c407e --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Common/actions/RefreshToken.tsx @@ -0,0 +1,34 @@ +import type { AvailabilityMonitor } from '@ringcentral-integration/commons/modules/AvailabilityMonitor'; + +import type { Context } from '../../../interfaces'; +import type { StepFunction } from '../../../lib/step'; + +interface RefreshTokenProps { + healthCheck?: boolean; + manualHealthCheck?: boolean; +} + +/** + * Trigger refresh auth token + * @param healthCheck indicates trigger healthCheck or not + */ +export const RefreshToken: StepFunction = async ( + { healthCheck, manualHealthCheck }, + { phone }: Context, +) => { + try { + await phone.auth.refreshToken(); + } catch (error) { + console.error('refreshToken error', error); + } + // Call directly as a workaround for faking timer + if (healthCheck || manualHealthCheck) { + try { + const availabilityMonitor: AvailabilityMonitor = + phone.availabilityMonitor; + await availabilityMonitor._healthCheck({ manual: manualHealthCheck }); + } catch (error) { + console.error('_healthCheck error', error); + } + } +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Common/actions/SelectOptionFromDropDown.tsx b/packages/ringcentral-widgets-test/test/steps/Common/actions/SelectOptionFromDropDown.tsx index 7aa5d78dfd..f02cb6b675 100644 --- a/packages/ringcentral-widgets-test/test/steps/Common/actions/SelectOptionFromDropDown.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Common/actions/SelectOptionFromDropDown.tsx @@ -1,10 +1,10 @@ import { waitForRenderReady } from '@ringcentral-integration/test-utils'; import { fireEvent, getByText, screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; -import type { DropdownDataSign } from '../interface'; export const SelectOptionFromDropDown: StepFunction<{ - dropdownSelector: DropdownDataSign; + dropdownSelector: string; targetOption: string; }> = async ({ dropdownSelector, targetOption }) => { const inputNode = screen diff --git a/packages/ringcentral-widgets-test/test/steps/Common/actions/TurnOffToggle.tsx b/packages/ringcentral-widgets-test/test/steps/Common/actions/TurnOffToggle.tsx new file mode 100644 index 0000000000..4749434195 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Common/actions/TurnOffToggle.tsx @@ -0,0 +1,16 @@ +import { screen } from '@testing-library/react'; + +import type { StepFunction } from '../../../lib/step'; +import type { RcvCheckboxDataSign } from '../../Meeting/Meeting.interface'; + +export const TurnOffToggle: StepFunction<{ + dataSign: RcvCheckboxDataSign | string; +}> = async ({ dataSign }) => { + const checkbox = screen + .getByTestId(dataSign) + .getElementsByTagName('input')[0]; + + if (checkbox.checked) { + screen.getByTestId(dataSign).click(); + } +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Common/actions/index.ts b/packages/ringcentral-widgets-test/test/steps/Common/actions/index.ts index 758e3eaf1b..b25fa170b6 100644 --- a/packages/ringcentral-widgets-test/test/steps/Common/actions/index.ts +++ b/packages/ringcentral-widgets-test/test/steps/Common/actions/index.ts @@ -1,5 +1,8 @@ -export * from './TurnOnToggle'; -export * from './SelectMenuItem'; export * from './ClickBackButton'; -export * from './SelectOptionFromDropDown'; export * from './ClickItemByDataSign'; +export * from './ClickItemByTextContent'; +export * from './RefreshToken'; +export * from './SelectMenuItem'; +export * from './SelectOptionFromDropDown'; +export * from './TurnOnToggle'; +export * from './TurnOffToggle'; diff --git a/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckDropDownExist.tsx b/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckDropDownExist.tsx index f595cef502..bfcaf7c1bb 100644 --- a/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckDropDownExist.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckDropDownExist.tsx @@ -1,4 +1,5 @@ import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; import type { DropdownDataSign } from '../interface'; diff --git a/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckDropDownList.tsx b/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckDropDownList.tsx index f32e23c5e5..5461ec4554 100644 --- a/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckDropDownList.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckDropDownList.tsx @@ -1,6 +1,6 @@ import { fireEvent, screen, waitFor, within } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; -import type { DropdownDataSign } from '../interface'; export const CheckDropDownList: StepFunction<{ options?: Array<{ @@ -8,7 +8,7 @@ export const CheckDropDownList: StepFunction<{ isDisabled?: boolean; isSelected?: boolean; }>; - dataSign: DropdownDataSign; + dataSign: string; }> = async ({ options = [], dataSign }) => { const inputNode = screen .getByTestId(dataSign) diff --git a/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckDropDownSelectedValueIs.tsx b/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckDropDownSelectedValueIs.tsx index f99c5a112b..0d76fd516b 100644 --- a/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckDropDownSelectedValueIs.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckDropDownSelectedValueIs.tsx @@ -1,4 +1,5 @@ import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; import type { DropdownDataSign } from '../interface'; diff --git a/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckEleNotExist.tsx b/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckEleNotExist.tsx new file mode 100644 index 0000000000..da62121b10 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckEleNotExist.tsx @@ -0,0 +1,10 @@ +import { screen } from '@testing-library/react'; + +import type { StepFunction } from '../../../lib/step'; + +export const CheckEleNotExist: StepFunction<{ + dataSign: string; +}> = async ({ dataSign }) => { + const dom = screen.queryByTestId(dataSign); + expect(dom).not.toBeInTheDocument(); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckMenuItemExist.tsx b/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckMenuItemExist.tsx index bf746e7271..a68fb9a126 100644 --- a/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckMenuItemExist.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckMenuItemExist.tsx @@ -1,4 +1,5 @@ import { fireEvent, screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; import type { DropdownDataSign } from '../interface'; diff --git a/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckToggleOff.tsx b/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckToggleOff.tsx new file mode 100644 index 0000000000..d45999e4a9 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckToggleOff.tsx @@ -0,0 +1,13 @@ +import { screen } from '@testing-library/react'; + +import type { StepFunction } from '../../../lib/step'; + +export const CheckToggleOff: StepFunction<{ + dataSign: string; +}> = async ({ dataSign }) => { + const checkbox = screen + .getByTestId(dataSign) + .getElementsByTagName('input')[0]; + + expect(checkbox.checked).toBe(false); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckToggleOn.tsx b/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckToggleOn.tsx new file mode 100644 index 0000000000..df2e0dd1c7 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckToggleOn.tsx @@ -0,0 +1,13 @@ +import { screen } from '@testing-library/react'; + +import type { StepFunction } from '../../../lib/step'; + +export const CheckToggleOn: StepFunction<{ + dataSign: string; +}> = async ({ dataSign }) => { + const checkbox = screen + .getByTestId(dataSign) + .getElementsByTagName('input')[0]; + + expect(checkbox.checked).toBe(true); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckTooltip.tsx b/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckTooltip.tsx index 3ab0dc1899..fbf281805d 100644 --- a/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckTooltip.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Common/checks/CheckTooltip.tsx @@ -9,18 +9,23 @@ import userEvent from '@testing-library/user-event'; export const CheckTooltip: StepFunction<{ element?: string; tooltip: string; - browserTooltip: boolean; + browserTooltip?: boolean; }> = async ({ element, tooltip, browserTooltip = false }) => { if (!browserTooltip) { - userEvent.hover(screen.getByTestId(element)); + userEvent.hover(screen.getByTestId(element!)); await waitFor( () => { expect(screen.getByRole('tooltip').textContent).toBe(tooltip); }, { timeout: 2000 }, ); - userEvent.unhover(screen.getByTestId(element)); + userEvent.unhover(screen.getByTestId(element!)); } else { - expect(screen.getByTitle(tooltip)).not.toBeNull(); + await waitFor( + () => { + expect(screen.getByTitle(tooltip)).not.toBeNull(); + }, + { timeout: 2000 }, + ); } }; diff --git a/packages/ringcentral-widgets-test/test/steps/Common/checks/index.ts b/packages/ringcentral-widgets-test/test/steps/Common/checks/index.ts index 5e2a26d318..78cde0ab9c 100644 --- a/packages/ringcentral-widgets-test/test/steps/Common/checks/index.ts +++ b/packages/ringcentral-widgets-test/test/steps/Common/checks/index.ts @@ -1,5 +1,8 @@ +export * from './CheckDropDownExist'; export * from './CheckDropDownList'; -export * from './CheckMenuItemExist'; export * from './CheckDropDownSelectedValueIs'; -export * from './CheckDropDownExist'; +export * from './CheckEleNotExist'; +export * from './CheckMenuItemExist'; +export * from './CheckToggleOff'; +export * from './CheckToggleOn'; export * from './CheckTooltip'; diff --git a/packages/ringcentral-widgets-test/test/steps/CommonLogin/CommonLogin.tsx b/packages/ringcentral-widgets-test/test/steps/CommonLogin/CommonLogin.tsx index 07a7c9895f..f72fa7557f 100644 --- a/packages/ringcentral-widgets-test/test/steps/CommonLogin/CommonLogin.tsx +++ b/packages/ringcentral-widgets-test/test/steps/CommonLogin/CommonLogin.tsx @@ -1,51 +1,83 @@ import { waitUntilTo } from '@ringcentral-integration/utils'; import { screen, waitForElementToBeRemoved } from '@testing-library/react'; + import type { StepFunction } from '../../lib/step'; import type { CreateInstanceProps } from '../CreateInstance'; -export interface LoginProps extends CreateInstanceProps { +export interface LoginProps { username?: string; password?: string; shouldWaitForConnected?: boolean; + shouldMockWebphone?: boolean; isMockUserMedia?: boolean; +} + +export interface CommonLoginProps extends LoginProps, CreateInstanceProps { CreateInstance: StepFunction; } -export const CommonLogin: StepFunction = async ( - { username = 'test', password = 'test', CreateInstance, ...options }, +export const ExecuteAuthLogin: StepFunction = async ( + { + username = 'test', + password = 'test', + shouldMockWebphone = true, + isMockUserMedia = true, + }, + context, +) => { + const { phone } = context; + await waitUntilTo(() => { + expect(phone.auth.ready).toBeTruthy(); + }); + if (isMockUserMedia) { + Object.defineProperties(phone.audioSettings, { + userMedia: { value: true }, + }); + } + expect(phone.auth.loggedIn).toBeFalsy(); + expect(screen.queryByTestId('loginButton')).toBeInTheDocument(); + await phone.auth.login({ + username, + password, + }); + await waitForElementToBeRemoved(() => screen.getByTestId('loginButton'), { + timeout: 9000, + }); + expect(phone.auth.loggedIn).toBeTruthy(); + await waitUntilTo(() => { + if (phone.activeCallControl) { + expect(phone.activeCallControl.ready).toBeTruthy(); + } + }); + if (shouldMockWebphone) { + await waitUntilTo(() => { + if (phone.call.ready) { + phone.webphone._webphone?.userAgent.trigger?.('registered'); + } + }); + } +}; + +export const CommonLogin: StepFunction = async ( + { + username = 'test', + password = 'test', + shouldMockWebphone = true, + isMockUserMedia = true, + CreateInstance, + ...options + }, context, ) => { return ( <> - - {async () => { - const { phone } = context; - await waitUntilTo(() => { - expect(phone.auth.ready).toBeTruthy(); - }); - const { isMockUserMedia = true } = options; - if (isMockUserMedia) { - Object.defineProperties(phone.audioSettings, { - userMedia: { value: true }, - }); - } - expect(phone.auth.loggedIn).toBeFalsy(); - expect(screen.queryByTestId('loginButton')).toBeInTheDocument(); - await phone.auth.login({ - username, - password, - }); - await waitForElementToBeRemoved( - () => screen.getByTestId('loginButton'), - { timeout: 9000 }, - ); - expect(phone.auth.loggedIn).toBeTruthy(); - await waitUntilTo(() => { - if (phone.activeCallControl) { - expect(phone.activeCallControl.ready).toBeTruthy(); - } - }); - }} + + ); }; diff --git a/packages/ringcentral-widgets-test/test/steps/CommonLogin/CommonLoginEntry.tsx b/packages/ringcentral-widgets-test/test/steps/CommonLogin/CommonLoginEntry.tsx index 2daa7b32f4..5da9f681bc 100644 --- a/packages/ringcentral-widgets-test/test/steps/CommonLogin/CommonLoginEntry.tsx +++ b/packages/ringcentral-widgets-test/test/steps/CommonLogin/CommonLoginEntry.tsx @@ -1,6 +1,7 @@ import type { StepFunction } from '../../lib/step'; -import { CreateMock } from '../Mock'; import { CreateInstance } from '../CreateInstance'; +import { CreateMock } from '../Mock'; + import type { LoginProps } from './CommonLogin'; import { CommonLogin } from './CommonLogin'; diff --git a/packages/ringcentral-widgets-test/test/steps/CommonLogin/checks/CheckLoginButtonExists.tsx b/packages/ringcentral-widgets-test/test/steps/CommonLogin/checks/CheckLoginButtonExists.tsx new file mode 100644 index 0000000000..e3cfff714b --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/CommonLogin/checks/CheckLoginButtonExists.tsx @@ -0,0 +1,18 @@ +import type { StepFunction } from '@ringcentral-integration/test-utils'; +import { screen } from '@testing-library/react'; + +interface CheckLoginButtonExistsProps { + exists?: boolean; +} + +export const CheckLoginButtonExists: StepFunction< + CheckLoginButtonExistsProps +> = async ({ exists = true }) => { + const loginButton = await screen.queryByTestId('loginButton'); + if (exists) { + expect(loginButton).toBeTruthy(); + expect(loginButton).toBeInTheDocument(); + } else { + expect(loginButton).toBeFalsy(); + } +}; diff --git a/packages/ringcentral-widgets-test/test/steps/CommonLogin/checks/index.ts b/packages/ringcentral-widgets-test/test/steps/CommonLogin/checks/index.ts new file mode 100644 index 0000000000..ff77fa999e --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/CommonLogin/checks/index.ts @@ -0,0 +1 @@ +export * from './CheckLoginButtonExists'; diff --git a/packages/ringcentral-widgets-test/test/steps/CommonLogin/index.ts b/packages/ringcentral-widgets-test/test/steps/CommonLogin/index.ts index a6f26d138e..5f1c6a5d4b 100644 --- a/packages/ringcentral-widgets-test/test/steps/CommonLogin/index.ts +++ b/packages/ringcentral-widgets-test/test/steps/CommonLogin/index.ts @@ -1,2 +1,3 @@ export * from './CommonLogin'; export * from './CommonLoginEntry'; +export * from './checks'; diff --git a/packages/ringcentral-widgets-test/test/steps/ContactsView/actions/NavigateToContactDetails.tsx b/packages/ringcentral-widgets-test/test/steps/ContactsView/actions/NavigateToContactDetails.tsx deleted file mode 100644 index 1f938a5d42..0000000000 --- a/packages/ringcentral-widgets-test/test/steps/ContactsView/actions/NavigateToContactDetails.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { fireEvent, screen } from '@testing-library/react'; - -import type { StepFunction } from '../../../lib/step'; - -export const NavigateToContactDetails: StepFunction<{ - userName: string; -}> = async ({ userName }) => { - const contactItem = await screen.findByTitle(userName, {}, { timeout: 2000 }); - fireEvent.click(contactItem); - - const header = await screen.findByTestId('headerTitle'); - expect(header).toHaveTextContent('Contact Details'); -}; diff --git a/packages/ringcentral-widgets-test/test/steps/ContactsView/actions/SelectContactSourceFilter.tsx b/packages/ringcentral-widgets-test/test/steps/ContactsView/actions/SelectContactSourceFilter.tsx index 8f34e38389..4c9f5cc181 100644 --- a/packages/ringcentral-widgets-test/test/steps/ContactsView/actions/SelectContactSourceFilter.tsx +++ b/packages/ringcentral-widgets-test/test/steps/ContactsView/actions/SelectContactSourceFilter.tsx @@ -3,9 +3,10 @@ import { fireEvent, screen, within } from '@testing-library/react'; import type { StepFunction } from '../../../lib/step'; -export const SelectContactSourceFilter: StepFunction<{ filter: string }> = - async ({ filter }) => { - const contactSourceList = screen.getByTestId('contactSourceList'); - const option = within(contactSourceList).getByText(filter); - fireEvent.click(option); - }; +export const SelectContactSourceFilter: StepFunction<{ + filter: string; +}> = async ({ filter }) => { + const contactSourceList = screen.getByTestId('contactSourceList'); + const option = within(contactSourceList).getByText(filter); + fireEvent.click(option); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/ContactsView/actions/index.ts b/packages/ringcentral-widgets-test/test/steps/ContactsView/actions/index.ts index 5e9f0df125..1d32389f48 100644 --- a/packages/ringcentral-widgets-test/test/steps/ContactsView/actions/index.ts +++ b/packages/ringcentral-widgets-test/test/steps/ContactsView/actions/index.ts @@ -1,4 +1,3 @@ export * from './ClickContactSourceFilterButton'; export * from './SelectContactSourceFilter'; -export * from './NavigateToContactDetails'; export * from './SearchContacts'; diff --git a/packages/ringcentral-widgets-test/test/steps/ContactsView/checks/CheckContactDetailsProfile.tsx b/packages/ringcentral-widgets-test/test/steps/ContactsView/checks/CheckContactDetailsProfile.tsx index 25ddfb8d41..acd189ad8c 100644 --- a/packages/ringcentral-widgets-test/test/steps/ContactsView/checks/CheckContactDetailsProfile.tsx +++ b/packages/ringcentral-widgets-test/test/steps/ContactsView/checks/CheckContactDetailsProfile.tsx @@ -1,35 +1,38 @@ import { waitUntilTo } from '@ringcentral-integration/utils'; import { screen, within } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; + import type { ContactDetailsProps } from './ContactDetailsProps.interface'; interface CheckContactDetailsProfileProps extends ContactDetailsProps {} -export const CheckContactDetailsProfile: StepFunction = - async ({ userName, isActive = true }) => { - await waitUntilTo(() => { - expect(screen.getByLabelText('profile')).toBeVisible(); - }); - const profileContainer = screen.getByLabelText('profile'); - if (!isActive) { - expect(within(profileContainer).getByText('Inactive')).toBeVisible(); - expect(within(profileContainer).getByTitle(userName)).toHaveAttribute( - 'data-inactive', - 'true', - ); - expect(within(profileContainer).getByTestId('profile')).toHaveAttribute( - 'data-inactive', - 'true', - ); - } else { - expect(within(profileContainer).getByText('Inactive')).not.toBeVisible(); - expect(within(profileContainer).getByTitle(userName)).toHaveAttribute( - 'data-inactive', - 'false', - ); - expect(within(profileContainer).getByTestId('profile')).toHaveAttribute( - 'data-inactive', - 'false', - ); - } - }; +export const CheckContactDetailsProfile: StepFunction< + CheckContactDetailsProfileProps +> = async ({ userName, isActive = true }) => { + await waitUntilTo(() => { + expect(screen.getByLabelText('profile')).toBeVisible(); + }); + const profileContainer = screen.getByLabelText('profile'); + if (!isActive) { + expect(within(profileContainer).getByText('Inactive')).toBeVisible(); + expect(within(profileContainer).getByTitle(userName)).toHaveAttribute( + 'data-inactive', + 'true', + ); + expect(within(profileContainer).getByTestId('profile')).toHaveAttribute( + 'data-inactive', + 'true', + ); + } else { + expect(within(profileContainer).getByText('Inactive')).not.toBeVisible(); + expect(within(profileContainer).getByTitle(userName)).toHaveAttribute( + 'data-inactive', + 'false', + ); + expect(within(profileContainer).getByTestId('profile')).toHaveAttribute( + 'data-inactive', + 'false', + ); + } +}; diff --git a/packages/ringcentral-widgets-test/test/steps/ContactsView/checks/CheckContactGroupCaption.tsx b/packages/ringcentral-widgets-test/test/steps/ContactsView/checks/CheckContactGroupCaption.tsx index 879248fdb9..6e7b97445c 100644 --- a/packages/ringcentral-widgets-test/test/steps/ContactsView/checks/CheckContactGroupCaption.tsx +++ b/packages/ringcentral-widgets-test/test/steps/ContactsView/checks/CheckContactGroupCaption.tsx @@ -1,4 +1,5 @@ import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const CheckContactGroupCaption: StepFunction<{ diff --git a/packages/ringcentral-widgets-test/test/steps/ContactsView/checks/CheckContactItemExistInList.tsx b/packages/ringcentral-widgets-test/test/steps/ContactsView/checks/CheckContactItemExistInList.tsx index b36620e562..f4f355f28f 100644 --- a/packages/ringcentral-widgets-test/test/steps/ContactsView/checks/CheckContactItemExistInList.tsx +++ b/packages/ringcentral-widgets-test/test/steps/ContactsView/checks/CheckContactItemExistInList.tsx @@ -1,39 +1,41 @@ import { screen, within } from '@testing-library/react'; import type { StepFunction } from '../../../lib/step'; + import type { ContactDetailsProps } from './ContactDetailsProps.interface'; interface CheckContactItemExistInListProps extends ContactDetailsProps {} -export const CheckContactItemExistInList: StepFunction = - async ({ userName, extensionNumber, isActive }) => { - if (userName) { - const contactItem = await screen.findByTitle(userName); - expect(contactItem).toBeVisible(); +export const CheckContactItemExistInList: StepFunction< + CheckContactItemExistInListProps +> = async ({ userName, extensionNumber, isActive }) => { + if (userName) { + const contactItem = await screen.findByTitle(userName); + expect(contactItem).toBeVisible(); - if (isActive === undefined) { - // do nothing - } else if (isActive === false) { - expect(screen.getByText('Inactive')).toBeVisible(); - expect(contactItem).toHaveAttribute('data-inactive', 'true'); - expect(screen.getByTestId('profile')).toHaveAttribute( - 'data-inactive', - 'true', - ); - } else { - expect(screen.getByText('Inactive')).not.toBeVisible(); - expect(contactItem).toHaveAttribute('data-inactive', 'false'); - expect(screen.getByTestId('profile')).toHaveAttribute( - 'data-inactive', - 'false', - ); - } + if (isActive === undefined) { + // do nothing + } else if (isActive === false) { + expect(screen.getByText('Inactive')).toBeVisible(); + expect(contactItem).toHaveAttribute('data-inactive', 'true'); + expect(screen.getByTestId('profile')).toHaveAttribute( + 'data-inactive', + 'true', + ); + } else { + expect(screen.getByText('Inactive')).not.toBeVisible(); + expect(contactItem).toHaveAttribute('data-inactive', 'false'); + expect(screen.getByTestId('profile')).toHaveAttribute( + 'data-inactive', + 'false', + ); + } - expect(contactItem.parentElement).not.toBeNull(); - if (extensionNumber && contactItem.parentElement) { - expect( - within(contactItem.parentElement).queryByTitle(extensionNumber), - ).toBeInTheDocument(); - } + expect(contactItem.parentElement).not.toBeNull(); + if (extensionNumber && contactItem.parentElement) { + expect( + within(contactItem.parentElement).queryByTitle(extensionNumber), + ).toBeInTheDocument(); } - }; + } +}; diff --git a/packages/ringcentral-widgets-test/test/steps/ContactsView/checks/CheckContactSourceList.tsx b/packages/ringcentral-widgets-test/test/steps/ContactsView/checks/CheckContactSourceList.tsx index 715618a5b3..a741955633 100644 --- a/packages/ringcentral-widgets-test/test/steps/ContactsView/checks/CheckContactSourceList.tsx +++ b/packages/ringcentral-widgets-test/test/steps/ContactsView/checks/CheckContactSourceList.tsx @@ -1,4 +1,5 @@ import { screen, within } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; const DEFAULT_CONTACT_FILTER_OPTIONS = ['All', 'Company', 'Personal']; diff --git a/packages/ringcentral-widgets-test/test/steps/ContactsView/checks/CheckInContactDetailsPage.tsx b/packages/ringcentral-widgets-test/test/steps/ContactsView/checks/CheckInContactDetailsPage.tsx index ae54c56904..92b16dee88 100644 --- a/packages/ringcentral-widgets-test/test/steps/ContactsView/checks/CheckInContactDetailsPage.tsx +++ b/packages/ringcentral-widgets-test/test/steps/ContactsView/checks/CheckInContactDetailsPage.tsx @@ -1,20 +1,22 @@ import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; interface CheckInContactDetailsPageProps { userName?: string; } -export const CheckInContactDetailsPage: StepFunction = - async ({ userName }) => { - const headers = screen.queryAllByTestId('headerTitle'); - const myHeader = headers.find((header) => - header.textContent?.includes('Contact Details'), - ); - expect(myHeader).toHaveTextContent('Contact Details'); +export const CheckInContactDetailsPage: StepFunction< + CheckInContactDetailsPageProps +> = async ({ userName }) => { + const headers = screen.queryAllByTestId('headerTitle'); + const myHeader = headers.find((header) => + header.textContent?.includes('Contact Details'), + ); + expect(myHeader).toHaveTextContent('Contact Details'); - if (userName) { - const detailsName = await screen.findByTestId('contactName'); - expect(detailsName).toHaveTextContent(userName); - } - }; + if (userName) { + const detailsName = await screen.findByTestId('contactName'); + expect(detailsName).toHaveTextContent(userName); + } +}; diff --git a/packages/ringcentral-widgets-test/test/steps/ContactsView/checks/CheckSearchContactInput.tsx b/packages/ringcentral-widgets-test/test/steps/ContactsView/checks/CheckSearchContactInput.tsx index 75f3e2065e..e77ef08855 100644 --- a/packages/ringcentral-widgets-test/test/steps/ContactsView/checks/CheckSearchContactInput.tsx +++ b/packages/ringcentral-widgets-test/test/steps/ContactsView/checks/CheckSearchContactInput.tsx @@ -1,4 +1,5 @@ import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const CheckSearchContactInput: StepFunction<{ diff --git a/packages/ringcentral-widgets-test/test/steps/Conversation/CheckConversations.ts b/packages/ringcentral-widgets-test/test/steps/Conversation/CheckConversations.ts index c7cf03c93a..522df6e918 100644 --- a/packages/ringcentral-widgets-test/test/steps/Conversation/CheckConversations.ts +++ b/packages/ringcentral-widgets-test/test/steps/Conversation/CheckConversations.ts @@ -1,5 +1,6 @@ import { waitUntilTo } from '@ringcentral-integration/utils'; import { screen, within } from '@testing-library/react'; + import type { StepFunction } from '../../lib/step'; interface CheckFaxPageCountProps { @@ -24,21 +25,22 @@ interface CheckConversationsProps { testId: string; } -export const CheckConversations: StepFunction = - async ({ parsedNumber, name, testId }) => { - await waitUntilTo(() => { - expect(screen.getByTestId(testId)).toBeInTheDocument(); - if (name) { - expect(screen.getByText(name)).toBeInTheDocument(); - expect( - within(screen.getByTestId(testId)).getByTitle( - `${name} | RingCentral ${parsedNumber}`, - ), - ).toBeInTheDocument(); - } else { - expect( - within(screen.getByTestId(testId)).getByTitle(parsedNumber), - ).toBeVisible(); - } - }); - }; +export const CheckConversations: StepFunction< + CheckConversationsProps +> = async ({ parsedNumber, name, testId }) => { + await waitUntilTo(() => { + expect(screen.getByTestId(testId)).toBeInTheDocument(); + if (name) { + expect(screen.getByText(name)).toBeInTheDocument(); + expect( + within(screen.getByTestId(testId)).getByTitle( + `${name} | RingCentral ${parsedNumber}`, + ), + ).toBeInTheDocument(); + } else { + expect( + within(screen.getByTestId(testId)).getByTitle(parsedNumber), + ).toBeVisible(); + } + }); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Conversation/CheckMessageNumbers.ts b/packages/ringcentral-widgets-test/test/steps/Conversation/CheckMessageNumbers.ts index 8077f2a722..2d347803a5 100644 --- a/packages/ringcentral-widgets-test/test/steps/Conversation/CheckMessageNumbers.ts +++ b/packages/ringcentral-widgets-test/test/steps/Conversation/CheckMessageNumbers.ts @@ -1,11 +1,12 @@ import { waitUntilTo } from '@ringcentral-integration/utils'; import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../lib/step'; export const CheckMessageNumbers: StepFunction<{ count: number; dataSign?: string; -}> = async ({ count, dataSign = 'Messages' }) => { +}> = async ({ count, dataSign = 'messagesTab' }) => { await waitUntilTo(() => { expect(screen.getByTestId(dataSign)).toHaveTextContent(`${count}`); }); diff --git a/packages/ringcentral-widgets-test/test/steps/CreateInstance.ts b/packages/ringcentral-widgets-test/test/steps/CreateInstance.ts index 2f5df2ad7d..f99ca92506 100644 --- a/packages/ringcentral-widgets-test/test/steps/CreateInstance.ts +++ b/packages/ringcentral-widgets-test/test/steps/CreateInstance.ts @@ -1,18 +1,18 @@ -import { createStore } from 'redux'; import SimulateWindowObject from '@ringcentral-integration/commons/integration-test/utils/SimulateWindowObject'; import { renderComponent } from '@ringcentral-integration/test-utils'; -import { brandConfig } from '@ringcentral-integration/widgets-demo/dev-server/brandConfig'; -import App from '@ringcentral-integration/widgets-demo/dev-server/containers/App'; // @ts-ignore import RcIcon from '@ringcentral-integration/widgets-demo/dev-server/Icon.svg'; // @ts-ignore import { createPhone } from '@ringcentral-integration/widgets-demo/dev-server/Phone'; +import { brandConfig } from '@ringcentral-integration/widgets-demo/dev-server/brandConfig'; +import App from '@ringcentral-integration/widgets-demo/dev-server/containers/App'; // @ts-ignore import prefix from '@ringcentral-integration/widgets-demo/dev-server/prefix'; // @ts-ignore import version from '@ringcentral-integration/widgets-demo/dev-server/version'; +import { createStore } from 'redux'; + import type { StepFunction } from '../lib/step'; -import { connectionStatus } from '@ringcentral-integration/commons/modules/Webphone'; const apiConfig = { appKey: 'test key', @@ -25,7 +25,6 @@ export interface CreateInstanceProps { shouldMockWebphone?: boolean; shouldMockDevices?: boolean; shouldInitMock?: boolean; - enableWebphone?: boolean; } export const CreateInstance: StepFunction = async ( @@ -33,7 +32,6 @@ export const CreateInstance: StepFunction = async ( shouldMockWebphone = true, shouldMockDevices = true, shouldInitMock = true, - enableWebphone = true, }, { rcMock }, ) => { @@ -43,6 +41,7 @@ export const CreateInstance: StepFunction = async ( SimulateWindowObject(shouldMockDevices); const phone = createPhone({ ...brandConfig, + analyticsKey: 'xxx', apiConfig, brandConfig, prefix: `${prefix}-${Date.now()}`, // storage with different prefix in different case @@ -53,15 +52,7 @@ export const CreateInstance: StepFunction = async ( const store = createStore(phone.reducer); phone.setStore(store); phone.connectivityMonitor._checkConnectionFunc = () => true; - if (enableWebphone) { - phone.webphone._setConnectionStatus(connectionStatus.connected); - } - if (shouldMockWebphone && phone.webphone.connecting) { - phone.webphone._webphone?.userAgent.trigger?.('registered'); - } - phone.webphone._createWebphone(); phone.webphone._removeWebphone = () => {}; - phone.webphone._connect = () => {}; const app = renderComponent( App, { phone, icon: RcIcon }, diff --git a/packages/ringcentral-widgets-test/test/steps/Dropdown/CheckDropdown.tsx b/packages/ringcentral-widgets-test/test/steps/Dropdown/CheckDropdown.tsx new file mode 100644 index 0000000000..3a12663d55 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Dropdown/CheckDropdown.tsx @@ -0,0 +1,11 @@ +import { screen, StepFunction } from '@ringcentral-integration/test-utils'; + +interface CheckDropdownProps { + appName: string; +} + +export const CheckDropdown: StepFunction = ({ + appName, +}) => { + expect(screen.getByText(appName)).toBeInTheDocument(); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Dropdown/ExpandDropdown.tsx b/packages/ringcentral-widgets-test/test/steps/Dropdown/ExpandDropdown.tsx new file mode 100644 index 0000000000..7c2eeb72fc --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Dropdown/ExpandDropdown.tsx @@ -0,0 +1,19 @@ +import { + fireEvent, + screen, + StepFunction, +} from '@ringcentral-integration/test-utils'; + +interface ExpandDropdownProps { + testId: string; +} + +export const ExpandDropdown: StepFunction = ({ + testId, +}) => { + const callingSettingElement = screen.getByTestId(testId); + const selectRoot = callingSettingElement.querySelector( + '[data-sign="selectRoot"]', + ); + fireEvent.click(selectRoot!); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Dropdown/index.ts b/packages/ringcentral-widgets-test/test/steps/Dropdown/index.ts new file mode 100644 index 0000000000..6e3d6bf7e0 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Dropdown/index.ts @@ -0,0 +1,2 @@ +export * from './ExpandDropdown'; +export * from './CheckDropdown'; diff --git a/packages/ringcentral-widgets-test/test/steps/Environment/actions/SetDataTrackingEnable.tsx b/packages/ringcentral-widgets-test/test/steps/Environment/actions/SetDataTrackingEnable.tsx new file mode 100644 index 0000000000..51bc436821 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Environment/actions/SetDataTrackingEnable.tsx @@ -0,0 +1,25 @@ +import { screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { Context } from '../../../interfaces'; +import type { StepFunction } from '../../../lib/step'; + +export const SetDataTrackingEnable: StepFunction<{ + enable: boolean; +}> = ({ enable }, { phone }: Context) => { + if (enable) { + phone.analytics.mixpanel.init('xxx'); + phone.analytics.setUserId(); + } + // enable + const toggleElem = screen.getByTestId('dataTrackingToggle'); + expect(toggleElem).toBeInTheDocument(); + if (toggleElem.querySelector('input')!.checked !== enable) { + userEvent.click(toggleElem); + } + + // save + const saveElem = screen.getByTestId('envSave'); + expect(saveElem).toBeInTheDocument(); + userEvent.click(saveElem); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Environment/actions/SetEnvironmentOptions.tsx b/packages/ringcentral-widgets-test/test/steps/Environment/actions/SetEnvironmentOptions.tsx index 37e1467efd..abfd46c17b 100644 --- a/packages/ringcentral-widgets-test/test/steps/Environment/actions/SetEnvironmentOptions.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Environment/actions/SetEnvironmentOptions.tsx @@ -1,8 +1,7 @@ +import type { SDKConfig } from '@ringcentral-integration/commons/lib/createSdkConfig'; import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import type { SDKConfig } from '@ringcentral-integration/commons/lib/createSdkConfig'; - import type { Context } from '../../../interfaces'; import type { StepFunction } from '../../../lib/step'; diff --git a/packages/ringcentral-widgets-test/test/steps/Environment/checks/CheckEnvironmentOptions.tsx b/packages/ringcentral-widgets-test/test/steps/Environment/checks/CheckEnvironmentOptions.tsx index 5902a160b9..513019e10b 100644 --- a/packages/ringcentral-widgets-test/test/steps/Environment/checks/CheckEnvironmentOptions.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Environment/checks/CheckEnvironmentOptions.tsx @@ -5,8 +5,19 @@ import type { StepFunction } from '../../../lib/step'; export const CheckEnvironmentOptions: StepFunction<{ server?: string; -}> = ({ server }, context: Context) => { - const serverElem = screen.getByTestId('envServerUrl'); - expect(serverElem).toBeTruthy(); + enableDataTracking?: boolean; +}> = ({ server, enableDataTracking }, context: Context) => { + const serverElem = screen.queryByTestId('envServerUrl'); + expect(serverElem).toBeInTheDocument(); expect(serverElem).toHaveValue(server); + + if (enableDataTracking !== undefined) { + const dataTrackingElem = screen.queryByTestId('dataTrackingToggle')!; + expect(dataTrackingElem).toBeInTheDocument(); + if (enableDataTracking) { + expect(dataTrackingElem.querySelector('input')).toBeChecked(); + } else { + expect(dataTrackingElem.querySelector('input')).not.toBeChecked(); + } + } }; diff --git a/packages/ringcentral-widgets-test/test/steps/Ha/actions/ClickConnectivityBadge.tsx b/packages/ringcentral-widgets-test/test/steps/Ha/actions/ClickConnectivityBadge.tsx new file mode 100644 index 0000000000..758004554b --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Ha/actions/ClickConnectivityBadge.tsx @@ -0,0 +1,15 @@ +import { screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { StepFunction } from '../../../lib/step'; + +export const ClickConnectivityBadge: StepFunction<{ + textContent?: string; +}> = async ({ textContent }) => { + const connectivityBadge = await screen.getByTestId('ConnectivityBadge'); + expect(connectivityBadge).toBeInTheDocument(); + if (connectivityBadge && textContent) { + expect(connectivityBadge.textContent).toBe(textContent); + } + userEvent.click(connectivityBadge); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Ha/actions/index.ts b/packages/ringcentral-widgets-test/test/steps/Ha/actions/index.ts new file mode 100644 index 0000000000..de43fc3a79 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Ha/actions/index.ts @@ -0,0 +1 @@ +export * from './ClickConnectivityBadge'; diff --git a/packages/ringcentral-widgets-test/test/steps/Ha/checks/CheckConnectivityBadge.tsx b/packages/ringcentral-widgets-test/test/steps/Ha/checks/CheckConnectivityBadge.tsx new file mode 100644 index 0000000000..46342b58d3 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Ha/checks/CheckConnectivityBadge.tsx @@ -0,0 +1,27 @@ +import { screen } from '@testing-library/react'; + +import type { StepFunction } from '../../../lib/step'; + +interface CheckConnectivityBadgeProps { + exists?: boolean; + textContent?: string; +} + +/** + * Check connectivity badge display or not and what is the text content in the badge + * @param exists boolean value for indicating to check exists of not + * @param textContent string value for checking the badge text when the badge exists + */ +export const CheckConnectivityBadge: StepFunction< + CheckConnectivityBadgeProps +> = async ({ exists = true, textContent }) => { + const connectivityBadge = await screen.queryByTestId('ConnectivityBadge'); + if (exists) { + expect(connectivityBadge).toBeInTheDocument(); + if (connectivityBadge && textContent) { + expect(connectivityBadge.textContent).toBe(textContent); + } + } else { + expect(connectivityBadge).not.toBeInTheDocument(); + } +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Ha/checks/index.ts b/packages/ringcentral-widgets-test/test/steps/Ha/checks/index.ts new file mode 100644 index 0000000000..18bf91498d --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Ha/checks/index.ts @@ -0,0 +1 @@ +export * from './CheckConnectivityBadge'; diff --git a/packages/ringcentral-widgets-test/test/steps/Ha/index.ts b/packages/ringcentral-widgets-test/test/steps/Ha/index.ts new file mode 100644 index 0000000000..2b782a6470 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Ha/index.ts @@ -0,0 +1,3 @@ +export * from './actions'; +export * from './checks'; +export * from './mocks'; diff --git a/packages/ringcentral-widgets-test/test/steps/Ha/mocks/MockCheckConnection.tsx b/packages/ringcentral-widgets-test/test/steps/Ha/mocks/MockCheckConnection.tsx new file mode 100644 index 0000000000..0f5acd9d9e --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Ha/mocks/MockCheckConnection.tsx @@ -0,0 +1,34 @@ +import type { ConnectivityMonitor } from '@ringcentral-integration/commons/modules/ConnectivityMonitor'; + +import type { Context } from '../../../interfaces'; +import type { StepFunction } from '../../../lib/step'; + +interface MockCheckConnectionProps { + handler: () => Promise; +} + +export const MockCheckConnection: StepFunction< + MockCheckConnectionProps +> = async ({ handler }, { phone }: Context) => { + const connectivityMonitor: ConnectivityMonitor = phone.connectivityMonitor; + if (!connectivityMonitor) { + throw new Error('connectivityMonitor is required'); + } + + /** + * The original function is replaced in for test environment + * integration-apps/ringcentral-js-widgets/ringcentral-widgets-test/test/steps/CreateInstance.ts + * We bring it back and add custom handler to mock its request behaviors. + **/ + connectivityMonitor._checkConnectionFunc = async () => { + try { + await handler(); + connectivityMonitor._requestSuccessHandler(); + } catch (error: any) { + connectivityMonitor._requestErrorHandler(error); + } + }; + + // Trigger checking connection + await connectivityMonitor._checkConnection(); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Ha/mocks/MockGetStatus.ts b/packages/ringcentral-widgets-test/test/steps/Ha/mocks/MockGetStatus.ts new file mode 100644 index 0000000000..69cdd10d98 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Ha/mocks/MockGetStatus.ts @@ -0,0 +1,35 @@ +import type { StepFunction } from '../../../lib/step'; + +interface MockGetStatusProps { + handler?: (mockData: any) => any; + status?: 200 | 503; + repeat?: 0 | 1; +} + +/** + * Mock get status API + * @param handler optional handler for API response + * @param status optional http-code for API response + * @param repeat optional repeat for fetch-mock + */ +export const MockGetStatus: StepFunction = ( + { repeat, status = 200, handler }, + { rcMock }, +) => { + function mock() { + rcMock.get('/restapi/v1.0/status' as any, status, { + repeat, + response: ({ mockData }) => { + return { + body: handler?.(mockData) ?? mockData, + }; + }, + }); + } + + if (rcMock.initialized) { + mock(); + } else { + rcMock.defaultInitMocks.add(mock); + } +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Ha/mocks/MockNetworkOffline.tsx b/packages/ringcentral-widgets-test/test/steps/Ha/mocks/MockNetworkOffline.tsx new file mode 100644 index 0000000000..6e09854d94 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Ha/mocks/MockNetworkOffline.tsx @@ -0,0 +1,29 @@ +import type { ConnectivityMonitor } from '@ringcentral-integration/commons/modules/ConnectivityMonitor'; + +import type { Context } from '../../../interfaces'; +import type { StepFunction } from '../../../lib/step'; + +export const MockNetworkOffline: StepFunction = ( + _: unknown, + context: Context, +) => { + const connectivityMonitor: ConnectivityMonitor = + context.phone.connectivityMonitor; + if (!connectivityMonitor) { + throw new Error('connectivityMonitor is required'); + } + + const payload = context.payload as any; + if (!payload._requestSuccessHandler) { + // Store original function + payload._requestSuccessHandler = connectivityMonitor._requestSuccessHandler; + // Stop API client from bringing it back to online + connectivityMonitor._unbindHandlers?.(); + connectivityMonitor._requestSuccessHandler = jest.fn(); + connectivityMonitor._bindHandlers(); + } + + // Event dispatch + const event = new Event('offline'); + window.dispatchEvent(event); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Ha/mocks/MockNetworkOnline.tsx b/packages/ringcentral-widgets-test/test/steps/Ha/mocks/MockNetworkOnline.tsx new file mode 100644 index 0000000000..87dc3ebfa9 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Ha/mocks/MockNetworkOnline.tsx @@ -0,0 +1,32 @@ +import type { ConnectivityMonitor } from '@ringcentral-integration/commons/modules/ConnectivityMonitor'; + +import type { Context } from '../../../interfaces'; +import type { StepFunction } from '../../../lib/step'; + +export const MockNetworkOnline: StepFunction = ( + _: unknown, + context: Context, +) => { + const connectivityMonitor: ConnectivityMonitor = + context.phone.connectivityMonitor; + if (!connectivityMonitor) { + throw new Error('connectivityMonitor is required'); + } + + const payload = context.payload as any; + if (payload._requestSuccessHandler) { + // Restore the original function + connectivityMonitor._unbindHandlers?.(); + connectivityMonitor._requestSuccessHandler = payload._requestSuccessHandler; + connectivityMonitor._bindHandlers(); + // Clean cache + delete payload._requestSuccessHandler; + } + + // Event dispatch + const event = new Event('online'); + window.dispatchEvent(event); + + // Notify + connectivityMonitor._requestSuccessHandler(); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Ha/mocks/index.ts b/packages/ringcentral-widgets-test/test/steps/Ha/mocks/index.ts new file mode 100644 index 0000000000..68a31749ce --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Ha/mocks/index.ts @@ -0,0 +1,4 @@ +export * from './MockCheckConnection'; +export * from './MockGetStatus'; +export * from './MockNetworkOffline'; +export * from './MockNetworkOnline'; diff --git a/packages/ringcentral-widgets-test/test/steps/IDB/checks/CheckEula.tsx b/packages/ringcentral-widgets-test/test/steps/IDB/checks/CheckEula.tsx index dbc9ccf530..13a16e2701 100644 --- a/packages/ringcentral-widgets-test/test/steps/IDB/checks/CheckEula.tsx +++ b/packages/ringcentral-widgets-test/test/steps/IDB/checks/CheckEula.tsx @@ -1,4 +1,5 @@ import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const CheckEula: StepFunction<{ diff --git a/packages/ringcentral-widgets-test/test/steps/Login.tsx b/packages/ringcentral-widgets-test/test/steps/Login.tsx index ac3d53bbc2..0089978f84 100644 --- a/packages/ringcentral-widgets-test/test/steps/Login.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Login.tsx @@ -1,4 +1,5 @@ import type { StepFunction } from '../lib/step'; + import { CommonLogin } from './CommonLogin'; import { CreateInstance } from './CreateInstance'; import { CreateMock, MockExtensionInfo, MockGetPhoneNumber } from './Mock'; @@ -8,12 +9,17 @@ interface LoginProps { password?: string; shouldWaitForConnected?: boolean; reLogin?: boolean; + skipCreateMock?: boolean; } -const Login: StepFunction = async (props, context) => { +export const Login: StepFunction = async (props, context) => { return ( <> - + {props.skipCreateMock + ? () => { + // + } + : CreateMock} = async (props, context) => { ); }; - -export { Login }; diff --git a/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckInvitation.tsx b/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckInvitation.tsx index f98fa10720..f1eecb61d6 100644 --- a/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckInvitation.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckInvitation.tsx @@ -1,4 +1,5 @@ import type dialInNumbersBody from '@ringcentral-integration/mock/src/platform/data/dialInNumbers.json'; + import type { StepFunction } from '../../../lib/step'; export const CheckInvitation: StepFunction = async (_, { phone, rcMock }) => { @@ -11,7 +12,7 @@ export const CheckInvitation: StepFunction = async (_, { phone, rcMock }) => { const rcvMeeting = await rcMock.fetchMock ?.lastResponse(new RegExp('.*/rcvideo/v1/bridges'), 'POST') ?.json(); - expect(phone.rcVideo.getMeetingInvitation).toBeCalledWith({ + expect(phone.rcVideo.getMeetingInvitation).toHaveBeenCalledWith({ brandId: phone.brand.id, brandName: phone.brand.name, currentLocale: phone.locale.currentLocale, diff --git a/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckPasswordField.tsx b/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckPasswordField.tsx index 764e786947..caf5b94a29 100644 --- a/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckPasswordField.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckPasswordField.tsx @@ -1,4 +1,5 @@ import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const CheckPasswordIsValid: StepFunction = async () => { diff --git a/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckRCMPage.tsx b/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckRCMPage.tsx index ed323dce37..1e6cb49bbe 100644 --- a/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckRCMPage.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckRCMPage.tsx @@ -1,3 +1,4 @@ +import { whenStateOrTimerChange } from '@ringcentral-integration/core/test'; import type { StepFunction } from '@ringcentral-integration/test-utils'; import { screen } from '@testing-library/react'; @@ -21,13 +22,15 @@ export const RcmOptionIsLocked: StepFunction<{ dataSign: string; isLocked: boolean; }> = async ({ dataSign, isLocked }) => { - const lockIcon = screen.queryByTestId(`${dataSign}_lock`); + await whenStateOrTimerChange(() => { + const lockIcon = screen.queryByTestId(`${dataSign}_lock`); - if (isLocked) { - expect(lockIcon).toBeInTheDocument(); - } else { - expect(lockIcon).not.toBeInTheDocument(); - } + if (isLocked) { + expect(lockIcon).toBeInTheDocument(); + } else { + expect(lockIcon).not.toBeInTheDocument(); + } + }); }; export const CheckDropDownValue: StepFunction<{ diff --git a/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckRCMPageDisplay.tsx b/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckRCMPageDisplay.tsx index b283b3369c..6e778d27f3 100644 --- a/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckRCMPageDisplay.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckRCMPageDisplay.tsx @@ -1,7 +1,8 @@ import { waitUntilTo } from '@ringcentral-integration/commons/utils'; import { screen } from '@testing-library/react'; -import { WaitForSpinner } from '../../WaitForSpinner'; + import type { StepFunction } from '../../../lib/step'; +import { WaitForSpinner } from '../../WaitForSpinner'; export const CheckRCMPageDisplay: StepFunction<{ timeout?: number }> = async ( props, diff --git a/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckRCVPage.tsx b/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckRCVPage.tsx index c21b4fdda6..ea836a2bbe 100644 --- a/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckRCVPage.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckRCVPage.tsx @@ -1,8 +1,9 @@ -import { screen } from '@testing-library/react'; import { waitUntilTo } from '@ringcentral-integration/commons/utils'; +import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; -import type { RcvCheckboxDataSign } from '../Meeting.interface'; import { WaitForSpinner } from '../../WaitForSpinner'; +import type { RcvCheckboxDataSign } from '../Meeting.interface'; export const CheckChangePmiConfirmButton: StepFunction<{ isShown: boolean; @@ -50,7 +51,7 @@ export const CheckItemLabel: StepFunction<{ }; export const CheckboxIsChecked: StepFunction<{ - dataSign: RcvCheckboxDataSign; + dataSign: string; isChecked: boolean; }> = async ({ dataSign, isChecked }) => { const checkbox = screen diff --git a/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckRcmRequestParams.tsx b/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckRcmRequestParams.tsx index 96cd1c055c..624b85adb9 100644 --- a/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckRcmRequestParams.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckRcmRequestParams.tsx @@ -1,4 +1,5 @@ import type { MeetingResponseResource } from '@ringcentral-integration/mock'; + import type { StepFunction } from '../../../lib/step'; type MeetingResponseResourceKey = keyof MeetingResponseResource; diff --git a/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckRcvMeetingSettingsValues.tsx b/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckRcvMeetingSettingsValues.tsx index da3f4aa3e4..c442b7b623 100644 --- a/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckRcvMeetingSettingsValues.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckRcvMeetingSettingsValues.tsx @@ -1,4 +1,5 @@ import type { StepFunction } from '../../../lib/step'; + import { CheckPasswordInputNotExist, CheckPasswordValue, diff --git a/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckRcvRequestParams.tsx b/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckRcvRequestParams.tsx index e8d5f3b6ec..b67b964d04 100644 --- a/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckRcvRequestParams.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Meeting/Check/CheckRcvRequestParams.tsx @@ -2,6 +2,7 @@ import type { RcVideoAPI, RcVSettingKey, } from '@ringcentral-integration/commons/interfaces/Rcv.model'; + import type { StepFunction } from '../../../lib/step'; export const CheckPostMeetingParams: StepFunction> = async ( @@ -18,14 +19,15 @@ export const CheckPostMeetingParams: StepFunction> = async ( }); }; -export const CheckPatchMeetingParams: StepFunction> = - async ({ children, ...params }, { phone, rcMock }) => { - const [_, request] = rcMock.fetchMock.lastCall( - new RegExp('.*/rcvideo/v1/bridges'), - { method: 'patch' }, - ); - const requestObj = JSON.parse(request.body as string); - Object.keys(params).forEach((key) => { - expect(requestObj[key]).toEqual(params[key as RcVSettingKey]); - }); - }; +export const CheckPatchMeetingParams: StepFunction< + Partial +> = async ({ children, ...params }, { phone, rcMock }) => { + const [_, request] = rcMock.fetchMock.lastCall( + new RegExp('.*/rcvideo/v1/bridges'), + { method: 'patch' }, + ); + const requestObj = JSON.parse(request.body as string); + Object.keys(params).forEach((key) => { + expect(requestObj[key]).toEqual(params[key as RcVSettingKey]); + }); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Meeting/Mocks/MockPostBridges.ts b/packages/ringcentral-widgets-test/test/steps/Meeting/Mocks/MockPostBridges.ts new file mode 100644 index 0000000000..236d406363 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Meeting/Mocks/MockPostBridges.ts @@ -0,0 +1,23 @@ +import type postRcvBridges from '@ringcentral-integration/mock/src/platform/data/postRcvBridges.json'; + +import type { StepFunction } from '../../../lib/step'; + +interface MockPostBridgesProps { + handler?: (bridge: typeof postRcvBridges) => typeof postRcvBridges; + repeat?: number; +} + +export const MockPostBridges: StepFunction = async ( + { handler, repeat }, + { rcMock }, +) => { + const mock = () => { + rcMock.postBridges(repeat, handler); + }; + if (rcMock.initialized) { + mock(); + } else { + rcMock.defaultInitMocks.delete(rcMock.postBridges); + rcMock.defaultInitMocks.add(mock); + } +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Meeting/Mocks/index.ts b/packages/ringcentral-widgets-test/test/steps/Meeting/Mocks/index.ts new file mode 100644 index 0000000000..6a46280c64 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Meeting/Mocks/index.ts @@ -0,0 +1 @@ +export * from './MockPostBridges'; diff --git a/packages/ringcentral-widgets-test/test/steps/Meeting/Operate/OperateFooter.tsx b/packages/ringcentral-widgets-test/test/steps/Meeting/Operate/OperateFooter.tsx index ff8060ad02..bfd02780a6 100644 --- a/packages/ringcentral-widgets-test/test/steps/Meeting/Operate/OperateFooter.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Meeting/Operate/OperateFooter.tsx @@ -1,8 +1,11 @@ +import { whenStateOrTimerChange } from '@ringcentral-integration/core/test'; import { screen } from '@testing-library/react'; import type { StepFunction } from '../../../lib/step'; export const ClickRemoveButton: StepFunction = async () => { - const removeButton = screen.getByTestId('removeButton'); - removeButton.click(); + await whenStateOrTimerChange(() => { + const removeButton = screen.getByTestId('removeButton'); + removeButton.click(); + }); }; diff --git a/packages/ringcentral-widgets-test/test/steps/Meeting/Operate/OperatePasswordField.tsx b/packages/ringcentral-widgets-test/test/steps/Meeting/Operate/OperatePasswordField.tsx index 398592a80e..e8a46c018f 100644 --- a/packages/ringcentral-widgets-test/test/steps/Meeting/Operate/OperatePasswordField.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Meeting/Operate/OperatePasswordField.tsx @@ -1,5 +1,5 @@ -import userEvent from '@testing-library/user-event'; import { fireEvent, screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const FocusOnPasswordField: StepFunction = async () => { @@ -14,15 +14,15 @@ export const BlurPasswordField: StepFunction = async () => { export const EnterRandomPassword: StepFunction = async () => { const password = Math.floor(Math.random() * 10e6).toString(); - fireEvent.change(screen.getByPlaceholderText('Enter Password'), { - target: { value: password }, - }); + return ; }; export const EnterPassword: StepFunction<{ password: string }> = async ({ password, }) => { - fireEvent.change(screen.getByPlaceholderText('Enter Password'), { + const element = + screen.getByPlaceholderText('Enter Password'); + fireEvent.change(element, { target: { value: password }, }); }; diff --git a/packages/ringcentral-widgets-test/test/steps/Meeting/Operate/OperateRCMPage.tsx b/packages/ringcentral-widgets-test/test/steps/Meeting/Operate/OperateRCMPage.tsx index 5736868b0b..643080da5e 100644 --- a/packages/ringcentral-widgets-test/test/steps/Meeting/Operate/OperateRCMPage.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Meeting/Operate/OperateRCMPage.tsx @@ -1,6 +1,6 @@ -import { screen } from '@testing-library/react'; import { waitForRenderReady } from '@ringcentral-integration/test-utils'; import type { StepFunction } from '@ringcentral-integration/test-utils'; +import { screen } from '@testing-library/react'; export const ClickChangePMIAlert: StepFunction = async () => { screen.getByTestId('setPmiConfirm').click(); diff --git a/packages/ringcentral-widgets-test/test/steps/Meeting/Operate/OperateRCVPage.tsx b/packages/ringcentral-widgets-test/test/steps/Meeting/Operate/OperateRCVPage.tsx index 35818050ae..d1604b2337 100644 --- a/packages/ringcentral-widgets-test/test/steps/Meeting/Operate/OperateRCVPage.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Meeting/Operate/OperateRCVPage.tsx @@ -1,11 +1,14 @@ +import { whenStateChange } from '@ringcentral-integration/core/test'; +import postRcvBridgesBody from '@ringcentral-integration/mock/src/platform/data/postRcvBridges.json'; +import { waitForRenderReady } from '@ringcentral-integration/test-utils'; import { screen, fireEvent, getByText } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { waitForRenderReady } from '@ringcentral-integration/test-utils'; -import postRcvBridgesBody from '@ringcentral-integration/mock/src/platform/data/postRcvBridges.json'; -import type { RcvCheckboxDataSign } from '../Meeting.interface'; -import { SelectOptionFromDropDown } from '../../Common'; + import type { Context } from '../../../interfaces'; import type { StepFunction } from '../../../lib/step'; +import { SelectOptionFromDropDown } from '../../Common'; +import type { RcvCheckboxDataSign } from '../Meeting.interface'; + import { EnterRandomPassword } from './OperatePasswordField'; export const ClickCancelOnPopup: StepFunction = async () => { @@ -23,31 +26,37 @@ export const SwitchUsePersonalMeetingId: StepFunction = async () => { export const SwitchToggle: StepFunction<{ dataSign: RcvCheckboxDataSign; }> = async ({ dataSign }) => { - screen.getByTestId(dataSign).click(); + await whenStateChange(() => { + screen.getByTestId(dataSign).click(); + }); }; export const TurnOnToggle: StepFunction<{ dataSign: RcvCheckboxDataSign; }> = async ({ dataSign }) => { - const checkbox = screen - .getByTestId(dataSign) - .getElementsByTagName('input')[0]; - - if (!checkbox.checked) { - screen.getByTestId(dataSign).click(); - } + await whenStateChange(() => { + const checkbox = screen + .getByTestId(dataSign) + .getElementsByTagName('input')[0]; + + if (!checkbox.checked) { + screen.getByTestId(dataSign).click(); + } + }); }; export const TurnOffToggle: StepFunction<{ dataSign: RcvCheckboxDataSign; }> = async ({ dataSign }) => { - const checkbox = screen - .getByTestId(dataSign) - .getElementsByTagName('input')[0]; - - if (checkbox.checked) { - screen.getByTestId(dataSign).click(); - } + await whenStateChange(() => { + const checkbox = screen + .getByTestId(dataSign) + .getElementsByTagName('input')[0]; + + if (checkbox.checked) { + screen.getByTestId(dataSign).click(); + } + }); }; export const SwitchToggleTo: StepFunction<{ diff --git a/packages/ringcentral-widgets-test/test/steps/Meeting/index.ts b/packages/ringcentral-widgets-test/test/steps/Meeting/index.ts index 4efb1fb7ad..5304bf4ad1 100644 --- a/packages/ringcentral-widgets-test/test/steps/Meeting/index.ts +++ b/packages/ringcentral-widgets-test/test/steps/Meeting/index.ts @@ -1,3 +1,4 @@ export * from './Check'; +export * from './Mocks'; export * from './Operate'; export * from './WaitForMeetingProvider'; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/actions/ClearMessage.ts b/packages/ringcentral-widgets-test/test/steps/Messages/actions/ClearMessage.ts index f4098ca7a5..2732139414 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/actions/ClearMessage.ts +++ b/packages/ringcentral-widgets-test/test/steps/Messages/actions/ClearMessage.ts @@ -1,6 +1,7 @@ import { waitForRenderReady } from '@ringcentral-integration/test-utils'; import { screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; + import { runInFakeTimer } from '../../../lib/runInFakeTimer'; import type { StepFunction } from '../../../lib/step'; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/actions/ClickActionButton.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/actions/ClickActionButton.tsx index 6f7acb3e40..446b4415f2 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/actions/ClickActionButton.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Messages/actions/ClickActionButton.tsx @@ -1,3 +1,4 @@ +import { whenStateChange } from '@ringcentral-integration/core/test'; import type { StepFunction } from '@ringcentral-integration/test-utils'; import { act, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; @@ -6,11 +7,18 @@ interface ClickActionButtonProps { testId: string; } -export const ClickActionButton: StepFunction = ({ +export const ClickActionButton: StepFunction = async ({ testId, }) => { - act(() => { + const actionButton = await whenStateChange(() => { const actionButton = screen.getByTestId(testId); + if (actionButton.getAttribute('aria-disabled')) { + expect(actionButton).toHaveAttribute('aria-disabled', 'false'); + } + return actionButton; + }); + + act(() => { userEvent.click(actionButton); }); }; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/actions/ClickConversationPageHyperLink.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/actions/ClickConversationPageHyperLink.tsx new file mode 100644 index 0000000000..6daeba31e1 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Messages/actions/ClickConversationPageHyperLink.tsx @@ -0,0 +1,10 @@ +import type { StepFunction } from '@ringcentral-integration/test-utils'; +import { fireEvent, screen } from '@ringcentral-integration/test-utils'; + +export const ClickConversationPageHyperLink: StepFunction = async () => { + const subjectDom = screen.getByTestId('message'); + const links = subjectDom.querySelectorAll('a') as any; + links.forEach((ele: HTMLElement) => { + fireEvent.click(ele); + }); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/actions/ClickMessageItem.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/actions/ClickMessageItem.tsx index 30b18143a3..7ac8aafff5 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/actions/ClickMessageItem.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Messages/actions/ClickMessageItem.tsx @@ -1,7 +1,8 @@ -import { fireEvent, screen } from '@testing-library/react'; +import { fireEvent, screen } from '@ringcentral-integration/test-utils'; + import type { StepFunction } from '../../../lib/step'; -export const ClickMessageItem: StepFunction = async () => { +export const ClickMessageItem: StepFunction = async (_, { phone }) => { const smsItem = screen.getAllByTestId('unread')[0]; fireEvent.click(smsItem); }; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/actions/ClickMessageItemAndBack.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/actions/ClickMessageItemAndBack.tsx index bd1eb65e58..59724bdb06 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/actions/ClickMessageItemAndBack.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Messages/actions/ClickMessageItemAndBack.tsx @@ -1,5 +1,6 @@ import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; + import type { StepFunction } from '../../../lib/step'; export const ClickMessageItemAndBack: StepFunction = async () => { diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/actions/EntryTheLongestCharacter.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/actions/EntryTheLongestCharacter.tsx index f706739fc0..2682556a93 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/actions/EntryTheLongestCharacter.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Messages/actions/EntryTheLongestCharacter.tsx @@ -1,14 +1,16 @@ import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; + import type { StepFunction } from '../../../lib/step'; interface EntryTheLongestCharacterProps { longest: number; dataSign: string; } -export const EntryTheLongestCharacter: StepFunction = - ({ longest, dataSign }) => { - const selector = screen.getByTestId(dataSign); +export const EntryTheLongestCharacter: StepFunction< + EntryTheLongestCharacterProps +> = ({ longest, dataSign }) => { + const selector = screen.getByTestId(dataSign); - userEvent.type(selector, new Array(longest + 1).join('0')); - }; + userEvent.type(selector, new Array(longest + 1).join('0')); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/actions/ExpandTheActionMenu.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/actions/ExpandTheActionMenu.tsx index aa51cd55e8..7ebe399036 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/actions/ExpandTheActionMenu.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Messages/actions/ExpandTheActionMenu.tsx @@ -1,10 +1,13 @@ +import { whenStateOrTimerChange } from '@ringcentral-integration/core/test'; import type { StepFunction } from '@ringcentral-integration/test-utils'; import { act, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; export const ExpandTheActionMenu: StepFunction = async () => { - act(() => { - const extendButton = screen.getAllByTestId('extendButton')[0]; - userEvent.click(extendButton); + await whenStateOrTimerChange(() => { + act(() => { + const extendButton = screen.getAllByTestId('extendButton')[0]; + userEvent.click(extendButton); + }); }); }; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/actions/FocusOnRecipients.ts b/packages/ringcentral-widgets-test/test/steps/Messages/actions/FocusOnRecipients.ts new file mode 100644 index 0000000000..5572bb2eb3 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Messages/actions/FocusOnRecipients.ts @@ -0,0 +1,17 @@ +import { screen, within } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { StepFunction } from '../../../lib/step'; + +/** + * Focus on 'To' field input + */ +export const FocusOnRecipients: StepFunction<{ + containerSelector?: string; +}> = async ({ containerSelector }) => { + let container = screen; + if (containerSelector) { + container = within(screen.queryByTestId(containerSelector)!); + } + userEvent.click(container.getByTestId('recipientsInput')); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/actions/InputRecipients.ts b/packages/ringcentral-widgets-test/test/steps/Messages/actions/InputRecipients.ts index aff0db3a10..8965c3ef83 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/actions/InputRecipients.ts +++ b/packages/ringcentral-widgets-test/test/steps/Messages/actions/InputRecipients.ts @@ -1,10 +1,16 @@ -import { screen } from '@testing-library/react'; +import { screen, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; + import type { StepFunction } from '../../../lib/step'; -export const InputRecipients: StepFunction<{ content: string }> = async ({ - content, -}) => { - userEvent.type(screen.getByTestId('recipientsInput'), content); - expect(screen.getByTestId('recipientsInput')).toHaveValue(content); +export const InputRecipients: StepFunction<{ + content: string; + containerSelector?: string; +}> = async ({ content, containerSelector }) => { + let container = screen; + if (containerSelector) { + container = within(screen.queryByTestId(containerSelector)!); + } + userEvent.type(container.getByTestId('recipientsInput'), content); + expect(container.getByTestId('recipientsInput')).toHaveValue(content); }; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/actions/InputSMS.ts b/packages/ringcentral-widgets-test/test/steps/Messages/actions/InputSMS.ts index 07239f3ea9..4fa113f26f 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/actions/InputSMS.ts +++ b/packages/ringcentral-widgets-test/test/steps/Messages/actions/InputSMS.ts @@ -1,6 +1,7 @@ import { act, fireEvent, screen, waitFor } from '@testing-library/react'; import type { StepFunction } from '../../../lib/step'; + import type { MessageProps } from './MessageProps.interface'; interface InputSMSProps extends MessageProps {} diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/actions/PasteMessageRecipients.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/actions/PasteMessageRecipients.tsx new file mode 100644 index 0000000000..e826ef2e97 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Messages/actions/PasteMessageRecipients.tsx @@ -0,0 +1,17 @@ +import { fireEvent, screen } from '@testing-library/react'; + +import type { StepFunction } from '../../../lib/step'; + +interface PasteMessageRecipientsProps { + pasteData: string; +} + +export const PasteMessageRecipients: StepFunction< + PasteMessageRecipientsProps +> = async ({ pasteData }) => { + const recipientsInput = + screen.getByTestId('recipientsInput'); + fireEvent.paste(recipientsInput, { + clipboardData: { getData: () => pasteData }, + }); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/actions/ReceiveOneNewConversation.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/actions/ReceiveOneNewConversation.tsx index a994f4243b..f40419fa8c 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/actions/ReceiveOneNewConversation.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Messages/actions/ReceiveOneNewConversation.tsx @@ -2,6 +2,7 @@ import type { GetMessageInfoResponse, GetMessageSyncResponse, } from '@ringcentral-integration/mock'; + import type { StepFunction } from '../../../lib/step'; type ReceiveOneNewSmsProps = { @@ -52,6 +53,8 @@ export const ReceiveOneNewSms: StepFunction = async ( _conversationId?.toString() || firstRecord.conversationId.toString(), }, + // by default, set record to delivered + messageStatus: 'Delivered', }, ]; }); diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/actions/ReceiveSMSSuccess.ts b/packages/ringcentral-widgets-test/test/steps/Messages/actions/ReceiveSMSSuccess.ts index bc2d6f1f59..f70e50195c 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/actions/ReceiveSMSSuccess.ts +++ b/packages/ringcentral-widgets-test/test/steps/Messages/actions/ReceiveSMSSuccess.ts @@ -1,5 +1,6 @@ -import type { StepFunction } from '../../../lib/step'; import { mockMessageListData } from '../../../__mock__'; +import type { StepFunction } from '../../../lib/step'; + import type { MessageProps } from './MessageProps.interface'; interface IReceiveSMSSuccess extends MessageProps { diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/actions/SendSMS.ts b/packages/ringcentral-widgets-test/test/steps/Messages/actions/SendSMS.ts index 575a447ad2..aafaf99b48 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/actions/SendSMS.ts +++ b/packages/ringcentral-widgets-test/test/steps/Messages/actions/SendSMS.ts @@ -1,5 +1,7 @@ import { fireEvent, screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; + import { InputSMS } from './InputSMS'; import type { MessageProps } from './MessageProps.interface'; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/actions/SendSMSSuccess.ts b/packages/ringcentral-widgets-test/test/steps/Messages/actions/SendSMSSuccess.ts index 5f4df72725..59130d53d4 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/actions/SendSMSSuccess.ts +++ b/packages/ringcentral-widgets-test/test/steps/Messages/actions/SendSMSSuccess.ts @@ -1,6 +1,8 @@ import { waitFor } from '@testing-library/react'; -import type { StepFunction } from '../../../lib/step'; + import { generateMessageRecord, mockMessageListData } from '../../../__mock__'; +import type { StepFunction } from '../../../lib/step'; + import type { MessageProps } from './MessageProps.interface'; import { SendSMS } from './SendSMS'; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/actions/TypeCharacter.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/actions/TypeCharacter.tsx index 8f324b29c3..61945a3794 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/actions/TypeCharacter.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Messages/actions/TypeCharacter.tsx @@ -1,4 +1,5 @@ import { screen, fireEvent } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; interface ITypeCharacter { diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/actions/TypingWordingInSearch.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/actions/TypingWordingInSearch.tsx index 23f45f6145..1dde63be13 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/actions/TypingWordingInSearch.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Messages/actions/TypingWordingInSearch.tsx @@ -1,5 +1,9 @@ -import { fireEvent, screen } from '@testing-library/react'; -import type { StepFunction } from '../../../lib/step'; +import { + fireEvent, + screen, + StepFunction, + WaitForRenderReady, +} from '@ringcentral-integration/test-utils'; interface TypingWordingInSearchProps { chars?: string; @@ -7,19 +11,28 @@ interface TypingWordingInSearchProps { dataSign: string; } -export const TypingWordingInSearch: StepFunction = - async ({ chars = '', wait = true, dataSign }) => { - const input = screen.getByTestId(dataSign); - fireEvent.focus(input); +export const TypingWordingInSearch: StepFunction< + TypingWordingInSearchProps +> = async ({ chars = '', wait = true, dataSign }, { app }) => { + const input = screen.getByTestId(dataSign); + fireEvent.focus(input); - if (wait) { - jest.useFakeTimers(); - } + if (wait) { + jest.useFakeTimers(); + } - fireEvent.change(input, { target: { value: chars } }); + fireEvent.change(input, { target: { value: chars } }); - if (wait) { - jest.advanceTimersByTime(1000); - jest.useRealTimers(); - } - }; + if (wait) { + jest.advanceTimersByTime(1000); + jest.useRealTimers(); + } + + // two wait fro render for async event + return ( + <> + + + + ); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/actions/index.ts b/packages/ringcentral-widgets-test/test/steps/Messages/actions/index.ts index af58263c67..b3dbc41eb0 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/actions/index.ts +++ b/packages/ringcentral-widgets-test/test/steps/Messages/actions/index.ts @@ -1,15 +1,18 @@ export * from './ClearMessage'; export * from './ClearSMSMock'; export * from './ClickActionButton'; +export * from './ClickConversationPageHyperLink'; export * from './ClickMessageItem'; export * from './ClickMessageItemAndBack'; export * from './ClickModalActionButton'; export * from './ClickSelectContactMenu'; export * from './EntryTheLongestCharacter'; export * from './ExpandTheActionMenu'; +export * from './FocusOnRecipients'; export * from './InputRecipients'; export * from './InputSMS'; export * from './MessageProps.interface'; +export * from './PasteMessageRecipients'; export * from './ReceiveOneNewConversation'; export * from './ReceiveSMSSuccess'; export * from './SendSMS'; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckActionMenu.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckActionMenu.tsx index 62e80dc95f..979b19f3ad 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckActionMenu.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckActionMenu.tsx @@ -1,3 +1,4 @@ +import { whenStateChange } from '@ringcentral-integration/core/test'; import type { StepFunction } from '@ringcentral-integration/test-utils'; import { waitForRenderReady } from '@ringcentral-integration/test-utils'; import { screen } from '@testing-library/react'; @@ -14,23 +15,26 @@ export const CheckActionMenu: StepFunction = async ({ expectShowEntityButton = null, expectShowCallButton = null, }) => { - await waitForRenderReady(); - const entityButton = screen.queryByTestId('View Details'); - if (expectShowEntityButton) { - expect(entityButton).toBeInTheDocument(); - } else { - expect(entityButton).not.toBeInTheDocument(); - } - const callButton = screen.queryByTestId('Call'); - if (expectShowCallButton) { - expect(callButton).toBeInTheDocument(); - } else { - expect(callButton).not.toBeInTheDocument(); - } + await whenStateChange(() => { + const entityButton = screen.queryByTestId('View Details'); + if (expectShowEntityButton) { + expect(entityButton).toBeInTheDocument(); + } else { + expect(entityButton).not.toBeInTheDocument(); + } + const callButton = screen.queryByTestId('Call'); + if (expectShowCallButton) { + expect(callButton).toBeInTheDocument(); + } else { + expect(callButton).not.toBeInTheDocument(); + } + }); }; -export const CheckActionMenuToolTip: StepFunction = - async ({ tooltip }) => { - await waitForRenderReady(); +export const CheckActionMenuToolTip: StepFunction< + ICheckActionMenuToolTip +> = async ({ tooltip }) => { + await whenStateChange(() => { const item = screen.queryByTitle(tooltip); expect(item).toBeInTheDocument(); - }; + }); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckActionsAfterSearch.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckActionsAfterSearch.tsx index 4602b03119..348dbc992c 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckActionsAfterSearch.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckActionsAfterSearch.tsx @@ -1,4 +1,5 @@ import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; interface CheckActionsAfterSearchProps { @@ -7,17 +8,18 @@ interface CheckActionsAfterSearchProps { matched: string[]; } -export const CheckActionsAfterSearch: StepFunction = - async ({ searchText, isMatched, matched }, { phone }: any) => { - screen.getByDisplayValue(searchText); - if (isMatched) { - expect(screen.getByTestId('conversationList')).not.toBeNull(); - matched?.forEach((item) => { - expect(screen.getByTitle(item)).not.toBeNull(); - }); - } else { - expect(screen.getByTestId('noMatch').textContent).toEqual( - 'No matching records found', - ); - } - }; +export const CheckActionsAfterSearch: StepFunction< + CheckActionsAfterSearchProps +> = async ({ searchText, isMatched, matched }, { phone }: any) => { + screen.getByDisplayValue(searchText); + if (isMatched) { + expect(screen.getByTestId('conversationList')).not.toBeNull(); + matched?.forEach((item) => { + expect(screen.getByTitle(item, { exact: false })).not.toBeNull(); + }); + } else { + expect(screen.getByTestId('noMatch').textContent).toEqual( + 'No matching records found', + ); + } +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckAllActionButtonsStatusWhenOffline.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckAllActionButtonsStatusWhenOffline.tsx index abe514a49b..9deee02c86 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckAllActionButtonsStatusWhenOffline.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckAllActionButtonsStatusWhenOffline.tsx @@ -1,20 +1,19 @@ import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; interface CheckAllActionButtonsStatusWhenOfflineProps { actionButtons: string[]; } -export const CheckAllActionButtonsStatusWhenOffline: StepFunction = - async ({ actionButtons }) => { - actionButtons.forEach((item) => { - if (item === 'download') { - expect(screen.getByTestId(item).className).toContain('disabled'); - } else { - expect(screen.getByTestId(item)).toHaveAttribute( - 'aria-disabled', - 'true', - ); - } - }); - }; +export const CheckAllActionButtonsStatusWhenOffline: StepFunction< + CheckAllActionButtonsStatusWhenOfflineProps +> = async ({ actionButtons }) => { + actionButtons.forEach((item) => { + if (item === 'download') { + expect(screen.getByTestId(item).className).toContain('disabled'); + } else { + expect(screen.getByTestId(item)).toHaveAttribute('aria-disabled', 'true'); + } + }); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckButton.ts b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckButton.ts index 3a971c6748..e83cb90372 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckButton.ts +++ b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckButton.ts @@ -1,4 +1,5 @@ import { screen, waitFor } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; interface CheckButtonProps { diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckClickToCallButton.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckClickToCallButton.tsx index 19324892ca..5a787a47db 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckClickToCallButton.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckClickToCallButton.tsx @@ -1,6 +1,7 @@ import { fireEvent, screen } from '@testing-library/react'; -import type { StepFunction } from '../../../lib/step'; + import type { Context } from '../../../interfaces'; +import type { StepFunction } from '../../../lib/step'; export const CheckClickToCallButton: StepFunction<{ phoneNumber: string; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckClickToSmsButton.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckClickToSmsButton.tsx index 468db81bf2..218a719299 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckClickToSmsButton.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckClickToSmsButton.tsx @@ -1,7 +1,8 @@ import { waitForRenderReady } from '@ringcentral-integration/test-utils'; import { fireEvent, screen } from '@testing-library/react'; -import type { StepFunction } from '../../../lib/step'; + import type { Context } from '../../../interfaces'; +import type { StepFunction } from '../../../lib/step'; export const CheckClickToSmsButton: StepFunction<{ phoneNumber: string; @@ -18,7 +19,7 @@ export const CheckClickToSmsButton: StepFunction<{ await waitForRenderReady(); const recipientsChip = screen - .queryByTestId('recipientsChip') + .queryByTestId('recipientItem') ?.querySelector('span'); expect(recipientsChip?.innerHTML).toEqual(phoneNumber); }; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckComposeTestPage.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckComposeTestPage.tsx index e4c7379219..92c26578b0 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckComposeTestPage.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckComposeTestPage.tsx @@ -19,7 +19,11 @@ export const CheckComposeTestPage: StepFunction = async () => { ); const selector = screen.getAllByTestId('phoneNumber')[0].querySelector('div'); - if (selector) userEvent.click(selector); + if (selector) { + userEvent.click(selector); + } else { + userEvent.click(screen.getAllByTestId('phoneNumber')[0]); + } await waitFor(() => expect(screen.queryAllByTestId('selectMenuItem')).not.toBeNull(), diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckConversationPage.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckConversationPage.tsx index 34e8f715ce..722fe6fcff 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckConversationPage.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckConversationPage.tsx @@ -1,4 +1,5 @@ import { screen, waitFor } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const CheckConversationPage: StepFunction<{ diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckConversationPageHyperLink.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckConversationPageHyperLink.tsx new file mode 100644 index 0000000000..e47106ab54 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckConversationPageHyperLink.tsx @@ -0,0 +1,18 @@ +import type { StepFunction } from '@ringcentral-integration/test-utils'; +import { screen } from '@testing-library/react'; + +interface ICheckConversationPageHyperLinkProps { + linksAmount: number; + content?: string; +} + +export const CheckConversationPageHyperLink: StepFunction< + ICheckConversationPageHyperLinkProps +> = async ({ linksAmount, content }) => { + const subjectDom = screen.getByTestId('message'); + if (content) { + expect(subjectDom).toHaveTextContent(content); + } + const links = subjectDom.querySelectorAll('a') as any; + expect(links.length).toBe(linksAmount); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckCurrentName.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckCurrentName.tsx index 8c36da3fc2..d1cebbef80 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckCurrentName.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckCurrentName.tsx @@ -1,4 +1,5 @@ import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; interface ICheckCurrentName { diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckFaxActionButtons.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckFaxActionButtons.tsx index 830493516d..e441ed3e76 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckFaxActionButtons.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckFaxActionButtons.tsx @@ -7,27 +7,32 @@ interface CheckFaxActionButtonsProps { userName?: string | null; } -export const CheckFaxActionButtons: StepFunction = - async ({ readStatus = 'Unread', direction = 'Outbound', userName = '' }) => { - const viewButton = screen.getByTitle('View'); - expect(viewButton).toBeInTheDocument(); +export const CheckFaxActionButtons: StepFunction< + CheckFaxActionButtonsProps +> = async ({ + readStatus = 'Unread', + direction = 'Outbound', + userName = '', +}) => { + const viewButton = screen.getByTitle('View'); + expect(viewButton).toBeInTheDocument(); - const downloadButton = screen.getByTitle('Download'); - expect(downloadButton).toBeInTheDocument(); + const downloadButton = screen.getByTitle('Download'); + expect(downloadButton).toBeInTheDocument(); - const deleteButton = screen.getByTitle('Delete'); - expect(deleteButton).toBeInTheDocument(); + const deleteButton = screen.getByTitle('Delete'); + expect(deleteButton).toBeInTheDocument(); - if (userName !== null) { - const detailsButton = screen.getByTitle('View Details'); - expect(detailsButton).toBeInTheDocument(); - } + if (userName !== null) { + const detailsButton = screen.getByTitle('View Details'); + expect(detailsButton).toBeInTheDocument(); + } - if (direction === 'Inbound' && readStatus) { - const markTitle = - readStatus === 'Unread' ? 'Mark as Read' : 'Mark as Unread'; + if (direction === 'Inbound' && readStatus) { + const markTitle = + readStatus === 'Unread' ? 'Mark as Read' : 'Mark as Unread'; - const markButton = screen.getAllByTitle(markTitle); - expect(markButton.length).toBeGreaterThan(0); - } - }; + const markButton = screen.getAllByTitle(markTitle); + expect(markButton.length).toBeGreaterThan(0); + } +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckInputToRecipients.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckInputToRecipients.tsx index a564f722a0..f9b400dedf 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckInputToRecipients.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckInputToRecipients.tsx @@ -1,6 +1,6 @@ import { waitForRenderReady } from '@ringcentral-integration/test-utils'; -import { screen, within, waitFor } from '@testing-library/react'; import type { StepFunction } from '@ringcentral-integration/test-utils'; +import { screen, within, waitFor } from '@testing-library/react'; export const CheckInputToRecipients: StepFunction<{ recipients: string[]; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckInvalidSmsPrompt.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckInvalidSmsPrompt.tsx index 099e82a2d7..52eefc44c7 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckInvalidSmsPrompt.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckInvalidSmsPrompt.tsx @@ -1,6 +1,7 @@ import { screen, waitFor } from '@testing-library/react'; -import type { StepFunction } from '../../../lib/step'; + import type { Context } from '../../../interfaces'; +import type { StepFunction } from '../../../lib/step'; export const CheckInvalidSmsPrompt: StepFunction = async ( props, diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckMessageListDisplay.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckMessageListDisplay.tsx index aa4d76d4af..ab784b629c 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckMessageListDisplay.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckMessageListDisplay.tsx @@ -1,4 +1,5 @@ import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; interface MessageListProps { @@ -17,18 +18,19 @@ interface MessageItemCreateTimeProps { }[]; } -export const CheckMessageListDisplayInTimeOrder: StepFunction = - ({ expectList }) => { - const messageItem = screen - .getByTestId('conversationList') - .querySelectorAll('[data-sign$="MessageItem"]'); - expect(messageItem.length).toBe(expectList.length); +export const CheckMessageListDisplayInTimeOrder: StepFunction< + MessageListProps +> = ({ expectList }) => { + const messageItem = screen + .getByTestId('conversationList') + .querySelectorAll('[data-sign$="MessageItem"]'); + expect(messageItem.length).toBe(expectList.length); - const messageList = Array.from(messageItem).map((item) => - item.getAttribute('data-sign'), - ); - expect(messageList).toEqual(expectList); - }; + const messageList = Array.from(messageItem).map((item) => + item.getAttribute('data-sign'), + ); + expect(messageList).toEqual(expectList); +}; export const CheckSMSMessageItemDisplay: StepFunction = ({ contactName, @@ -56,13 +58,14 @@ export const CheckSMSMessageItemDisplay: StepFunction = ({ ).toBeInTheDocument(); }; -export const CheckMessageCreateTimeDisplay: StepFunction = - ({ expectResult }) => { - expectResult.forEach((item) => { - expect( - screen - .getByTestId(item.testId) - .querySelector('[data-sign="msgCreateTime"]')?.innerHTML, - ).toBe(item.createTime); - }); - }; +export const CheckMessageCreateTimeDisplay: StepFunction< + MessageItemCreateTimeProps +> = ({ expectResult }) => { + expectResult.forEach((item) => { + expect( + screen + .getByTestId(item.testId) + .querySelector('[data-sign="msgCreateTime"]')?.innerHTML, + ).toBe(item.createTime); + }); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckNoMessagesDisplay.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckNoMessagesDisplay.tsx index a516922bde..5b428bdc4a 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckNoMessagesDisplay.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckNoMessagesDisplay.tsx @@ -1,4 +1,5 @@ import { screen, waitFor } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const CheckNoMessagesDisplay: StepFunction = async (props, context) => { diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckPastingAction.ts b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckPastingAction.ts index 57f221378e..eaecf6be96 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckPastingAction.ts +++ b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckPastingAction.ts @@ -1,4 +1,4 @@ -import { fireEvent, screen, waitFor } from '@testing-library/react'; +import { screen, waitFor } from '@testing-library/react'; import type { StepFunction } from '../../../lib/step'; interface CheckPastingActionProps { @@ -8,33 +8,30 @@ interface CheckPastingActionProps { searchSuccessful: boolean; } -export const CheckPastingAction: StepFunction = - async ({ pasteData, dataShowAsCard, showAtInputBox, searchSuccessful }) => { - fireEvent.paste(screen.getByTestId('recipientsInput'), { - clipboardData: { getData: () => pasteData }, - }); +export const CheckPastingAction: StepFunction< + CheckPastingActionProps +> = async ({ pasteData, dataShowAsCard, showAtInputBox, searchSuccessful }) => { + await waitFor(() => { + const input = screen.getByTestId('recipientsInput'); - await waitFor(() => { - const input = screen.getByTestId('recipientsInput'); + expect(input).toBeInTheDocument(); - expect(input).toBeInTheDocument(); + showAtInputBox + ? expect(input).toHaveValue(showAtInputBox) + : expect(input).not.toHaveValue(pasteData); - showAtInputBox - ? expect(input).toHaveValue(showAtInputBox) - : expect(input).not.toHaveValue(pasteData); + dataShowAsCard && dataShowAsCard.length + ? dataShowAsCard.every((data) => + expect(screen.getByTitle(data)).toBeVisible(), + ) + : expect(screen.queryByTitle(pasteData)).toBeNull(); + }); - dataShowAsCard && dataShowAsCard.length - ? dataShowAsCard.every((data) => - expect(screen.getByTitle(data)).toBeVisible(), - ) - : expect(screen.queryByTitle(pasteData)).toBeNull(); + if (searchSuccessful) { + await waitFor(() => { + expect(screen.getByTestId('contactDropdownList')).toBeVisible(); }); - - if (searchSuccessful) { - await waitFor(() => { - expect(screen.getByTestId('contactDropdownList')).toBeVisible(); - }); - } else { - expect(screen.queryByTestId('contactDropdownList')).toBeNull(); - } - }; + } else { + expect(screen.queryByTestId('contactDropdownList')).toBeNull(); + } +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckRecipientsInput.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckRecipientsInput.tsx index 6c2b62a5e3..ce48653b99 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckRecipientsInput.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckRecipientsInput.tsx @@ -4,16 +4,19 @@ import type { StepFunction } from '../../../lib/step'; export const CheckRecipientsInput: StepFunction = async () => { const recipientsInput = screen.getByTestId('recipientsInput') as HTMLElement; - - expect(screen.getByTestId('deleteButton')).toBeInTheDocument(); expect(recipientsInput.value).toEqual(new Array(31).join('0')); expect(recipientsInput.value.length).toBe(30); }; -export const CheckRecipientsItems: StepFunction<{ nameList: string[] }> = - async ({ nameList }) => { - const recipientsItems = screen.queryAllByTestId('recipientsChip'); - const res = recipientsItems.map((item) => item?.textContent); +export const CheckRecipientsItems: StepFunction<{ + nameList: string[]; +}> = async ({ nameList }) => { + const recipientsItems = screen.queryAllByTestId('recipientsChip'); + const res = recipientsItems.map((item) => item?.textContent); + + expect(res).toEqual(nameList); +}; - expect(res).toEqual(nameList); - }; +export const CheckRecipientsInputRemoveButton: StepFunction = async () => { + expect(screen.getByTestId('removeBtn')).toBeInTheDocument(); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckSearchInputDisplay.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckSearchInputDisplay.tsx index bbfc2b8535..dbdfa7f217 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckSearchInputDisplay.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckSearchInputDisplay.tsx @@ -1,4 +1,5 @@ import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const CheckSearchInputDisplay: StepFunction = () => { diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckUnreadCounts.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckUnreadCounts.tsx index f3583a7825..8781fccb1a 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckUnreadCounts.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckUnreadCounts.tsx @@ -1,5 +1,6 @@ import { waitUntilTo } from '@ringcentral-integration/utils'; import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; interface CheckUnreadCountsProps { diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckVoiceMailActionMenu.tsx b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckVoiceMailActionMenu.tsx index c2b85561ab..e5130de654 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckVoiceMailActionMenu.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Messages/checks/CheckVoiceMailActionMenu.tsx @@ -22,9 +22,10 @@ interface ICheckVoiceMailActionDisable { itemName: string; } -export const CheckVoiceMailActionDisable: StepFunction = - async ({ itemName }) => { - const item = screen.queryByTestId(itemName); - const disable = item!.getAttribute('aria-disabled'); - expect(disable).toBeTruthy(); - }; +export const CheckVoiceMailActionDisable: StepFunction< + ICheckVoiceMailActionDisable +> = async ({ itemName }) => { + const item = screen.queryByTestId(itemName); + const disable = item!.getAttribute('aria-disabled'); + expect(disable).toBeTruthy(); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Messages/checks/index.ts b/packages/ringcentral-widgets-test/test/steps/Messages/checks/index.ts index 905842075d..573ffafe48 100644 --- a/packages/ringcentral-widgets-test/test/steps/Messages/checks/index.ts +++ b/packages/ringcentral-widgets-test/test/steps/Messages/checks/index.ts @@ -34,3 +34,4 @@ export * from './CheckCurrentName'; export * from './CheckMessageToFieldValue'; export * from './CheckInputToRecipients'; export * from './CheckConversationHistory'; +export * from './CheckConversationPageHyperLink'; diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockAccountInfo.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockAccountInfo.ts index 8af1df0816..e089f1888b 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/MockAccountInfo.ts +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockAccountInfo.ts @@ -1,4 +1,5 @@ import type accountBody from '@ringcentral-integration/mock/src/platform/data/accountInfo.json'; + import type { StepFunction } from '../../lib/step'; interface MockAccountInfoProps { diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockActiveCall.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockActiveCall.ts index 5f0a70a7ca..d470698dba 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/MockActiveCall.ts +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockActiveCall.ts @@ -1,3 +1,5 @@ +import type { StepFunction } from '../../lib/step'; + interface MockActiveCallProps { repeat: number; } diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockAddressBookSync.tsx b/packages/ringcentral-widgets-test/test/steps/Mock/MockAddressBookSync.tsx index d971a33660..8d9c7d98f2 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/MockAddressBookSync.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockAddressBookSync.tsx @@ -1,8 +1,11 @@ +import type { PersonalContactResource } from '@ringcentral-integration/mock'; import type { ArraySchemaObject } from '@ringcentral-integration/mock/src/interface'; import type { StepFunction } from '@ringcentral-integration/test-utils'; interface MockAddressBookSyncProps { - handler?: (personalUsers: any) => any; + handler?: ( + personalUsers: PersonalContactResource[], + ) => PersonalContactResource[]; page?: number; } diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockBringInToConference.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockBringInToConference.ts index 03c70bed92..35a32ece64 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/MockBringInToConference.ts +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockBringInToConference.ts @@ -9,7 +9,8 @@ interface MockBringInToConferenceProps { repeat?: number; } -export const MockBringInToConference: StepFunction = - async ({ handler, repeat }, { rcMock }) => { - rcMock.bringInToConference(handler, repeat); - }; +export const MockBringInToConference: StepFunction< + MockBringInToConferenceProps +> = async ({ handler, repeat }, { rcMock }) => { + rcMock.bringInToConference(handler, repeat); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockCall/MockCallLogSync.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockCall/MockCallLogSync.ts index a27db27f80..722a333623 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/MockCall/MockCallLogSync.ts +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockCall/MockCallLogSync.ts @@ -1,5 +1,6 @@ import type { CallLogSync } from '@ringcentral-integration/mock'; import callLogBody from '@ringcentral-integration/mock/src/platform/data/callLog.json'; + import type { StepFunction } from '../../../lib/step'; interface MockCallLogSyncProps { diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockCall/MockGetTelephonyState.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockCall/MockGetTelephonyState.ts index 12b41e2324..1fd67d9fd1 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/MockCall/MockGetTelephonyState.ts +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockCall/MockGetTelephonyState.ts @@ -7,22 +7,23 @@ interface MockGetTelephonyStateProps { hasActiveCall?: boolean; } -export const MockGetTelephonyState: StepFunction = - async ( - { - handler = (mockData) => mockData, - hasActiveCall = false, - repeat = 0, - isDefaultInit = true, - }, - { rcMock }, - ) => { - if (!isDefaultInit) { - rcMock.getTelephonyState({ hasActiveCall, handler, repeat }); - return; - } - rcMock.defaultInitMocks.delete(rcMock.getTelephonyState); - rcMock.defaultInitMocks.add(() => { - rcMock.getTelephonyState({ hasActiveCall, handler, repeat }); - }); - }; +export const MockGetTelephonyState: StepFunction< + MockGetTelephonyStateProps +> = async ( + { + handler = (mockData) => mockData, + hasActiveCall = false, + repeat = 0, + isDefaultInit = true, + }, + { rcMock }, +) => { + if (!isDefaultInit) { + rcMock.getTelephonyState({ hasActiveCall, handler, repeat }); + return; + } + rcMock.defaultInitMocks.delete(rcMock.getTelephonyState); + rcMock.defaultInitMocks.add(() => { + rcMock.getTelephonyState({ hasActiveCall, handler, repeat }); + }); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockCall/TriggerActiveCallChanged.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockCall/TriggerActiveCallChanged.ts index fde6191022..f8e479569d 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/MockCall/TriggerActiveCallChanged.ts +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockCall/TriggerActiveCallChanged.ts @@ -1,5 +1,6 @@ import type { NormalizedSession } from '@ringcentral-integration/commons/interfaces/Webphone.interface'; import type { GetPresenceInfo } from '@ringcentral-integration/mock'; + import type { StepFunction } from '../../../lib/step'; interface TriggerActiveCallChangedProps { @@ -7,9 +8,10 @@ interface TriggerActiveCallChangedProps { handlerSessions?: (sessions: NormalizedSession[]) => NormalizedSession[]; } -export const TriggerActiveCallChanged: StepFunction = - ({ handler, handlerSessions }, { phone, rcMock }) => { - const sessions = - handlerSessions?.(phone.webphone.sessions) ?? phone.webphone.sessions; - rcMock.triggerActiveCallChanged({ sessions, handler }); - }; +export const TriggerActiveCallChanged: StepFunction< + TriggerActiveCallChangedProps +> = ({ handler, handlerSessions }, { phone, rcMock }) => { + const sessions = + handlerSessions?.(phone.webphone.sessions) ?? phone.webphone.sessions; + rcMock.triggerActiveCallChanged({ sessions, handler }); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockCall/generateCallLogData.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockCall/generateCallLogData.ts index 7c9292a91b..6522b9cb8e 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/MockCall/generateCallLogData.ts +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockCall/generateCallLogData.ts @@ -1,11 +1,11 @@ import callLogBody from '@ringcentral-integration/commons/integration-test/mock/data/callLog.json'; -import { v4 as uuidV4 } from 'uuid'; -import { mergeDeepRight, mergeRight } from 'ramda'; +import { getISODateFrom } from '@ringcentral-integration/commons/modules/CallLog'; import type { CallLogSync, UserCallLogRecord, } from '@ringcentral-integration/mock'; -import { getISODateFrom } from '@ringcentral-integration/commons/modules/CallLog'; +import { mergeDeepRight, mergeRight } from 'ramda'; +import { v4 as uuidV4 } from 'uuid'; const callLogItem = callLogBody.records[0]; const DEFAULT_DAY_SPAN = 7; diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockDefaultRingtoneSource.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockDefaultRingtoneSource.ts new file mode 100644 index 0000000000..524b1c58d6 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockDefaultRingtoneSource.ts @@ -0,0 +1,46 @@ +jest.mock( + '@ringcentral-integration/commons/modules/RingtoneConfiguration/const', + () => { + const list = [ + { id: 'phone_ring1' }, + { id: 'phone_ring2' }, + { id: 'acoustic_dreams' }, + { id: 'air_raid' }, + { id: 'allusive' }, + { id: 'attention' }, + { id: 'blub_blub' }, + { id: 'buzzy' }, + { id: 'channel_open' }, + { id: 'contemplation' }, + { id: 'crystal_ball' }, + { id: 'disco' }, + { id: 'door_bell' }, + { id: 'fairy' }, + { id: 'fast_bells' }, + { id: 'high_gong' }, + { id: 'immersion' }, + { id: 'indeed' }, + { id: 'lazy_day' }, + { id: 'neural_funk' }, + { id: 'nice' }, + { id: 'ring' }, + { id: 'ringing_bells' }, + { id: 'simple' }, + { id: 'soothing' }, + { id: 'sunshine' }, + { id: 'off' }, + ] + .map((item) => ({ + ...item, + url: item.id === 'off' ? '' : `${item.id}.mp3`, + })) + .slice(0); + return { + __esModule: true, + ...jest.requireActual( + '@ringcentral-integration/commons/modules/RingtoneConfiguration/const', + ), + DEFAULT_RINGTONE_LIST: list, + }; + }, +); diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockDialingPlan.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockDialingPlan.ts index 1148f5172b..715f36875b 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/MockDialingPlan.ts +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockDialingPlan.ts @@ -1,8 +1,11 @@ import type dialingPlanBody from '@ringcentral-integration/mock/src/platform/data/dialingPlan.json'; -import type { StepFunction } from '../lib/step'; + +import type { StepFunction } from '../../lib/step'; interface MockDialingPlanProps { - handler?: (permissions: typeof dialingPlanBody) => typeof dialingPlanBody; + handler?: ( + records: typeof dialingPlanBody.records, + ) => typeof dialingPlanBody.records; repeat?: number; } @@ -10,13 +13,18 @@ export const MockDialingPlan: StepFunction = async ( { handler, repeat }, { rcMock }, ) => { - rcMock.defaultInitMocks.delete(rcMock.getDialingPlan); - rcMock.defaultInitMocks.add(() => { + const mock = () => { rcMock.getDialingPlan((mockData) => { return { ...mockData, records: handler?.(mockData.records) ?? mockData.records, }; }, repeat); - }); + }; + + if (rcMock.initialized) { + mock(); + } else { + rcMock.replaceDefaultInitMock(rcMock.getDialingPlan, mock); + } }; diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockExtension/MockExtensionDeviceList.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockExtension/MockExtensionDeviceList.ts new file mode 100644 index 0000000000..83c2bddbaa --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockExtension/MockExtensionDeviceList.ts @@ -0,0 +1,28 @@ +import type { StepFunction } from '../../../lib/step'; + +interface MockExtensionDeviceListProps { + DLs: 'default' | 'no'; +} + +export const MockExtensionDeviceList: StepFunction< + MockExtensionDeviceListProps +> = async ({ DLs }, { rcMock }) => { + const mock = () => { + rcMock.getDevice((mockData) => { + if (DLs === 'no') { + return { + ...mockData, + records: [], + }; + } + return mockData; + }); + }; + + if (rcMock.initialized) { + mock(); + } else { + rcMock.defaultInitMocks.delete(rcMock.getDevice); + rcMock.defaultInitMocks.add(mock); + } +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockExtension/MockExtensionInfo.tsx b/packages/ringcentral-widgets-test/test/steps/Mock/MockExtension/MockExtensionInfo.tsx index fc2829865c..f760cf2b04 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/MockExtension/MockExtensionInfo.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockExtension/MockExtensionInfo.tsx @@ -1,5 +1,6 @@ import type { GetExtensionInfoResponse } from '@ringcentral-integration/mock'; import extensionInfoBody from '@ringcentral-integration/mock/src/platform/data/extensionInfo.json'; + import type { StepFunction } from '../../../lib/step'; interface MockExtensionInfoProps { diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockExtension/index.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockExtension/index.ts index 711d078c07..ce5f380269 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/MockExtension/index.ts +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockExtension/index.ts @@ -1,3 +1,4 @@ export * from './MockExtensionInfo'; export * from './MockExtensionsList'; export * from './mockExtensionsListData'; +export * from './MockExtensionDeviceList'; diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockExtension/mockExtensionsListData.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockExtension/mockExtensionsListData.ts index 10ef3fc53c..515395745a 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/MockExtension/mockExtensionsListData.ts +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockExtension/mockExtensionsListData.ts @@ -3,7 +3,7 @@ import extensionsListBody from '@ringcentral-integration/commons/integration-tes const companyContactItem = extensionsListBody.records[0]; type PhoneNumberProps = Partial< - typeof companyContactItem['phoneNumbers'] & { hidden?: boolean } + (typeof companyContactItem)['phoneNumbers'] & { hidden?: boolean } >; type CompanyContactItem = typeof companyContactItem & { diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockFeaturePermission.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockFeaturePermission.ts new file mode 100644 index 0000000000..fc02b7b21d --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockFeaturePermission.ts @@ -0,0 +1,35 @@ +import type { StepFunction } from '../../lib/step'; + +import { MockPermission } from './MockPermission'; + +interface MockFeaturePermissionProps { + featureId: string; + available: boolean; +} + +export const MockFeaturePermission: StepFunction< + MockFeaturePermissionProps +> = async ({ featureId, available }, context) => { + return MockPermission( + { + handler: (features) => { + const featureInfo = features.find( + (feature) => feature.id === featureId, + ); + if (!featureInfo) { + throw new Error(`Not found feature "${featureId}"`); + } + return features + .filter((feature) => feature.id !== featureId) + .concat([ + { + ...featureInfo, + id: featureId, + available, + }, + ]); + }, + }, + context, + ); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockGetPhoneNumber.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockGetPhoneNumber.ts index 8197f8c494..5ec048e00d 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/MockGetPhoneNumber.ts +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockGetPhoneNumber.ts @@ -1,5 +1,6 @@ -import phoneNumberBody from '@ringcentral-integration/mock/src/platform/data/phoneNumber.json'; import type { GetExtensionPhoneNumbersResponse } from '@ringcentral-integration/mock'; +import phoneNumberBody from '@ringcentral-integration/mock/src/platform/data/phoneNumber.json'; + import type { StepFunction } from '../../lib/step'; export interface MockGetPhoneNumberProps { diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockGetRingOut.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockGetRingOut.ts index aaba083d12..5443813c3c 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/MockGetRingOut.ts +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockGetRingOut.ts @@ -1,4 +1,5 @@ import type { RcMock } from '@ringcentral-integration/mock'; + import type { StepFunction } from '../../lib/step'; interface MockGetRingOutProps { diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockGetStatus.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockGetStatus.ts new file mode 100644 index 0000000000..758d2d1855 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockGetStatus.ts @@ -0,0 +1,37 @@ +import type { StepFunction } from '../../lib/step'; + +interface MockGetStatusProps { + repeat?: number; + isDefaultInit?: boolean; + retryAfter?: string; + status?: 200 | 500 | 503; + mockData?: any; +} + +const contentType = { 'Content-Type': 'application/json' }; + +function generateRetryAfter(retryAfter?: string) { + const retryAfterHeader = !retryAfter ? {} : { 'Retry-After': retryAfter }; + return retryAfterHeader; +} + +export const MockGetStatus: StepFunction = async ( + { repeat = 1, retryAfter, status = 200, mockData = {} }, + { rcMock }, +) => { + const retryAfterHeader = generateRetryAfter(retryAfter); + const setupMock = () => { + rcMock.get('/restapi/v1.0/status', status, { + repeat, + response: { + body: mockData, + headers: { + ...contentType, + ...retryAfterHeader, + }, + }, + }); + }; + + setupMock(); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockLimitedPutPresence.tsx b/packages/ringcentral-widgets-test/test/steps/Mock/MockLimitedPutPresence.tsx new file mode 100644 index 0000000000..b4e1f3c564 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockLimitedPutPresence.tsx @@ -0,0 +1,33 @@ +import type { StepFunction } from '../../lib/step'; + +interface MockPutPresenceProps { + repeat?: number; + isDefaultInit?: boolean; +} + +export const MockLimitedPutPresence: StepFunction = ( + { repeat = 1, isDefaultInit = false }, + { rcMock }, +) => { + const mock = () => { + rcMock.put( + '/restapi/v1.0/account/:accountId/extension/:extensionId/presence', + 503, + { + response: { + body: { + status: 503, + errorCode: 'CMN-211', + errors: [{ errorCode: 'CMN-211' }], + }, + }, + repeat, + }, + ); + }; + if (!isDefaultInit) { + mock(); + return; + } + rcMock.replaceDefaultInitMock(rcMock.presenceUpdate, mock); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockMessage/GenerateMessage.tsx b/packages/ringcentral-widgets-test/test/steps/Mock/MockMessage/GenerateMessage.tsx index d3fca209d5..229295d10f 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/MockMessage/GenerateMessage.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockMessage/GenerateMessage.tsx @@ -1,10 +1,10 @@ import type messageSyncBody from '@ringcentral-integration/mock/src/platform/data/messageSync.json'; import fax from './MockMessageRecords/fax.json'; -import voiceMail from './MockMessageRecords/voicemail.json'; import sms from './MockMessageRecords/sms.json'; +import voiceMail from './MockMessageRecords/voicemail.json'; -type MessageRecord = typeof messageSyncBody.records[0]; +type MessageRecord = (typeof messageSyncBody.records)[0]; const message = { Fax: fax, diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockMessage/MockCompanyPager.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockMessage/MockCompanyPager.ts index 1d3a8b17c9..9364852c14 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/MockMessage/MockCompanyPager.ts +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockMessage/MockCompanyPager.ts @@ -1,5 +1,6 @@ -import type companyPagerBody from '@ringcentral-integration/mock/src/platform/data/companyPager.json'; import type companyPagerInvalidResponse from '@ringcentral-integration/mock/src/platform//data/companyPagerInvalid.json'; +import type companyPagerBody from '@ringcentral-integration/mock/src/platform/data/companyPager.json'; + import type { StepFunction } from '../../../lib/step'; interface MockCompanyPagerProps { diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockMessage/MockMessageList.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockMessage/MockMessageList.ts index 089a78c698..88b8d46170 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/MockMessage/MockMessageList.ts +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockMessage/MockMessageList.ts @@ -1,4 +1,5 @@ import messageListBody from '@ringcentral-integration/mock/src/platform/data/messageList.json'; + import type { StepFunction } from '../../../lib/step'; interface MockMessageListProps { diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockMessage/MockMessageSync.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockMessage/MockMessageSync.ts index c37a8721e5..cb67942162 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/MockMessage/MockMessageSync.ts +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockMessage/MockMessageSync.ts @@ -3,7 +3,7 @@ import messageSyncBody from '@ringcentral-integration/mock/src/platform/data/mes import type { StepFunction } from '../../../lib/step'; interface MockMessageSyncProps { - handler?: (messageSync: any) => any; + handler?: (messageSync: typeof messageSyncBody) => typeof messageSyncBody; isDefaultInit?: boolean; useFaker?: boolean; repeat?: number; @@ -56,3 +56,42 @@ export const MockReadMessage: StepFunction<{ }, ); }; + +interface MockMessageErrorProps { + repeat?: number; + errorCode?: string; + message?: string; +} + +export const MockMessageError: StepFunction = ( + { + repeat = 1, + message = 'CMN-101', + errorCode = 'Parameter [syncToken] value is invalid.', + }, + { rcMock }, +) => { + const mock = () => + rcMock.get( + '/restapi/v1.0/account/:accountId/extension/:extensionId/message-sync', + 400, + { + response: () => { + return { + body: { + errorCode, + message, + errors: [{ errorCode, message }], + }, + }; + }, + repeat, + }, + ); + if (rcMock.initialized) { + return mock(); + } + rcMock.defaultInitMocks.delete(rcMock.getMessageSync); + rcMock.defaultInitMocks.add(mock); + rcMock.defaultInitMocks.add(rcMock.getMessageSync); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockMessage/MockPostSMS.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockMessage/MockPostSMS.ts index 5733c88655..c4354a66ce 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/MockMessage/MockPostSMS.ts +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockMessage/MockPostSMS.ts @@ -1,4 +1,5 @@ import type { GetSMSMessageInfoResponse } from '@ringcentral-integration/mock'; + import type { StepFunction } from '../../../lib/step'; interface MockPostSMSProps { diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockNumberParserV1.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockNumberParserV1.ts index f2c74ff8e6..a1e4820d9b 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/MockNumberParserV1.ts +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockNumberParserV1.ts @@ -1,4 +1,5 @@ import type parerPhoneNumbersResponse from '@ringcentral-integration/mock/src/platform/data/numberParser.json'; + import type { StepFunction } from '../../lib/step'; interface MockNumberParserProps { diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockNumberParserV2.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockNumberParserV2.ts index 3a710c4b89..44f9dd969e 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/MockNumberParserV2.ts +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockNumberParserV2.ts @@ -1,4 +1,5 @@ import type numberParserInfoBody from '@ringcentral-integration/mock/src/platform/data/numberParserV2.json'; + import type { StepFunction } from '../../lib/step'; interface MockNumberParserProps { diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockPermission.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockPermission.ts index 07ca7787de..d990d980e8 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/MockPermission.ts +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockPermission.ts @@ -1,7 +1,9 @@ +import type { FeatureInfo } from '@ringcentral-integration/mock'; + import type { StepFunction } from '../../lib/step'; interface MockPermissionProps { - handler?: (permissions: any[]) => any; + handler?: (permissions: FeatureInfo[]) => FeatureInfo[] | undefined; repeat?: number; isDefaultInit?: boolean; } diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockPersonalMeetingSettings.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockPersonalMeetingSettings.ts index 631fc34a61..d9f07c3f9b 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/MockPersonalMeetingSettings.ts +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockPersonalMeetingSettings.ts @@ -1,14 +1,16 @@ import type { RcMock } from '@ringcentral-integration/internal-mock'; + import type { StepFunction } from '../../lib/step'; export interface MockPersonalMeetingSettingsProps { handle?: Parameters[0]; } -export const MockPersonalMeetingSettings: StepFunction = - async ({ handle }, { rcMock }) => { - rcMock.defaultInitMocks.delete(rcMock.getVideoPersonalSettings); - rcMock.defaultInitMocks.add(() => { - rcMock.getVideoPersonalSettings(handle); - }); - }; +export const MockPersonalMeetingSettings: StepFunction< + MockPersonalMeetingSettingsProps +> = async ({ handle }, { rcMock }) => { + rcMock.defaultInitMocks.delete(rcMock.getVideoPersonalSettings); + rcMock.defaultInitMocks.add(() => { + rcMock.getVideoPersonalSettings(handle); + }); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockPhoneNumber.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockPhoneNumber.ts index 07eb8e01b1..35ad240ab6 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/MockPhoneNumber.ts +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockPhoneNumber.ts @@ -1,4 +1,5 @@ import type { GetExtensionPhoneNumbersResponse } from '@ringcentral-integration/mock/src/platform/interfaces'; + import type { StepFunction } from '../../lib/step'; interface MockPhoneNumberProps { diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/MockTelephonySession.ts b/packages/ringcentral-widgets-test/test/steps/Mock/MockTelephonySession.ts index 58507916dd..54f2693c8f 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/MockTelephonySession.ts +++ b/packages/ringcentral-widgets-test/test/steps/Mock/MockTelephonySession.ts @@ -9,7 +9,8 @@ interface MockTelephonySessionProps { repeat?: number; } -export const MockTelephonySession: StepFunction = - async ({ handler, repeat }, { rcMock }) => { - rcMock.getTelephonySession(handler, repeat); - }; +export const MockTelephonySession: StepFunction< + MockTelephonySessionProps +> = async ({ handler, repeat }, { rcMock }) => { + rcMock.getTelephonySession(handler, repeat); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Mock/index.ts b/packages/ringcentral-widgets-test/test/steps/Mock/index.ts index e3676a753e..c824e60616 100644 --- a/packages/ringcentral-widgets-test/test/steps/Mock/index.ts +++ b/packages/ringcentral-widgets-test/test/steps/Mock/index.ts @@ -20,3 +20,7 @@ export * from './MockActiveCall'; export * from './MockGetRingOut'; export * from './MockErrorRcvInvitation'; export * from './MockPersonalMeetingSettings'; +export * from './MockFeaturePermission'; +export * from './MockPostOauthToken'; +export * from './MockGetStatus'; +export * from './MockLimitedPutPresence'; diff --git a/packages/ringcentral-widgets-test/test/steps/Modal/CheckModalExist.ts b/packages/ringcentral-widgets-test/test/steps/Modal/CheckModalExist.ts index 30526678e7..5d4009a977 100644 --- a/packages/ringcentral-widgets-test/test/steps/Modal/CheckModalExist.ts +++ b/packages/ringcentral-widgets-test/test/steps/Modal/CheckModalExist.ts @@ -1,3 +1,4 @@ +import { whenStateOrTimerChange } from '@ringcentral-integration/core/test'; import { screen } from '@testing-library/react'; import type { StepFunction } from '../../lib/step'; @@ -5,11 +6,13 @@ import type { StepFunction } from '../../lib/step'; export const CheckModalExist: StepFunction<{ isExist?: boolean; }> = async ({ isExist = true }) => { - const modal = screen.getByRole('dialog'); + await whenStateOrTimerChange(() => { + const modal = screen.getByRole('dialog'); - if (isExist) { - expect(modal).toBeVisible(); - } else { - expect(modal).not.toBeVisible(); - } + if (isExist) { + expect(modal).toBeVisible(); + } else { + expect(modal).not.toBeVisible(); + } + }); }; diff --git a/packages/ringcentral-widgets-test/test/steps/Modal/CheckModalValue.ts b/packages/ringcentral-widgets-test/test/steps/Modal/CheckModalValue.ts index ae3cf6964f..03f0a59bf8 100644 --- a/packages/ringcentral-widgets-test/test/steps/Modal/CheckModalValue.ts +++ b/packages/ringcentral-widgets-test/test/steps/Modal/CheckModalValue.ts @@ -1,3 +1,4 @@ +import { whenStateOrTimerChange } from '@ringcentral-integration/core/test'; import { screen } from '@testing-library/react'; import type { StepFunction } from '../../lib/step'; @@ -13,26 +14,28 @@ export const CheckModalValue: StepFunction<{ confirmButtonText, cancelButtonText, }) => { - const modal = screen.getByRole('dialog'); - expect(modal).toBeVisible(); + await whenStateOrTimerChange(() => { + const modal = screen.getByRole('dialog'); + expect(modal).toBeVisible(); - expect( - modal.querySelector('[data-test-automation-id="DialogTitle"]'), - ).toHaveTextContent(title); - - if (confirmButtonText) { - expect( - modal.querySelector('[data-test-automation-id="DialogOKButton"]'), - ).toHaveTextContent(confirmButtonText); - } - if (cancelButtonText) { expect( - modal.querySelector('[data-test-automation-id="DialogCancelButton"]'), - ).toHaveTextContent(cancelButtonText); - } - if (childrenContent) { - expect( - modal.querySelector('[data-test-automation-id="DialogContent"]'), - ).toHaveTextContent(childrenContent); - } + modal.querySelector('[data-test-automation-id="DialogTitle"]'), + ).toHaveTextContent(title); + + if (confirmButtonText) { + expect( + modal.querySelector('[data-test-automation-id="DialogOKButton"]'), + ).toHaveTextContent(confirmButtonText); + } + if (cancelButtonText) { + expect( + modal.querySelector('[data-test-automation-id="DialogCancelButton"]'), + ).toHaveTextContent(cancelButtonText); + } + if (childrenContent) { + expect( + modal.querySelector('[data-test-automation-id="DialogContent"]'), + ).toHaveTextContent(childrenContent); + } + }); }; diff --git a/packages/ringcentral-widgets-test/test/steps/Modal/CheckRemoveMeetingModal.ts b/packages/ringcentral-widgets-test/test/steps/Modal/CheckRemoveMeetingModal.ts index 2d023926d5..08791dad84 100644 --- a/packages/ringcentral-widgets-test/test/steps/Modal/CheckRemoveMeetingModal.ts +++ b/packages/ringcentral-widgets-test/test/steps/Modal/CheckRemoveMeetingModal.ts @@ -1,5 +1,6 @@ -import type { StepFunction } from '../../lib/step'; import type { Context } from '../../interfaces'; +import type { StepFunction } from '../../lib/step'; + import { CheckModalValue } from './CheckModalValue'; export const CheckRemoveMeetingModal: StepFunction = async ( diff --git a/packages/ringcentral-widgets-test/test/steps/Modal/ClickCancelInModal.tsx b/packages/ringcentral-widgets-test/test/steps/Modal/ClickCancelInModal.tsx new file mode 100644 index 0000000000..170a14242d --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Modal/ClickCancelInModal.tsx @@ -0,0 +1,19 @@ +import { whenStateOrTimerChange } from '@ringcentral-integration/core/test'; +import { screen, fireEvent } from '@testing-library/react'; + +import type { StepFunction } from '../../lib/step'; + +export const ClickCancelInModal: StepFunction = async () => { + const okButton = await whenStateOrTimerChange(() => { + const okButton = screen + .getByRole('dialog') + .querySelector('[data-test-automation-id="DialogCancelButton"]'); + expect(okButton).toBeInTheDocument(); + + return okButton; + }); + + if (okButton) { + fireEvent.click(okButton); + } +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Modal/ClickConfirmInModal.tsx b/packages/ringcentral-widgets-test/test/steps/Modal/ClickConfirmInModal.tsx index ccdf7c0d65..44b29b1471 100644 --- a/packages/ringcentral-widgets-test/test/steps/Modal/ClickConfirmInModal.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Modal/ClickConfirmInModal.tsx @@ -1,12 +1,18 @@ +import { whenStateOrTimerChange } from '@ringcentral-integration/core/test'; import { screen, fireEvent } from '@testing-library/react'; import type { StepFunction } from '../../lib/step'; -export const ClickConfirmInModal: StepFunction<{}> = async () => { - const okButton = screen - .getByRole('dialog') - .querySelector('[data-test-automation-id="DialogOKButton"]'); - expect(okButton).toBeInTheDocument(); +export const ClickConfirmInModal: StepFunction = async () => { + const okButton = await whenStateOrTimerChange(() => { + const okButton = screen + .getByRole('dialog') + .querySelector('[data-test-automation-id="DialogOKButton"]'); + expect(okButton).toBeInTheDocument(); + + return okButton; + }); + if (okButton) { fireEvent.click(okButton); } diff --git a/packages/ringcentral-widgets-test/test/steps/Modal/index.ts b/packages/ringcentral-widgets-test/test/steps/Modal/index.ts index 1c67f2884f..1604f09f14 100644 --- a/packages/ringcentral-widgets-test/test/steps/Modal/index.ts +++ b/packages/ringcentral-widgets-test/test/steps/Modal/index.ts @@ -1,4 +1,5 @@ export * from './CheckModalValue'; export * from './ClickConfirmInModal'; +export * from './ClickCancelInModal'; export * from './CheckModalExist'; export * from './CheckRemoveMeetingModal'; diff --git a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToAllCalls.tsx b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToAllCalls.tsx new file mode 100644 index 0000000000..04044b73d4 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToAllCalls.tsx @@ -0,0 +1,10 @@ +import { + fireEvent, + screen, + StepFunction, +} from '@ringcentral-integration/test-utils'; + +export const NavigateToAllCalls: StepFunction = async () => { + fireEvent.click(screen.getByText('All Calls')); + return; +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToCallingSetting.tsx b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToCallingSetting.tsx index 7e3feb8b25..ab724f1d46 100644 --- a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToCallingSetting.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToCallingSetting.tsx @@ -1,5 +1,7 @@ import { fireEvent, screen, waitFor } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; + import { NavigateToSettings } from './NavigateToSettings'; const GoToCallingSetting: StepFunction = async () => { diff --git a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToComposeText.tsx b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToComposeText.tsx index 26445c6e16..c027f5d6d5 100644 --- a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToComposeText.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToComposeText.tsx @@ -1,6 +1,7 @@ import { fireEvent, screen } from '@testing-library/react'; import type { StepFunction } from '../../../lib/step'; + import { NavigateToMessagesTab } from './NavigateToMessages'; export const NavigateToComposeText: StepFunction = async (props, context) => { diff --git a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToContactDetail.tsx b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToContactDetail.tsx deleted file mode 100644 index c818ffc422..0000000000 --- a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToContactDetail.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { waitUntilTo } from '@ringcentral-integration/utils'; -import { fireEvent, screen } from '@testing-library/react'; - -import type { StepFunction } from '../../../lib/step'; - -export const NavigateToContactDetails: StepFunction<{ - userName: string; -}> = async ({ userName }) => { - await waitUntilTo(() => { - expect(screen.getByTitle(userName)).toBeInTheDocument(); - }); - const contactItem = screen.getByTitle(userName); - fireEvent.click(contactItem); - - const header = await screen.findByTestId('headerTitle'); - expect(header).toHaveTextContent('Contact Details'); -}; diff --git a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToContactDetails.tsx b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToContactDetails.tsx new file mode 100644 index 0000000000..eb61c937ad --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToContactDetails.tsx @@ -0,0 +1,28 @@ +import { waitUntilTo } from '@ringcentral-integration/utils'; +import { fireEvent, screen, within } from '@testing-library/react'; + +import type { StepFunction } from '../../../lib/step'; + +export const NavigateToContactDetails: StepFunction<{ + userName: string; +}> = async ({ userName }) => { + await waitUntilTo(() => { + expect(screen.getByTitle(userName)).toBeInTheDocument(); + }); + const contactItem = screen.getByTitle(userName); + fireEvent.click(contactItem); + + let recentActivityHeader: HTMLElement | undefined; + const recentActivityPanel = screen.queryByTestId('recentActivityPanel'); + if (recentActivityPanel) { + recentActivityHeader = + within(recentActivityPanel).getByTestId('headerTitle'); + } + + const header = screen + .queryAllByTestId('headerTitle') + .filter((node) => node !== recentActivityHeader); + + expect(header[0]).toBeInTheDocument(); + expect(header[0]).toHaveTextContent('Contact Details'); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToContacts.ts b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToContacts.ts index abc96fdd54..5173e1da3f 100644 --- a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToContacts.ts +++ b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToContacts.ts @@ -1,4 +1,5 @@ import { fireEvent, screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const NavigateToContacts: StepFunction = async (_, context) => { @@ -19,8 +20,6 @@ export const NavigateToContacts: StepFunction = async (_, context) => { let element; if (screen.queryByTestId('contactsTab')) { element = screen.getByTestId('contactsTab'); - } else if (screen.queryByTestId('Contacts')) { - element = screen.getByTestId('Contacts'); } expect(element).toBeInTheDocument(); diff --git a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToDialer.tsx b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToDialer.tsx index 143d096214..3a91fd2506 100644 --- a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToDialer.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToDialer.tsx @@ -1,16 +1,16 @@ -import { fireEvent, screen } from '@testing-library/react'; -import type { StepFunction } from '../../../lib/step'; +import { + fireEvent, + screen, + StepFunction, +} from '@ringcentral-integration/test-utils'; -export const NavigateToDialer: StepFunction = () => { - if (screen.queryByTestId('Dial Pad')) { - fireEvent.click(screen.getByTestId('Dial Pad')); - return; - } +export const NavigateToDialer: StepFunction = async () => { if (screen.queryByTestId('dialerTab')) { fireEvent.click(screen.getByTestId('dialerTab')); } -}; - -export const NavigateToDialerPage: StepFunction = () => { - fireEvent.click(screen.getByTestId('Phone')); + // When there is calls ongoing, the second level tab "dial pad" will be shown + const dialPad = screen.queryByTestId('Dial Pad'); + if (dialPad) { + fireEvent.click(dialPad); + } }; diff --git a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToFax.tsx b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToFax.tsx index e55c011710..52ac52ed5a 100644 --- a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToFax.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToFax.tsx @@ -3,8 +3,6 @@ import { fireEvent, screen } from '@testing-library/react'; import type { StepFunction } from '../../../lib/step'; export const NavigateToFax: StepFunction = async () => { - fireEvent.click( - (screen.queryByTestId('Messages') ?? screen.queryByTestId('messagesTab'))!, - ); + fireEvent.click(screen.queryByTestId('messagesTab')!); fireEvent.click(screen.getByTestId('Fax')); }; diff --git a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToHistory.tsx b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToHistory.tsx index 611687997f..f5f9d6a034 100644 --- a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToHistory.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToHistory.tsx @@ -1,5 +1,5 @@ -import { fireEvent, screen } from '@testing-library/react'; import type { RouterInteraction } from '@ringcentral-integration/widgets/modules/RouterInteraction'; +import { fireEvent, screen } from '@testing-library/react'; import type { StepFunction } from '../../../lib/step'; @@ -10,7 +10,7 @@ export const NavigateToHistory: StepFunction< routerInteraction: RouterInteraction; }; } -> = ({ testId = 'History' }, context) => { +> = ({ testId = 'historyTab' }, context) => { /** * * Note: diff --git a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToMessageHistory.ts b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToMessageHistory.ts index c10929559c..daf6a7749f 100644 --- a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToMessageHistory.ts +++ b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToMessageHistory.ts @@ -1,11 +1,13 @@ import { fireEvent, screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; interface NavigateToMessageHistoryProps { tabName: 'Fax' | 'Text' | 'All' | 'Voice'; } -export const NavigateToMessageHistory: StepFunction = - async ({ tabName = 'All' }) => { - fireEvent.click(screen.getByText(tabName)); - }; +export const NavigateToMessageHistory: StepFunction< + NavigateToMessageHistoryProps +> = async ({ tabName = 'All' }) => { + fireEvent.click(screen.getByText(tabName)); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToMessages.tsx b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToMessages.tsx index 3486c6b3f7..968497cf4c 100644 --- a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToMessages.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToMessages.tsx @@ -1,13 +1,20 @@ +import { waitForRenderReady } from '@ringcentral-integration/test-utils'; import { fireEvent, screen } from '@testing-library/react'; import type { StepFunction } from '../../../lib/step'; -export const NavigateToMessagesTab: StepFunction = async () => { - try { - // under google: the data-sign=messagesTab - fireEvent.click(screen.getByTestId('messagesTab')); - } catch (e) { - // under hubspot: the data-sign=Messages - fireEvent.click(screen.getByTestId('Messages')); +interface NavigateToMessagesTabProps { + category?: false | 'All' | 'Voice' | 'Fax' | 'Text'; +} + +export const NavigateToMessagesTab: StepFunction< + NavigateToMessagesTabProps +> = async ({ category = 'All' }) => { + fireEvent.click(screen.getByTestId('messagesTab')); + await waitForRenderReady(); + + if (category !== false) { + fireEvent.click(screen.getByTestId(category)); + await waitForRenderReady(); } }; diff --git a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToRegionSettings.tsx b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToRegionSettings.tsx index 7850640c18..7eb3d9c6e8 100644 --- a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToRegionSettings.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToRegionSettings.tsx @@ -2,8 +2,20 @@ import { fireEvent, screen, waitFor } from '@testing-library/react'; import type { StepFunction } from '../../../lib/step'; -export const NavigateToRegionSettings: StepFunction = async () => { - expect(screen.getByText('Region')).toBeInTheDocument(); - fireEvent.click(screen.getByText('Region')); - await waitFor(() => expect(screen.getByText('Country')).toBeInTheDocument()); +interface NavigateToRegionSettingsProps { + checkCountryField?: boolean; +} + +export const NavigateToRegionSettings: StepFunction< + NavigateToRegionSettingsProps +> = async ({ checkCountryField = true }) => { + const region = screen.queryByTestId('region'); + expect(region).toBeInTheDocument(); + fireEvent.click(region!); + + if (checkCountryField) { + await waitFor(() => { + expect(screen.getByText('Country')).toBeInTheDocument(); + }); + } }; diff --git a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToSMSTextTab.tsx b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToSMSTextTab.tsx index fb386aff82..895a59a452 100644 --- a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToSMSTextTab.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToSMSTextTab.tsx @@ -1,6 +1,7 @@ import { fireEvent, screen } from '@testing-library/react'; import type { StepFunction } from '../../../lib/step'; + import { NavigateToMessagesTab } from './NavigateToMessages'; export const NavigateToSMSTextTab: StepFunction = async (props, context) => { diff --git a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToSettings.tsx b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToSettings.tsx index 74fb83824a..2caa7a1eaf 100644 --- a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToSettings.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToSettings.tsx @@ -4,17 +4,20 @@ import { fireEvent, screen } from '@testing-library/react'; import type { StepFunction } from '../../../lib/step'; export const NavigateToSettings: StepFunction = async () => { - const showSettings = screen.queryByTestId('Settings'); + const showSettings = screen.queryByTestId('settingsTab'); if (showSettings) { - fireEvent.click(screen.getByTestId('Settings')); + fireEvent.click(showSettings); return; } - if (screen.queryByTestId('moreMenu')) { - fireEvent.click(screen.getByTestId('moreMenu')); + const moreMenu = screen.queryByTestId('moreMenu'); + if (moreMenu) { + fireEvent.click(moreMenu); await waitForRenderReady(); } fireEvent.click(screen.getByTestId('settingsTab')); + await waitForRenderReady(); + expect(screen.getByTestId('version')).toBeInTheDocument(); }; diff --git a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToTransferPage.tsx b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToTransferPage.tsx index 26cfe9c796..854835689a 100644 --- a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToTransferPage.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToTransferPage.tsx @@ -1,4 +1,5 @@ import { fireEvent, screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const NavigateToTransferPage: StepFunction = () => { diff --git a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToTypeTabUnderMessage.tsx b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToTypeTabUnderMessage.tsx index 72ceb18eef..e9ce9f625e 100644 --- a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToTypeTabUnderMessage.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToTypeTabUnderMessage.tsx @@ -5,7 +5,8 @@ import type { StepFunction } from '../../../lib/step'; interface NavigateToTypeTabUnderMessageProps { type: string; } -export const NavigateToTypeTabUnderMessage: StepFunction = - async ({ type }) => { - fireEvent.click(screen.getByTestId(type)); - }; +export const NavigateToTypeTabUnderMessage: StepFunction< + NavigateToTypeTabUnderMessageProps +> = async ({ type }) => { + fireEvent.click(screen.getByTestId(type)); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToVoiceMail.tsx b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToVoiceMail.tsx index dfe45e5e3b..f3d0e16068 100644 --- a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToVoiceMail.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/NavigateToVoiceMail.tsx @@ -3,8 +3,6 @@ import { fireEvent, screen } from '@testing-library/react'; import type { StepFunction } from '../../../lib/step'; export const NavigateToVoiceMail: StepFunction = async () => { - fireEvent.click( - (screen.queryByTestId('Messages') ?? screen.queryByTestId('messagesTab'))!, - ); + fireEvent.click(screen.queryByTestId('messagesTab')!); fireEvent.click(screen.getByTestId('Voice')); }; diff --git a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/index.ts b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/index.ts index 0925a6523f..716fec4243 100644 --- a/packages/ringcentral-widgets-test/test/steps/Navigate/actions/index.ts +++ b/packages/ringcentral-widgets-test/test/steps/Navigate/actions/index.ts @@ -1,15 +1,17 @@ +export * from './NavigateToAllCalls'; +export * from './NavigateToCallingSetting'; export * from './NavigateToComposeText'; +export * from './NavigateToContactDetails'; +export * from './NavigateToContacts'; export * from './NavigateToDialer'; export * from './NavigateToFax'; export * from './NavigateToHistory'; -export * from './NavigateToSettings'; -export * from './NavigateToContactDetail'; -export * from './NavigateToTransferPage'; -export * from './NavigateToRegionSettings'; +export * from './NavigateToMeeting'; +export * from './NavigateToMessageHistory'; export * from './NavigateToMessages'; -export * from './NavigateToContacts'; +export * from './NavigateToRegionSettings'; export * from './NavigateToSMSTextTab'; -export * from './NavigateToVoiceMail'; +export * from './NavigateToSettings'; +export * from './NavigateToTransferPage'; export * from './NavigateToTypeTabUnderMessage'; -export * from './NavigateToCallingSetting'; -export * from './NavigateToMeeting'; +export * from './NavigateToVoiceMail'; diff --git a/packages/ringcentral-widgets-test/test/steps/Navigate/checks/CheckNavigationBarItem.tsx b/packages/ringcentral-widgets-test/test/steps/Navigate/checks/CheckNavigationBarItem.tsx index ac3c9aab14..4314cc2b20 100644 --- a/packages/ringcentral-widgets-test/test/steps/Navigate/checks/CheckNavigationBarItem.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Navigate/checks/CheckNavigationBarItem.tsx @@ -2,19 +2,19 @@ import type { StepFunction } from '@ringcentral-integration/test-utils'; import { screen, waitFor } from '@testing-library/react'; interface ICheckNavigationBarItemProps { - item: 'DialPad' | 'History' | 'Messages' | 'More Menu'; + dataSign: 'dialerTab' | 'historyTab' | 'messagesTab' | 'moreMenu'; isExist: boolean; } -export const CheckNavigationBarItem: StepFunction = - async ({ item, isExist }) => { - await waitFor(() => { - const ItemComp = screen.queryByTestId(item); - - if (isExist) { - expect(ItemComp).toBeInTheDocument(); - } else { - expect(ItemComp).not.toBeInTheDocument(); - } - }); - }; +export const CheckNavigationBarItem: StepFunction< + ICheckNavigationBarItemProps +> = async ({ dataSign, isExist }) => { + await waitFor(() => { + const item = screen.queryByTestId(dataSign); + if (isExist) { + expect(item).toBeInTheDocument(); + } else { + expect(item).not.toBeInTheDocument(); + } + }); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Navigate/checks/CheckRouterNavigation.tsx b/packages/ringcentral-widgets-test/test/steps/Navigate/checks/CheckRouterNavigation.tsx index adcf63e13f..5fc988f58c 100644 --- a/packages/ringcentral-widgets-test/test/steps/Navigate/checks/CheckRouterNavigation.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Navigate/checks/CheckRouterNavigation.tsx @@ -15,26 +15,27 @@ export interface CheckRouterNavigationProps { toPage?: keyof typeof targets; } -export const CheckRouterNavigation: StepFunction = - async (props, context) => { - const { toPage = 'Settings' } = props; - const { phone } = context; - const targetRegExp = targets[toPage]; - if (targetRegExp) { - await waitUntilTo(() => { - expect( - targetRegExp.test(phone.routerInteraction.currentPath), - ).toBeTruthy(); - }).catch(() => { - throw new Error( - [ - `Navigation to the ${toPage} Failed,`, - `Current path: ${phone.routerInteraction.currentPath},`, - `targetRegExp: ${targetRegExp}`, - ].join('\r\n'), - ); - }); - } else { - throw new Error(`Navigation to the ${toPage} is not implement`); - } - }; +export const CheckRouterNavigation: StepFunction< + CheckRouterNavigationProps +> = async (props, context) => { + const { toPage = 'Settings' } = props; + const { phone } = context; + const targetRegExp = targets[toPage]; + if (targetRegExp) { + await waitUntilTo(() => { + expect( + targetRegExp.test(phone.routerInteraction.currentPath), + ).toBeTruthy(); + }).catch(() => { + throw new Error( + [ + `Navigation to the ${toPage} Failed,`, + `Current path: ${phone.routerInteraction.currentPath},`, + `targetRegExp: ${targetRegExp}`, + ].join('\r\n'), + ); + }); + } else { + throw new Error(`Navigation to the ${toPage} is not implement`); + } +}; diff --git a/packages/ringcentral-widgets-test/test/steps/OAuthCheck.tsx b/packages/ringcentral-widgets-test/test/steps/OAuthCheck.tsx index ed39bc9150..17a04dda47 100644 --- a/packages/ringcentral-widgets-test/test/steps/OAuthCheck.tsx +++ b/packages/ringcentral-widgets-test/test/steps/OAuthCheck.tsx @@ -1,4 +1,3 @@ -import { URL } from 'url'; import type { StepFunction } from '../lib/step'; interface OAuthCheckProps { @@ -38,8 +37,6 @@ const OAuthCheck: StepFunction = async ( ['brand_id', brandId], ['display', 'page'], ['prompt', ''], - ['ui_options', 'hide_remember_me'], - ['ui_options', 'hide_tos'], ['ui_locales', ''], ['localeId', localeId], ...(useDiscovery ? [['discovery', 'true']] : []), diff --git a/packages/ringcentral-widgets-test/test/steps/SearchField/CheckContactDropdownList.ts b/packages/ringcentral-widgets-test/test/steps/SearchField/CheckContactDropdownList.ts deleted file mode 100644 index b893fa62f7..0000000000 --- a/packages/ringcentral-widgets-test/test/steps/SearchField/CheckContactDropdownList.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { waitUntilTo } from '@ringcentral-integration/utils'; -import { screen, within } from '@testing-library/react'; -import type { StepFunction } from '../../lib/step'; - -interface CheckContactDropdownListProps { - name: string; -} - -export const CheckContactDropdownList: StepFunction = - async ({ name }) => { - await waitUntilTo(() => { - expect(screen.getByTestId('contactDropdownList')).toBeVisible(); - expect( - within(screen.getAllByTestId('contactNameSection')[0]).getByText(name), - ).toBeVisible(); - }); - }; diff --git a/packages/ringcentral-widgets-test/test/steps/SearchField/Search.ts b/packages/ringcentral-widgets-test/test/steps/SearchField/Search.ts index 9f12c7071f..e668dd21a7 100644 --- a/packages/ringcentral-widgets-test/test/steps/SearchField/Search.ts +++ b/packages/ringcentral-widgets-test/test/steps/SearchField/Search.ts @@ -1,6 +1,7 @@ import { waitUntilTo } from '@ringcentral-integration/utils'; import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; + import type { StepFunction } from '../../lib/step'; interface SearchProps { diff --git a/packages/ringcentral-widgets-test/test/steps/SearchField/actions/ClickContactItem.ts b/packages/ringcentral-widgets-test/test/steps/SearchField/actions/ClickContactItem.ts new file mode 100644 index 0000000000..850807dca6 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/SearchField/actions/ClickContactItem.ts @@ -0,0 +1,20 @@ +import { screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { StepFunction } from '../../../lib/step'; + +interface ClickContactItemProps { + index: number; +} + +/** + * Click {index} of matched contacts list + */ +export const ClickContactItem: StepFunction = async ({ + index, +}) => { + expect(screen.getByTestId('contactDropdownList')).toBeVisible(); + const item = screen.getAllByTestId('contactNameSection')[index]; + expect(item).toBeVisible(); + userEvent.click(item); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/SearchField/actions/DeleteRecipientItem.ts b/packages/ringcentral-widgets-test/test/steps/SearchField/actions/DeleteRecipientItem.ts new file mode 100644 index 0000000000..b2205c9ca0 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/SearchField/actions/DeleteRecipientItem.ts @@ -0,0 +1,14 @@ +import { screen, within } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { StepFunction } from '../../../lib/step'; + +/** + * Delete matched contact in 'To' field input + */ +export const DeleteRecipientItem: StepFunction = async () => { + const recipientItem = screen.getByTestId('recipientItem') as HTMLElement; + + const deleteBtn = within(recipientItem).getByTestId('removeBtn'); + userEvent.click(deleteBtn); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/SearchField/actions/index.ts b/packages/ringcentral-widgets-test/test/steps/SearchField/actions/index.ts new file mode 100644 index 0000000000..22596d0d3c --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/SearchField/actions/index.ts @@ -0,0 +1,2 @@ +export * from './ClickContactItem'; +export * from './DeleteRecipientItem'; diff --git a/packages/ringcentral-widgets-test/test/steps/SearchField/checks/CheckContactDropdownList.ts b/packages/ringcentral-widgets-test/test/steps/SearchField/checks/CheckContactDropdownList.ts new file mode 100644 index 0000000000..33025a827d --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/SearchField/checks/CheckContactDropdownList.ts @@ -0,0 +1,22 @@ +import { waitUntilTo } from '@ringcentral-integration/utils'; +import { screen, within } from '@testing-library/react'; + +import type { StepFunction } from '../../../lib/step'; + +interface CheckContactDropdownListProps { + name: string; +} + +/** + * Check matched contacts list should contains {name} + */ +export const CheckContactDropdownList: StepFunction< + CheckContactDropdownListProps +> = async ({ name }) => { + await waitUntilTo(() => { + expect(screen.getByTestId('contactDropdownList')).toBeVisible(); + expect( + within(screen.getAllByTestId('contactNameSection')[0]).getByText(name), + ).toBeVisible(); + }); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/SearchField/checks/CheckNotContactsMatched.ts b/packages/ringcentral-widgets-test/test/steps/SearchField/checks/CheckNotContactsMatched.ts new file mode 100644 index 0000000000..de38ab7393 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/SearchField/checks/CheckNotContactsMatched.ts @@ -0,0 +1,19 @@ +import { waitUntilTo } from '@ringcentral-integration/utils'; +import { screen } from '@testing-library/react'; + +import type { StepFunction } from '../../../lib/step'; + +interface CheckNotContactsMatchedProps { + selector?: string; +} + +/** + * Check no matched contacts list shown + */ +export const CheckNotContactsMatched: StepFunction< + CheckNotContactsMatchedProps +> = async ({ selector = 'contactDropdownList' }) => { + await waitUntilTo(() => { + expect(screen.queryByTestId(selector)).not.toBeInTheDocument(); + }); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/SearchField/checks/CheckRecipientInput.ts b/packages/ringcentral-widgets-test/test/steps/SearchField/checks/CheckRecipientInput.ts new file mode 100644 index 0000000000..97a12ac4a3 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/SearchField/checks/CheckRecipientInput.ts @@ -0,0 +1,20 @@ +import { waitUntilTo } from '@ringcentral-integration/utils'; +import { screen, within } from '@testing-library/react'; + +import type { StepFunction } from '../../../lib/step'; + +interface CheckRecipientInputProps { + name: string; +} + +/** + * Check value in 'To' field input + */ +export const CheckRecipientInput: StepFunction< + CheckRecipientInputProps +> = async ({ name }) => { + const recipientItem = screen.getByTestId('recipientItem') as HTMLElement; + + expect(within(recipientItem).getByTestId('removeBtn')).toBeInTheDocument(); + expect(recipientItem).toHaveTextContent(name); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/SearchField/checks/index.ts b/packages/ringcentral-widgets-test/test/steps/SearchField/checks/index.ts new file mode 100644 index 0000000000..3c365eef0a --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/SearchField/checks/index.ts @@ -0,0 +1,3 @@ +export * from './CheckContactDropdownList'; +export * from './CheckNotContactsMatched'; +export * from './CheckRecipientInput'; diff --git a/packages/ringcentral-widgets-test/test/steps/SearchField/index.ts b/packages/ringcentral-widgets-test/test/steps/SearchField/index.ts index e4b00a9edf..43fe02aa01 100644 --- a/packages/ringcentral-widgets-test/test/steps/SearchField/index.ts +++ b/packages/ringcentral-widgets-test/test/steps/SearchField/index.ts @@ -1,2 +1,3 @@ -export * from './CheckContactDropdownList'; export * from './Search'; +export * from './checks'; +export * from './actions'; diff --git a/packages/ringcentral-widgets-test/test/steps/Settings/actions/ClickSaveButton.tsx b/packages/ringcentral-widgets-test/test/steps/Settings/actions/ClickSaveButton.tsx new file mode 100644 index 0000000000..3b3591c622 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Settings/actions/ClickSaveButton.tsx @@ -0,0 +1,11 @@ +import { screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { StepFunction } from '../../../lib/step'; + +/** + * Click save button on Settings calling options page + */ +export const ClickSaveButton: StepFunction = () => { + userEvent.click(screen.getByText('Save')); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Settings/actions/ExpandCallingSettingDropdown.tsx b/packages/ringcentral-widgets-test/test/steps/Settings/actions/ExpandCallingSettingDropdown.tsx new file mode 100644 index 0000000000..3d8a765abc --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Settings/actions/ExpandCallingSettingDropdown.tsx @@ -0,0 +1,12 @@ +import { fireEvent, screen, within } from '@testing-library/react'; + +import type { StepFunction } from '../../../lib/step'; + +/** + * Expand the calling options dropdown list + */ +export const ExpandCallingSettingDropdown: StepFunction = () => { + const callingSettingElement = screen.getByTestId('callingSetting'); + const selectRoot = within(callingSettingElement).getByTestId('selectRoot'); + fireEvent.click(selectRoot); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Settings/actions/SelectCallingSetting.tsx b/packages/ringcentral-widgets-test/test/steps/Settings/actions/SelectCallingSetting.tsx new file mode 100644 index 0000000000..a4eeb12840 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Settings/actions/SelectCallingSetting.tsx @@ -0,0 +1,19 @@ +import { fireEvent, screen, within } from '@testing-library/react'; + +import type { StepFunction } from '../../../lib/step'; + +interface SelectCallingSettingProps { + settingName: string; +} + +/** + * Select the specified calling option + * @param settingName Option name to select + */ +export const SelectCallingSetting: StepFunction = ({ + settingName, +}) => { + const menuItem = within(screen.getByRole('menu')).getByText(settingName); + fireEvent.click(menuItem); + expect(screen.getByTestId('callingSetting')).toHaveTextContent(settingName); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Settings/actions/SelectCountryCode.tsx b/packages/ringcentral-widgets-test/test/steps/Settings/actions/SelectCountryCode.tsx deleted file mode 100644 index 6656f519d8..0000000000 --- a/packages/ringcentral-widgets-test/test/steps/Settings/actions/SelectCountryCode.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { queryByText, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import type { StepFunction } from '../../../lib/step'; - -interface SelectCountryCodeInt { - countryCode: string; -} - -export const SelectCountryCode: StepFunction = async ({ - countryCode, -}) => { - userEvent.click(screen.queryByTestId('selectedItem')); - await waitFor(() => expect(screen.queryByRole('menu')).toBeInTheDocument()); - userEvent.click(queryByText(screen.queryByRole('menu'), countryCode)); - await waitFor(() => - expect(screen.queryByRole('menu')).not.toBeInTheDocument(), - ); -}; diff --git a/packages/ringcentral-widgets-test/test/steps/Settings/actions/SetAcceptCallQueueCalls.tsx b/packages/ringcentral-widgets-test/test/steps/Settings/actions/SetAcceptCallQueueCalls.tsx index a3e9a67fa5..85ab92e68e 100644 --- a/packages/ringcentral-widgets-test/test/steps/Settings/actions/SetAcceptCallQueueCalls.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Settings/actions/SetAcceptCallQueueCalls.tsx @@ -1,7 +1,8 @@ import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { MockPutPresence } from '../../Mock'; + import type { StepFunction } from '../../../lib/step'; +import { MockPutPresence } from '../../Mock'; interface SetAcceptCallQueueCallsOptions { isAccept: boolean; diff --git a/packages/ringcentral-widgets-test/test/steps/Settings/actions/SetAreaCode.tsx b/packages/ringcentral-widgets-test/test/steps/Settings/actions/SetAreaCode.tsx index 18c1ab07d8..1da1e2fc46 100644 --- a/packages/ringcentral-widgets-test/test/steps/Settings/actions/SetAreaCode.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Settings/actions/SetAreaCode.tsx @@ -1,3 +1,4 @@ +import type { RegionSettings } from '@ringcentral-integration/commons/modules/RegionSettings'; import { waitUntilTo } from '@ringcentral-integration/utils'; import { fireEvent, screen } from '@testing-library/react'; import { act } from 'react-dom/test-utils'; @@ -6,26 +7,50 @@ import type { StepFunction } from '../../../lib/step'; interface SetAreaCodeInt { areaCode: string; + clickSave?: boolean; } -export const SetAreaCode: StepFunction = async ({ - areaCode, -}) => { +export const SetAreaCode: StepFunction = async ( + { areaCode, clickSave = true }, + context, +) => { + const areaCodeInput = + screen.queryByTestId('areaCodeInputField'); + + // When area code is set to auto detected, no area code input field then + if (!areaCodeInput) { + const regionSettings = context.phone.regionSettings as RegionSettings; + regionSettings.setAreaCode(areaCode); + return; + } + + // check if alert saving + const hasAlert = areaCode !== areaCodeInput.value; + + // input act(() => { - fireEvent.change(screen.getByTestId('areaCodeInputField'), { + fireEvent.change(areaCodeInput, { target: { value: areaCode }, }); }); + expect(areaCodeInput).toHaveValue(areaCode); - expect(screen.getByTestId('areaCodeInputField')).toHaveValue(areaCode); - - act(() => { - fireEvent.click(screen.getByTestId('saveButton')); - }); + // save + if (clickSave) { + act(() => { + fireEvent.click(screen.getByTestId('saveButton')); + }); - await waitUntilTo(() => { - expect( - screen.getByText('Settings saved successfully.'), - ).toBeInTheDocument(); - }); + // check alert + if (hasAlert) { + await waitUntilTo(() => { + // multiple alerts will be shown when calling step SetCountryCode and step SetAreaCode at the same time + const alerts = screen.queryAllByText('Settings saved successfully.'); + expect(alerts.length).toBeGreaterThanOrEqual(1); + alerts.forEach((alert) => { + expect(alert).toBeInTheDocument(); + }); + }); + } + } }; diff --git a/packages/ringcentral-widgets-test/test/steps/Settings/actions/SetCallSetting.tsx b/packages/ringcentral-widgets-test/test/steps/Settings/actions/SetCallSetting.tsx deleted file mode 100644 index c3fb323489..0000000000 --- a/packages/ringcentral-widgets-test/test/steps/Settings/actions/SetCallSetting.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { fireEvent, screen, within } from '@testing-library/react'; - -import type { StepFunction } from '../../../lib/step'; - -export const ExpandDropdown: StepFunction = async () => { - const callingSettingElement = screen.getByTestId('callingSetting'); - const selectRoot = within(callingSettingElement).getByTestId('selectRoot'); - fireEvent.click(selectRoot); -}; - -export const SelectCallingSetting: StepFunction<{ settingName: string }> = ({ - settingName, -}) => { - const menuItem = within(screen.getByRole('menu')).getByText(settingName); - fireEvent.click(menuItem); - expect(screen.getByTestId('callingSetting')).toHaveTextContent(settingName); -}; - -export const ClickSaveButton: StepFunction = () => { - fireEvent.click(screen.getByText('Save')); -}; diff --git a/packages/ringcentral-widgets-test/test/steps/Settings/actions/SetCountryCode.ts b/packages/ringcentral-widgets-test/test/steps/Settings/actions/SetCountryCode.ts index 813e1326b5..db62f0264c 100644 --- a/packages/ringcentral-widgets-test/test/steps/Settings/actions/SetCountryCode.ts +++ b/packages/ringcentral-widgets-test/test/steps/Settings/actions/SetCountryCode.ts @@ -1,39 +1,52 @@ -import { waitUntilTo } from '@ringcentral-integration/utils'; -import { fireEvent, screen } from '@testing-library/react'; +import { whenStateChange } from '@ringcentral-integration/core/test'; +import { formatCountryDisplay } from '@ringcentral-integration/widgets/components/RegionSettingsPanel'; +import { fireEvent, screen, within } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; interface SetCountryCodeProps { - selectedCountryCallingCode?: string; - countryName?: string; + countryCallingCode: string; + countryName: string; + clickSave?: boolean; } export const SetCountryCode: StepFunction = async ({ - selectedCountryCallingCode, + countryCallingCode, countryName, + clickSave = true, }) => { - if ( - document.querySelector('[data-sign="selectedItem"]').textContent === - `(+${selectedCountryCallingCode}) ${countryName}` - ) { - return; - } + // format target value + const targetValue = formatCountryDisplay(countryCallingCode, countryName); + + // check if alert saving + const selectedItem = screen.getByTestId('selectedItem'); + const hasAlert = selectedItem.textContent !== targetValue; + + // open dropdown fireEvent.click(screen.getByTestId('selectRoot')); + const dropdownList = screen.getByRole('menu'); + expect(dropdownList).toBeInTheDocument(); - await waitUntilTo(() => { - expect(screen.getByRole('menu')).toBeInTheDocument(); - }); - fireEvent.click( - screen.getByText(`(+${selectedCountryCallingCode}) ${countryName}`), - ); - expect(screen.getByTestId('selectedItem')).toHaveTextContent( - `(+${selectedCountryCallingCode}) ${countryName}`, - ); - expect(screen.getByTestId('saveButton')).not.toBeDisabled(); - - fireEvent.click(screen.getByTestId('saveButton')); - await waitUntilTo(() => { - expect( - screen.getByText('Settings saved successfully.'), - ).toBeInTheDocument(); - }); + // select target value + fireEvent.click(within(dropdownList).getByText(targetValue)); + expect(selectedItem).toHaveTextContent(targetValue); + + // save + if (clickSave) { + const saveButton = screen.getByTestId('saveButton'); + expect(saveButton).not.toBeDisabled(); + fireEvent.click(saveButton); + + // check alert + if (hasAlert) { + await whenStateChange(() => { + // multiple alerts will be shown when calling step SetCountryCode and step SetAreaCode at the same time + const alerts = screen.queryAllByText('Settings saved successfully.'); + expect(alerts.length).toBeGreaterThanOrEqual(1); + alerts.forEach((alert) => { + expect(alert).toBeInTheDocument(); + }); + }); + } + } }; diff --git a/packages/ringcentral-widgets-test/test/steps/Settings/actions/SetPresenceStatus.tsx b/packages/ringcentral-widgets-test/test/steps/Settings/actions/SetPresenceStatus.tsx index 098d9af799..84cb51ce5b 100644 --- a/packages/ringcentral-widgets-test/test/steps/Settings/actions/SetPresenceStatus.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Settings/actions/SetPresenceStatus.tsx @@ -1,14 +1,16 @@ import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { MockPutPresence } from '../../Mock'; + import type { StepFunction } from '../../../lib/step'; +import { MockPutPresence } from '../../Mock'; interface SetPresenceStatusOptions { presence: 'available' | 'busy' | 'DND' | 'offline'; + shouldMockPutPresence?: boolean; } export const SetPresenceStatus: StepFunction = ( - { presence }, + { presence, shouldMockPutPresence = true }, context, ) => { const element = screen.queryByTestId('statusToggleShow'); @@ -23,7 +25,9 @@ export const SetPresenceStatus: StepFunction = ( const statusIcon = container.querySelector(`[type="${presence}"]`); expect(statusIcon).toBeInTheDocument(); if (statusIcon) { - MockPutPresence({}, context); + if (shouldMockPutPresence) { + MockPutPresence({}, context); + } userEvent.click(statusIcon.parentElement!); } } diff --git a/packages/ringcentral-widgets-test/test/steps/Settings/actions/index.ts b/packages/ringcentral-widgets-test/test/steps/Settings/actions/index.ts index 3f316782d9..0f0f236cb7 100644 --- a/packages/ringcentral-widgets-test/test/steps/Settings/actions/index.ts +++ b/packages/ringcentral-widgets-test/test/steps/Settings/actions/index.ts @@ -1,9 +1,10 @@ export * from './ClickLogout'; -export * from './SetAreaCode'; -export * from './SetCountryCode'; -export * from './SetCallSetting'; -export * from './SelectCountryCode'; +export * from './ClickSaveButton'; +export * from './ExpandCallingSettingDropdown'; export * from './ExpandRingOutDropdown'; +export * from './SelectCallingSetting'; export * from './SelectRingOutOption'; -export * from './SetPresenceStatus'; export * from './SetAcceptCallQueueCalls'; +export * from './SetAreaCode'; +export * from './SetCountryCode'; +export * from './SetPresenceStatus'; diff --git a/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckAcceptCallQueueCalls.tsx b/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckAcceptCallQueueCalls.tsx index aec4a71475..04d19df922 100644 --- a/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckAcceptCallQueueCalls.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckAcceptCallQueueCalls.tsx @@ -1,5 +1,6 @@ import { waitForRenderReady } from '@ringcentral-integration/test-utils'; import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; interface CheckAcceptCallQueueCallsOptions { diff --git a/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckAppVersionDisplay.tsx b/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckAppVersionDisplay.tsx index 4e448e6e8e..743f20dda6 100644 --- a/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckAppVersionDisplay.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckAppVersionDisplay.tsx @@ -1,4 +1,5 @@ import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const CheckAppVersionDisplay: StepFunction = async (props, context) => { diff --git a/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckAreaCodeField.tsx b/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckAreaCodeField.tsx index b90c8b710f..946229caa6 100644 --- a/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckAreaCodeField.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckAreaCodeField.tsx @@ -1,17 +1,23 @@ -import { getNodeText, screen, waitFor } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import type { StepFunction } from '../../../lib/step'; -export const CheckAreaCodeField: StepFunction<{ +interface CheckAreaCodeFieldProps { exist?: boolean; value?: string; -}> = async ({ exist = true, value = '' }) => { +} + +export const CheckAreaCodeField: StepFunction< + CheckAreaCodeFieldProps +> = async ({ exist = true, value }) => { + const areaCodeInputField = + screen.queryByTestId('areaCodeInputField'); if (exist) { - expect(screen.queryByTestId('areaCodeInputField')).toBeInTheDocument(); - expect( - screen.queryByTestId('areaCodeInputField')?.value, - ).toBe(value); + expect(areaCodeInputField).toBeInTheDocument(); + if (typeof value === 'string') { + expect(areaCodeInputField?.value).toBe(value); + } } else { - expect(screen.queryByTestId('areaCodeInputField')).not.toBeInTheDocument(); + expect(areaCodeInputField).not.toBeInTheDocument(); } }; diff --git a/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckCallSettingPage.tsx b/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckCallSettingPage.tsx index 8ebab84926..12c3ab2dfc 100644 --- a/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckCallSettingPage.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckCallSettingPage.tsx @@ -2,26 +2,44 @@ import { fireEvent, screen } from '@testing-library/react'; import type { StepFunction } from '../../../lib/step'; -export const CheckInfoTooltip: StepFunction<{ - tooltipContent: string | RegExp; -}> = ({ tooltipContent }) => { - // check if i icon show in the calling setting page - const infoIcon = document.querySelector( - '[data-sign="callSettingInfo"] span', - )!; - expect(infoIcon).toBeInTheDocument(); - - jest.useFakeTimers(); - - fireEvent.mouseEnter(infoIcon); - jest.advanceTimersByTime(1000); - - expect(screen.getByRole('tooltip')).toHaveTextContent(tooltipContent); - - fireEvent.mouseLeave(infoIcon); - jest.advanceTimersByTime(1000); - - expect(screen.queryByRole('tooltip')).toBeNull(); - - jest.useRealTimers(); +interface CheckCallSettingPageProps { + tooltipContent?: string | RegExp; + infoIcon?: boolean; + titleContent?: string; +} + +export const CheckCallSettingPage: StepFunction = ({ + titleContent, + infoIcon, + tooltipContent, +}) => { + // check in the calling setting page + const callSettingInfoElement = screen.getByTestId('callSettingInfo'); + expect(callSettingInfoElement).toBeInTheDocument(); + + // check label "Make my calls with" + if (titleContent) { + expect(callSettingInfoElement).toHaveTextContent(titleContent); + } + + // check has icon "i" + if (infoIcon || tooltipContent) { + const infoIconElement = callSettingInfoElement.querySelector('span')!; + expect(infoIconElement).toBeInTheDocument(); + + // check tooltip content of "i" icon + if (tooltipContent) { + jest.useFakeTimers(); + fireEvent.mouseEnter(infoIconElement); + jest.advanceTimersByTime(1000); + + expect(screen.getByRole('tooltip')).toHaveTextContent(tooltipContent); + + fireEvent.mouseLeave(infoIconElement); + jest.advanceTimersByTime(1000); + jest.useRealTimers(); + + expect(screen.queryByRole('tooltip')).toBeNull(); + } + } }; diff --git a/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckCallWithOption.tsx b/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckCallWithOption.tsx new file mode 100644 index 0000000000..7d1bf2f226 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckCallWithOption.tsx @@ -0,0 +1,21 @@ +import { screen, within } from '@testing-library/react'; + +import type { StepFunction } from '../../../lib/step'; + +export interface CheckCallWithOptionProps { + optionText: string; + exists?: boolean; +} + +export const CheckCallWithOption: StepFunction< + CheckCallWithOptionProps +> = async ({ optionText, exists = true }) => { + const optionElement = within( + screen.getByTestId('callingSetting'), + ).queryByText(optionText); + if (exists) { + expect(optionElement).toBeInTheDocument(); + } else { + expect(optionElement).not.toBeInTheDocument(); + } +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckCountryCodeField.tsx b/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckCountryCodeField.tsx index 683393a69a..1b3ec89fd1 100644 --- a/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckCountryCodeField.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckCountryCodeField.tsx @@ -1,10 +1,20 @@ +import { formatCountryDisplay } from '@ringcentral-integration/widgets/components/RegionSettingsPanel'; import { getNodeText, screen, waitFor } from '@testing-library/react'; import type { StepFunction } from '../../../lib/step'; -export const CheckCountryCodeField: StepFunction<{ countryCode: string }> = - async ({ countryCode }) => { - await waitFor(() => - expect(getNodeText(screen.getByTestId('selectedItem'))).toBe(countryCode), - ); - }; +interface CheckCountryCodeFieldProps { + countryCallingCode: string; + countryName: string; +} + +export const CheckCountryCodeField: StepFunction< + CheckCountryCodeFieldProps +> = async ({ countryCallingCode, countryName }) => { + // format target value + const targetValue = formatCountryDisplay(countryCallingCode, countryName); + + await waitFor(() => + expect(getNodeText(screen.getByTestId('selectedItem'))).toBe(targetValue), + ); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckSettingsPage.tsx b/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckSettingsPage.tsx index ec0cf01086..4a9e468879 100644 --- a/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckSettingsPage.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Settings/checks/CheckSettingsPage.tsx @@ -1,6 +1,8 @@ import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; import { CheckRouterNavigation } from '../../Navigate'; + import { CheckAppVersionDisplay } from './CheckAppVersionDisplay'; export const CheckRegionDisplay: StepFunction = async (props, context) => { diff --git a/packages/ringcentral-widgets-test/test/steps/Settings/checks/index.ts b/packages/ringcentral-widgets-test/test/steps/Settings/checks/index.ts index 66c5378b18..d4e85b490e 100644 --- a/packages/ringcentral-widgets-test/test/steps/Settings/checks/index.ts +++ b/packages/ringcentral-widgets-test/test/steps/Settings/checks/index.ts @@ -1,8 +1,10 @@ -export * from './CheckSettingsPage'; -export * from './CheckUserAtRegionSettings'; -export * from './CheckCountryCodeField'; +export * from './CheckAcceptCallQueueCalls'; +export * from './CheckAppVersionDisplay'; export * from './CheckAreaCodeField'; +export * from './CheckCallSettingPage'; +export * from './CheckCallWithOption'; export * from './CheckCallWithSoftphoneOption'; -export * from './CheckAppVersionDisplay'; +export * from './CheckCountryCodeField'; export * from './CheckCountryCodeHint'; -export * from './CheckAcceptCallQueueCalls'; +export * from './CheckSettingsPage'; +export * from './CheckUserAtRegionSettings'; diff --git a/packages/ringcentral-widgets-test/test/steps/Settings/index.ts b/packages/ringcentral-widgets-test/test/steps/Settings/index.ts index 20e593ecf4..e6ffa57916 100644 --- a/packages/ringcentral-widgets-test/test/steps/Settings/index.ts +++ b/packages/ringcentral-widgets-test/test/steps/Settings/index.ts @@ -1,2 +1,2 @@ -export * from './checks'; export * from './actions'; +export * from './checks'; diff --git a/packages/ringcentral-widgets-test/test/steps/Tracking/CheckEventTracking.tsx b/packages/ringcentral-widgets-test/test/steps/Tracking/CheckEventTracking.tsx index 8836402a12..b1d56af34c 100644 --- a/packages/ringcentral-widgets-test/test/steps/Tracking/CheckEventTracking.tsx +++ b/packages/ringcentral-widgets-test/test/steps/Tracking/CheckEventTracking.tsx @@ -1,4 +1,5 @@ import type { TrackLog } from '@ringcentral-integration/commons/modules/AnalyticsV2'; + import type { StepFunction } from '../../lib/step'; interface CheckEventTrackingProps { diff --git a/packages/ringcentral-widgets-test/test/steps/Transfer/actions/ClickTransfer.ts b/packages/ringcentral-widgets-test/test/steps/Transfer/actions/ClickTransfer.ts index 6833202ae5..b622136437 100644 --- a/packages/ringcentral-widgets-test/test/steps/Transfer/actions/ClickTransfer.ts +++ b/packages/ringcentral-widgets-test/test/steps/Transfer/actions/ClickTransfer.ts @@ -1,4 +1,5 @@ import { fireEvent, screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const ClickTransfer: StepFunction = async () => { diff --git a/packages/ringcentral-widgets-test/test/steps/Transfer/actions/index.ts b/packages/ringcentral-widgets-test/test/steps/Transfer/actions/index.ts new file mode 100644 index 0000000000..f51895608c --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Transfer/actions/index.ts @@ -0,0 +1 @@ +export * from './ClickTransfer'; diff --git a/packages/ringcentral-widgets-test/test/steps/Transfer/checks/CheckCompleteTransferButtonStatus.tsx b/packages/ringcentral-widgets-test/test/steps/Transfer/checks/CheckCompleteTransferButtonStatus.tsx new file mode 100644 index 0000000000..d002edd81e --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Transfer/checks/CheckCompleteTransferButtonStatus.tsx @@ -0,0 +1,15 @@ +import type { StepFunction } from '@ringcentral-integration/test-utils'; +import { screen } from '@testing-library/react'; + +interface CheckControlButtonOnWarmTransferProps { + isDisable: boolean; +} + +export const CheckCompleteTransferButtonStatus: StepFunction< + CheckControlButtonOnWarmTransferProps +> = async ({ isDisable }) => { + const button = screen.getByTestId('completeTransfer'); + isDisable + ? expect(button).toHaveClass('buttonDisabled') + : expect(button).not.toHaveClass('buttonDisabled'); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/Transfer/checks/index.ts b/packages/ringcentral-widgets-test/test/steps/Transfer/checks/index.ts new file mode 100644 index 0000000000..245d08899a --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Transfer/checks/index.ts @@ -0,0 +1 @@ +export * from './CheckCompleteTransferButtonStatus'; diff --git a/packages/ringcentral-widgets-test/test/steps/Transfer/index.ts b/packages/ringcentral-widgets-test/test/steps/Transfer/index.ts new file mode 100644 index 0000000000..e6ffa57916 --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/Transfer/index.ts @@ -0,0 +1,2 @@ +export * from './actions'; +export * from './checks'; diff --git a/packages/ringcentral-widgets-test/test/steps/WaitForSpinner.tsx b/packages/ringcentral-widgets-test/test/steps/WaitForSpinner.tsx index 2ac9134257..d6aeddfac3 100644 --- a/packages/ringcentral-widgets-test/test/steps/WaitForSpinner.tsx +++ b/packages/ringcentral-widgets-test/test/steps/WaitForSpinner.tsx @@ -1,10 +1,12 @@ -import { screen, waitForElementToBeRemoved } from '@testing-library/react'; -import type { StepFunction } from '../lib/step'; +import { whenStateOrTimerChange } from '@ringcentral-integration/core/test'; +import type { StepFunction } from '@ringcentral-integration/test-utils'; +import { screen } from '@testing-library/react'; export const WaitForSpinner: StepFunction = async () => { if (screen.queryByTestId('spinnerOverlay')) { - await waitForElementToBeRemoved(() => - screen.queryByTestId('spinnerOverlay'), - ); + await whenStateOrTimerChange(() => { + const overlay = screen.queryByTestId('spinnerOverlay'); + expect(overlay).not.toBeInTheDocument(); + }); } }; diff --git a/packages/ringcentral-widgets-test/test/steps/dialer/actions/ClickCallButton.tsx b/packages/ringcentral-widgets-test/test/steps/dialer/actions/ClickCallButton.tsx index 996bc84652..d3746d7f67 100644 --- a/packages/ringcentral-widgets-test/test/steps/dialer/actions/ClickCallButton.tsx +++ b/packages/ringcentral-widgets-test/test/steps/dialer/actions/ClickCallButton.tsx @@ -1,6 +1,13 @@ +import { screen, userEvent } from '@ringcentral-integration/test-utils'; import { fireEvent } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const ClickCallButton: StepFunction = () => { fireEvent.click(document.querySelector('.callBtn circle')); }; + +export const ClickCallButtonByDataSign: StepFunction = async (_, context) => { + const callButton = screen.getByTestId('callButton'); + userEvent.click(callButton); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/dialer/actions/ClickFromField.tsx b/packages/ringcentral-widgets-test/test/steps/dialer/actions/ClickFromField.tsx index 232d657940..c5960f705f 100644 --- a/packages/ringcentral-widgets-test/test/steps/dialer/actions/ClickFromField.tsx +++ b/packages/ringcentral-widgets-test/test/steps/dialer/actions/ClickFromField.tsx @@ -1,6 +1,7 @@ -import { screen, waitFor } from '@testing-library/react'; import { waitForRenderReady } from '@ringcentral-integration/test-utils'; +import { screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; + import type { StepFunction } from '../../../lib/step'; export const ClickFromField: StepFunction = async () => { diff --git a/packages/ringcentral-widgets-test/test/steps/dialer/actions/InputToField.ts b/packages/ringcentral-widgets-test/test/steps/dialer/actions/InputToField.ts index 4852db0162..19904fcd78 100644 --- a/packages/ringcentral-widgets-test/test/steps/dialer/actions/InputToField.ts +++ b/packages/ringcentral-widgets-test/test/steps/dialer/actions/InputToField.ts @@ -1,6 +1,7 @@ import { waitForRenderReady } from '@ringcentral-integration/test-utils'; import { screen, fireEvent } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; + import type { StepFunction } from '../../../lib/step'; export const InputToField: StepFunction<{ diff --git a/packages/ringcentral-widgets-test/test/steps/dialer/actions/SelectContactSearchOptionInDialPad.ts b/packages/ringcentral-widgets-test/test/steps/dialer/actions/SelectContactSearchOptionInDialPad.ts index 8e788e8bcb..e71a70a839 100644 --- a/packages/ringcentral-widgets-test/test/steps/dialer/actions/SelectContactSearchOptionInDialPad.ts +++ b/packages/ringcentral-widgets-test/test/steps/dialer/actions/SelectContactSearchOptionInDialPad.ts @@ -1,4 +1,5 @@ import { fireEvent, screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const SelectContactSearchOptionInDialPad: StepFunction<{ diff --git a/packages/ringcentral-widgets-test/test/steps/dialer/checks/CheckCallButtonStatus.tsx b/packages/ringcentral-widgets-test/test/steps/dialer/checks/CheckCallButtonStatus.tsx index 131d87e4a6..5b12927537 100644 --- a/packages/ringcentral-widgets-test/test/steps/dialer/checks/CheckCallButtonStatus.tsx +++ b/packages/ringcentral-widgets-test/test/steps/dialer/checks/CheckCallButtonStatus.tsx @@ -1,4 +1,5 @@ import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const CheckCallButtonDisabled: StepFunction = () => { diff --git a/packages/ringcentral-widgets-test/test/steps/dialer/checks/CheckDailerSpinner.tsx b/packages/ringcentral-widgets-test/test/steps/dialer/checks/CheckDialerSpinner.tsx similarity index 100% rename from packages/ringcentral-widgets-test/test/steps/dialer/checks/CheckDailerSpinner.tsx rename to packages/ringcentral-widgets-test/test/steps/dialer/checks/CheckDialerSpinner.tsx diff --git a/packages/ringcentral-widgets-test/test/steps/dialer/checks/CheckFromFieldExists.tsx b/packages/ringcentral-widgets-test/test/steps/dialer/checks/CheckFromFieldExists.tsx new file mode 100644 index 0000000000..ddf6e54d5f --- /dev/null +++ b/packages/ringcentral-widgets-test/test/steps/dialer/checks/CheckFromFieldExists.tsx @@ -0,0 +1,11 @@ +import { screen } from '@testing-library/react'; + +import type { StepFunction } from '../../../lib/step'; + +interface CheckFromFieldExistsProps {} + +export const CheckFromFieldExists: StepFunction< + CheckFromFieldExistsProps +> = () => { + expect(screen.getByTitle('From:')).toBeTruthy(); +}; diff --git a/packages/ringcentral-widgets-test/test/steps/dialer/checks/CheckInputToRecipientsNoExist.tsx b/packages/ringcentral-widgets-test/test/steps/dialer/checks/CheckInputToRecipientsNoExist.tsx index 41c699539c..1dc6ff31b6 100644 --- a/packages/ringcentral-widgets-test/test/steps/dialer/checks/CheckInputToRecipientsNoExist.tsx +++ b/packages/ringcentral-widgets-test/test/steps/dialer/checks/CheckInputToRecipientsNoExist.tsx @@ -1,5 +1,9 @@ +import { StepFunction } from '@ringcentral-integration/test-utils'; import { screen } from '@testing-library/react'; -export const CheckInputToRecipientsNoExist = async () => { +/** + * Check no value in 'To' field input + */ +export const CheckInputToRecipientsNoExist: StepFunction = async () => { expect(screen.queryByTestId('recipientItem')).not.toBeInTheDocument(); }; diff --git a/packages/ringcentral-widgets-test/test/steps/dialer/checks/CheckProvisionedCallerId.tsx b/packages/ringcentral-widgets-test/test/steps/dialer/checks/CheckProvisionedCallerId.tsx index 770f73bfe2..369f1fffd7 100644 --- a/packages/ringcentral-widgets-test/test/steps/dialer/checks/CheckProvisionedCallerId.tsx +++ b/packages/ringcentral-widgets-test/test/steps/dialer/checks/CheckProvisionedCallerId.tsx @@ -1,4 +1,5 @@ import { screen } from '@testing-library/react'; + import type { StepFunction } from '../../../lib/step'; export const CheckProvisionedCallerId: StepFunction<{ diff --git a/packages/ringcentral-widgets-test/test/steps/dialer/checks/index.ts b/packages/ringcentral-widgets-test/test/steps/dialer/checks/index.ts index b03d76e848..1deeb634cf 100644 --- a/packages/ringcentral-widgets-test/test/steps/dialer/checks/index.ts +++ b/packages/ringcentral-widgets-test/test/steps/dialer/checks/index.ts @@ -2,7 +2,8 @@ export * from './CheckContactSearchResultInDialPad'; export * from './CheckContactSearchOptionsInDialPad'; export * from './CheckToFieldValue'; export * from './CheckCallButtonStatus'; -export * from './CheckDailerSpinner'; +export * from './CheckDialerSpinner'; export * from './CheckInDialPage'; export * from './CheckProvisionedCallerId'; export * from './CheckInputToRecipientsNoExist'; +export * from './CheckFromFieldExists'; diff --git a/packages/ringcentral-widgets/assets/DynamicsFont/DynamicsFont.scss b/packages/ringcentral-widgets/assets/DynamicsFont/DynamicsFont.scss index fb8fb5b1d6..8c45076ba7 100644 --- a/packages/ringcentral-widgets/assets/DynamicsFont/DynamicsFont.scss +++ b/packages/ringcentral-widgets/assets/DynamicsFont/DynamicsFont.scss @@ -1,7 +1,7 @@ @font-face { font-family: 'dynamics_icon'; - src: url('./fonts/dynamics_icon.eot?64k03c'); - src: url('./fonts/dynamics_icon.eot?64k03c#iefix') format('embedded-opentype'), + src: url('./fonts/dynamics_icon.eot?64k03c'); + src: url('./fonts/dynamics_icon.eot?64k03c#iefix') format('embedded-opentype'), url('./fonts/dynamics_icon.woff2?64k03c') format('woff2'), url('./fonts/dynamics_icon.ttf?64k03c') format('truetype'), url('./fonts/dynamics_icon.woff?64k03c') format('woff'), @@ -10,7 +10,9 @@ font-style: normal; } -.icon { +.icon, +.icon::before, +.icon::after { /* use !important to prevent issues with browser extensions that change fonts */ font-family: 'dynamics_icon' !important; speak: none; @@ -29,269 +31,269 @@ composes: icon; } .groupConversation:before { - content: "\e913"; + content: '\e913'; } .conferenceHover { composes: icon; } .conferenceHover:before { - content: "\e90f"; + content: '\e90f'; } .conference { composes: icon; } .conference:before { - content: "\e910"; + content: '\e910'; } .menuHover { composes: icon; } .menuHover:before { - content: "\e911"; + content: '\e911'; } .menu { composes: icon; } .menu:before { - content: "\e912"; + content: '\e912'; } .addEntity { composes: icon; } .addEntity:before { - content: "\e960"; + content: '\e960'; } .clear { composes: icon; } .clear:before { - content: "\e901"; + content: '\e901'; } .portrait { composes: icon; } .portrait:before { - content: "\e904"; + content: '\e904'; } .search { composes: icon; } .search:before { - content: "\e905"; + content: '\e905'; } .activeHover { composes: icon; } .activeHover:before { - content: "\e908"; + content: '\e908'; } .active { composes: icon; } .active:before { - content: "\e90c"; + content: '\e90c'; } .composeTextHover { composes: icon; } .composeTextHover:before { - content: "\e90d"; + content: '\e90d'; } .composeText { composes: icon; } .composeText:before { - content: "\e941"; + content: '\e941'; } .message { composes: icon; } .message:before { - content: "\e95e"; + content: '\e95e'; } .messageHover { composes: icon; } .messageHover:before { - content: "\e95f"; + content: '\e95f'; } .expand { composes: icon; } .expand:before { - content: "\e902"; + content: '\e902'; } .information { composes: icon; } .information:before { - content: "\e903"; + content: '\e903'; } .collapse { composes: icon; } .collapse:before { - content: "\e949"; + content: '\e949'; } .close { composes: icon; } .close:before { - content: "\e94a"; + content: '\e94a'; } .outbound { composes: icon; } .outbound:before { - content: "\e94b"; + content: '\e94b'; } .missed { composes: icon; } .missed:before { - content: "\e94c"; + content: '\e94c'; } .inbound { composes: icon; } .inbound:before { - content: "\e94d"; + content: '\e94d'; } .record { composes: icon; } .record:before { - content: "\e94e"; + content: '\e94e'; } .call { composes: icon; } .call:before { - content: "\e94f"; + content: '\e94f'; } .add { composes: icon; } .add:before { - content: "\e950"; + content: '\e950'; } .arrow { composes: icon; } .arrow:before { - content: "\e951"; + content: '\e951'; } .callLog { composes: icon; } .callLog:before { - content: "\e952"; + content: '\e952'; } .callHover { composes: icon; } .callHover:before { - content: "\e953"; + content: '\e953'; } .dialHover { composes: icon; } .dialHover:before { - content: "\e954"; + content: '\e954'; } .dial { composes: icon; } .dial:before { - content: "\e955"; + content: '\e955'; } .edit { composes: icon; } .edit:before { - content: "\e956"; + content: '\e956'; } .file { composes: icon; } .file:before { - content: "\e957"; + content: '\e957'; } .historyHover { composes: icon; } .historyHover:before { - content: "\e958"; + content: '\e958'; } .history { composes: icon; } .history:before { - content: "\e959"; + content: '\e959'; } .logout { composes: icon; } .logout:before { - content: "\e95a"; + content: '\e95a'; } .settingHover { composes: icon; } .settingHover:before { - content: "\e95b"; + content: '\e95b'; } .setting { composes: icon; } .setting:before { - content: "\e95c"; + content: '\e95c'; } .trash { composes: icon; } .trash:before { - content: "\e95d"; + content: '\e95d'; } .delete { composes: icon; } .delete:before { - content: "\e909"; + content: '\e909'; } .plus { composes: icon; } .plus:before { - content: "\e90a"; + content: '\e90a'; } .caretDownCircle { composes: icon; } .caretDownCircle:before { - content: "\e90b"; + content: '\e90b'; } .add2 { composes: icon; } .add2:before { - content: "\e90e"; + content: '\e90e'; } .arrowLeft { composes: icon; } .arrowLeft:before { - content: "\e900"; + content: '\e900'; } .refresh { composes: icon; } .refresh:before { - content: "\e906"; + content: '\e906'; } .save { composes: icon; } .save:before { - content: "\e907"; + content: '\e907'; } diff --git a/packages/ringcentral-widgets/components/ActionMenu/index.tsx b/packages/ringcentral-widgets/components/ActionMenu/index.tsx index 484c694a54..cea205ac1b 100644 --- a/packages/ringcentral-widgets/components/ActionMenu/index.tsx +++ b/packages/ringcentral-widgets/components/ActionMenu/index.tsx @@ -42,7 +42,7 @@ type ActionMenuProps = { withAnimation?: boolean; selectedMatchContactType?: string; showChooseEntityModal?: boolean; - shouldHideEntityButton?: boolean; + shouldHideEntityButton?: (...args: any[]) => boolean; }; class ActionMenu extends Component { // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message @@ -180,6 +180,6 @@ ActionMenu.defaultProps = { withAnimation: true, selectedMatchContactType: '', showChooseEntityModal: true, - shouldHideEntityButton: false, + shouldHideEntityButton: () => false, }; export default ActionMenu; diff --git a/packages/ringcentral-widgets/components/ActionMenuList/i18n/en-US.ts b/packages/ringcentral-widgets/components/ActionMenuList/i18n/en-US.ts index 773b7b99bc..460a36b5a1 100644 --- a/packages/ringcentral-widgets/components/ActionMenuList/i18n/en-US.ts +++ b/packages/ringcentral-widgets/components/ActionMenuList/i18n/en-US.ts @@ -2,4 +2,4 @@ export default { sureToDeleteVoiceMail: 'Are you sure you want to delete this voicemail?', sureToDeleteFax: 'Are you sure you want to delete this fax?', doNotAskAgain: "Don't ask me again", -}; +} as const; diff --git a/packages/ringcentral-widgets/components/ActionMenuList/i18n/es-419.ts b/packages/ringcentral-widgets/components/ActionMenuList/i18n/es-419.ts index bf3b27c3c1..97eef0425e 100644 --- a/packages/ringcentral-widgets/components/ActionMenuList/i18n/es-419.ts +++ b/packages/ringcentral-widgets/components/ActionMenuList/i18n/es-419.ts @@ -1,6 +1,6 @@ export default { - sureToDeleteVoiceMail: "¿Está seguro de que desea eliminar este mensaje de voz?", - sureToDeleteFax: "¿Está seguro de que desea eliminar este fax?", + sureToDeleteVoiceMail: "¿Confirma que desea eliminar este mensaje de voz?", + sureToDeleteFax: "¿Confirma que desea eliminar este fax?", doNotAskAgain: "No preguntarme de nuevo" }; diff --git a/packages/ringcentral-widgets/components/ActionMenuList/i18n/index.ts b/packages/ringcentral-widgets/components/ActionMenuList/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/ringcentral-widgets/components/ActionMenuList/i18n/index.ts +++ b/packages/ringcentral-widgets/components/ActionMenuList/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/ringcentral-widgets/components/ActionMenuList/i18n/zh-HK.ts b/packages/ringcentral-widgets/components/ActionMenuList/i18n/zh-HK.ts index d5a3a9f0d4..ff7ddc8e9b 100644 --- a/packages/ringcentral-widgets/components/ActionMenuList/i18n/zh-HK.ts +++ b/packages/ringcentral-widgets/components/ActionMenuList/i18n/zh-HK.ts @@ -1,7 +1,7 @@ export default { sureToDeleteVoiceMail: "確定要刪除此語音訊息嗎?", - sureToDeleteFax: "您確定要刪除此傳真嗎?", - doNotAskAgain: "不要再問我" + sureToDeleteFax: "確定要刪除此傳真嗎?", + doNotAskAgain: "不再詢問" }; // @key: @#@"sureToDeleteVoiceMail"@#@ @source: @#@"Are you sure you want to delete this voicemail?"@#@ diff --git a/packages/ringcentral-widgets/components/ActionMenuList/i18n/zh-TW.ts b/packages/ringcentral-widgets/components/ActionMenuList/i18n/zh-TW.ts index d5a3a9f0d4..ff7ddc8e9b 100644 --- a/packages/ringcentral-widgets/components/ActionMenuList/i18n/zh-TW.ts +++ b/packages/ringcentral-widgets/components/ActionMenuList/i18n/zh-TW.ts @@ -1,7 +1,7 @@ export default { sureToDeleteVoiceMail: "確定要刪除此語音訊息嗎?", - sureToDeleteFax: "您確定要刪除此傳真嗎?", - doNotAskAgain: "不要再問我" + sureToDeleteFax: "確定要刪除此傳真嗎?", + doNotAskAgain: "不再詢問" }; // @key: @#@"sureToDeleteVoiceMail"@#@ @source: @#@"Are you sure you want to delete this voicemail?"@#@ diff --git a/packages/ringcentral-widgets/components/ActionMenuList/index.tsx b/packages/ringcentral-widgets/components/ActionMenuList/index.tsx index b61e2c47b8..04a6707ea9 100644 --- a/packages/ringcentral-widgets/components/ActionMenuList/index.tsx +++ b/packages/ringcentral-widgets/components/ActionMenuList/index.tsx @@ -1,10 +1,8 @@ -import React, { Component } from 'react'; - -import classnames from 'classnames'; -import PropTypes from 'prop-types'; - import { extensionTypes } from '@ringcentral-integration/commons/enums/extensionTypes'; import messageTypes from '@ringcentral-integration/commons/enums/messageTypes'; +import clsx from 'clsx'; +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; import dynamicsFont from '../../assets/DynamicsFont/DynamicsFont.scss'; import DeleteMessageIcon from '../../assets/images/DeleteMessageIcon.svg'; @@ -17,6 +15,7 @@ import EntityButton from '../EntityButton'; import EntityModal from '../EntityModal'; import LogButton from '../LogButton'; import Modal from '../Modal'; + import i18n from './i18n'; import styles from './styles.scss'; @@ -69,7 +68,7 @@ export const ClickToDialButton = ({ }: any) => { return ( + } + > + {i18n.getString('micNoPermissionMessage', currentLocale)} + + ); +}; + +export const getFallbackLabel = ( + devices: OmitFunctions[], + index: number, + currentLocale: string, +) => { + let fallbackLabel = i18n.getString('noLabel', currentLocale); + if (devices.length > 1) { + fallbackLabel = `${fallbackLabel} ${index + 1}`; + } + return fallbackLabel; +}; + +export const getDeviceValueRenderer = + (devices: OmitFunctions[], currentLocale: string) => + (value: string | null) => { + if (value === null) { + return i18n.getString('noDevice', currentLocale); + } + const index = devices.findIndex((device) => device.deviceId === value); + if (index > -1 && devices[index].label) { + return devices[index].label; + } + return getFallbackLabel(devices, index, currentLocale); + }; + +export const getDeviceOptionRenderer = + (devices: OmitFunctions[], currentLocale: string) => + (device: OmitFunctions, index: number) => { + if (device && device.label) { + return device.label; + } + return getFallbackLabel(devices, index, currentLocale); + }; + +const useDeviceRenderers = ( + devices: OmitFunctions[], + currentLocale: string, +) => { + return useMemo( + () => + [ + getDeviceValueRenderer(devices, currentLocale), + getDeviceOptionRenderer(devices, currentLocale), + ] as const, + [devices, currentLocale], + ); +}; + +const deviceValueFunction = (device: OmitFunctions) => + device.deviceId; + +const OutputDevice: FC< + Pick< + AudioSettingsPanelProps, + | 'availableOutputDevices' + | 'currentLocale' + | 'outputDeviceDisabled' + | 'outputDeviceId' + > & { + isFirefox: boolean; + onChange: (device: OmitFunctions) => void; + } +> = ({ + availableOutputDevices, + currentLocale, + isFirefox, + onChange, + outputDeviceDisabled, + outputDeviceId, +}) => { + const [deviceValueRenderer, deviceOptionRenderer] = useDeviceRenderers( + availableOutputDevices, + currentLocale, + ); + if (isFirefox && !availableOutputDevices.length) { + return ( + {i18n.getString('outputDevice', currentLocale)}} + noBorder + > +
+ {i18n.getString('defaultOutputDevice', currentLocale)} +
+
+ ); + } + return ( + {i18n.getString('outputDevice', currentLocale)}} + noBorder + > + + + ); +}; + +const InputDevice: FC< + Pick< + AudioSettingsPanelProps, + | 'availableInputDevices' + | 'inputDeviceId' + | 'inputDeviceDisabled' + | 'currentLocale' + > & { + onChange: (device: OmitFunctions) => void; + isFirefox: boolean; + } +> = ({ + availableInputDevices, + currentLocale, + inputDeviceDisabled, + inputDeviceId, + isFirefox, + onChange, +}) => { + const [deviceValueRenderer, deviceOptionRenderer] = useDeviceRenderers( + availableInputDevices, + currentLocale, + ); + + const showTooltip = + availableInputDevices.length > 0 + ? availableInputDevices[0].label === '' + : isFirefox; + + const tooltipContainer = useRef(null); + + const inputTooltip = showTooltip ? ( + } + getTooltipContainer={() => tooltipContainer.current} + > + + + ) : null; + return ( + + {i18n.getString('inputDevice', currentLocale)} + {inputTooltip} + + } + noBorder + > + +
{ + tooltipContainer.current = el; + }} + /> + + ); +}; + +function useDeviceIdState( + deviceId: string, + devices: OmitFunctions[], +) { + const [deviceIdState, setDeviceIdState] = useState(deviceId); + const setDeviceState = useCallback( + (device: OmitFunctions) => { + setDeviceIdState(device.deviceId); + }, + [setDeviceIdState], + ); + const oldDeviceId = usePrevious(() => deviceId, true); + const oldDevices = usePrevious(() => devices, true); + useEffect(() => { + if (deviceId !== oldDeviceId) { + setDeviceIdState(deviceId); + } + if (devices !== oldDevices) { + if (!devices.find((device) => device.deviceId === deviceIdState)) { + setDeviceIdState(deviceId); + } + } + }, [oldDeviceId, oldDevices, devices, deviceIdState, deviceId]); + + return [deviceIdState, setDeviceState] as const; +} + +const VolumeInput: FC<{ + volume: number; + minVolume?: number; + maxVolume?: number; + onChange: (volume: number) => void; + label: string; +}> = ({ volume, minVolume, maxVolume, onChange, label }) => { + return ( + {label}} noBorder> + + + ); +}; + +export const AudioSettingsPanel: FC = ({ + availableInputDevices, + availableOutputDevices, + callVolume, + checkUserMedia, + className = null, + currentLocale, + inputDeviceDisabled = false, + inputDeviceId, + onBackButtonClick, + onSave, + outputDeviceDisabled = false, + outputDeviceId, + ringtoneVolume, + showCallVolume = false, + showRingToneVolume = false, + supportDevices, + userMedia, +}) => { + // For firefox, when input device have empty label + // trigger get-user-media to load the device info at the first time + const triggerCheckUserMedia = useRef(false); + if (!triggerCheckUserMedia.current) { + triggerCheckUserMedia.current = true; + if (userMedia && availableInputDevices[0]?.label === '') { + checkUserMedia(); + } + } + const [outputDeviceIdState, setOutputDeviceState] = useDeviceIdState( + outputDeviceId, + availableOutputDevices, + ); + const [inputDeviceIdState, setInputDeviceState] = useDeviceIdState( + inputDeviceId, + availableInputDevices, + ); + const [isFirefox] = useState( + navigator.userAgent.indexOf('Firefox') > -1, + ); + + const [ringtoneVolumeState, setRingtoneVolumeState] = + useState(ringtoneVolume); + const [callVolumeState, setCallVolumeState] = useState(callVolume); + + const oldRingtoneVolume = usePrevious(() => ringtoneVolume, true); + const oldCallVolume = usePrevious(() => callVolume, true); + + useEffect(() => { + if (ringtoneVolume !== oldRingtoneVolume) { + setRingtoneVolumeState(ringtoneVolume); + } + if (callVolume !== oldCallVolume) { + setCallVolumeState(callVolume); + } + }, [ringtoneVolume, callVolume, oldRingtoneVolume, oldCallVolume]); + + const hasChanges = + outputDeviceId !== outputDeviceIdState || + inputDeviceId !== inputDeviceIdState || + ringtoneVolume !== ringtoneVolumeState || + callVolume !== callVolumeState; + + const onSaveClick = useCallback( + () => + onSave({ + outputDeviceId: outputDeviceIdState, + inputDeviceId: inputDeviceIdState, + ringtoneVolume: ringtoneVolumeState, + callVolume: callVolumeState, + }), + [ + onSave, + outputDeviceIdState, + inputDeviceIdState, + ringtoneVolumeState, + callVolumeState, + ], + ); + + return ( +
+ + + + {i18n.getString('title', currentLocale)} + + + + + {supportDevices ? ( + <> + + + + ) : null} + + {showCallVolume ? ( + + ) : null} + {showRingToneVolume ? ( + + ) : null} + + +
+ ); +}; diff --git a/packages/ringcentral-widgets/components/AudioSettingsPanel/VolumeSlider.tsx b/packages/ringcentral-widgets/components/AudioSettingsPanel/VolumeSlider.tsx new file mode 100644 index 0000000000..c02745cf1d --- /dev/null +++ b/packages/ringcentral-widgets/components/AudioSettingsPanel/VolumeSlider.tsx @@ -0,0 +1,48 @@ +import { RcIcon, RcSlider, styled } from '@ringcentral/juno'; +import { Audio, AudioSp } from '@ringcentral/juno-icon'; +import React, { FC } from 'react'; + +const Container = styled.div` + display: flex; + flex-direction: row; +`; +const VolumeIconContainer = styled.div` + display: flex; + justify-content: center; + align-items: center; +`; +const PaddedSlider = styled(RcSlider)` + margin: 0 16px; +`; +function toPercentValue(value: number) { + return Math.floor(value * 100); +} +function toValue(percent: number) { + return percent / 100; +} +export const VolumeSlider: FC<{ + className?: string; + volume: number; + minVolume?: number; + maxVolume?: number; + onChange: (volume: number) => void; +}> = ({ className, volume, minVolume = 0, maxVolume = 1, onChange }) => { + return ( + + + + + onChange(toValue(value as number))} + /> + + + + + ); +}; diff --git a/packages/ringcentral-widgets/components/AudioSettingsPanel/i18n/en-US.ts b/packages/ringcentral-widgets/components/AudioSettingsPanel/i18n/en-US.ts index c7d00274c5..5a87681329 100644 --- a/packages/ringcentral-widgets/components/AudioSettingsPanel/i18n/en-US.ts +++ b/packages/ringcentral-widgets/components/AudioSettingsPanel/i18n/en-US.ts @@ -17,4 +17,4 @@ export default { "Sorry, your current browser permissions aren't set to allow us to access your audio input devices.", clickHere: 'Click here', defaultOutputDevice: 'Default output device', -}; +} as const; diff --git a/packages/ringcentral-widgets/components/AudioSettingsPanel/i18n/es-ES.ts b/packages/ringcentral-widgets/components/AudioSettingsPanel/i18n/es-ES.ts index 5b34f50255..eb99ed91cb 100644 --- a/packages/ringcentral-widgets/components/AudioSettingsPanel/i18n/es-ES.ts +++ b/packages/ringcentral-widgets/components/AudioSettingsPanel/i18n/es-ES.ts @@ -9,7 +9,7 @@ export default { micPermission: "Permiso para el micrófono", noDevice: "Sin dispositivo", checkMicPermission: "Comprobar permiso", - micNoPermissionMessage: "La aplicación no tiene permiso para utilizar el micrófono", + micNoPermissionMessage: "La app no tiene permiso para utilizar el micrófono", noLabel: "Dispositivo desconocido", notSetSinkIdTip: "Los permisos de su navegador actual no están configurados para que podamos acceder a sus dispositivos de salida de audio. {clickHereLink} para ver cómo configurarlos.", noLabelTip: "Los permisos de su navegador actual no están configurados para que podamos acceder a sus dispositivos de entrada de audio.", diff --git a/packages/ringcentral-widgets/components/AudioSettingsPanel/i18n/index.ts b/packages/ringcentral-widgets/components/AudioSettingsPanel/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/ringcentral-widgets/components/AudioSettingsPanel/i18n/index.ts +++ b/packages/ringcentral-widgets/components/AudioSettingsPanel/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/ringcentral-widgets/components/AudioSettingsPanel/index.tsx b/packages/ringcentral-widgets/components/AudioSettingsPanel/index.tsx index 8bd12f804d..fad292a3ca 100644 --- a/packages/ringcentral-widgets/components/AudioSettingsPanel/index.tsx +++ b/packages/ringcentral-widgets/components/AudioSettingsPanel/index.tsx @@ -1,543 +1,2 @@ -import React, { Component } from 'react'; - -import classnames from 'classnames'; -import PropTypes from 'prop-types'; -import { all, find } from 'ramda'; -// @ts-expect-error TS(7016): Could not find a declaration file for module 'rc-t... Remove this comment to see the full error message -import Tooltip from 'rc-tooltip'; - -import InfoIcon from '../../assets/images/Info.svg'; -import BackHeader from '../BackHeader'; -import { Button } from '../Button'; -import Select from '../DropdownSelect'; -import IconLine from '../IconLine'; -import InputField from '../InputField'; -import Panel from '../Panel'; -import SaveButton from '../SaveButton'; -import i18n from './i18n'; -import styles from './styles.scss'; - -const TooltipCom = typeof Tooltip === 'function' ? Tooltip : Tooltip.default; - -class AudioSettingsPanel extends Component { - inputTooltipContainner: any; - outputTooltipContainner: any; - _isFirefox = false; - - constructor(props: any) { - super(props); - this.state = { - dialButtonVolume: props.dialButtonVolume, - dialButtonMuted: props.dialButtonMuted, - ringtoneVolume: props.ringtoneVolume, - ringtoneMuted: props.ringtoneMuted, - callVolume: props.callVolume, - inputDeviceId: props.inputDeviceId, - outputDeviceId: props.outputDeviceId, - }; - - this._isFirefox = navigator.userAgent.indexOf('Firefox') > -1; - } - - // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message - // eslint-disable-next-line react/no-deprecated - UNSAFE_componentWillReceiveProps(newProps: any) { - // @ts-expect-error TS(2339): Property 'dialButtonVolume' does not exist on type... Remove this comment to see the full error message - if (newProps.dialButtonVolume !== this.props.dialButtonVolume) { - this.setState({ - dialButtonVolume: newProps.dialButtonVolume, - }); - } - // @ts-expect-error TS(2339): Property 'dialButtonMuted' does not exist on type ... Remove this comment to see the full error message - if (newProps.dialButtonMuted !== this.props.dialButtonMuted) { - this.setState({ - dialButtonMuted: newProps.dialButtonMuted, - }); - } - // @ts-expect-error TS(2339): Property 'ringtoneVolume' does not exist on type '... Remove this comment to see the full error message - if (newProps.ringtoneVolume !== this.props.ringtoneVolume) { - this.setState({ - ringtoneVolume: newProps.ringtoneVolume, - }); - } - // @ts-expect-error TS(2339): Property 'ringtoneMuted' does not exist on type 'R... Remove this comment to see the full error message - if (newProps.ringtoneMuted !== this.props.ringtoneMuted) { - this.setState({ - ringtoneMuted: newProps.ringtoneMuted, - }); - } - // @ts-expect-error TS(2339): Property 'callVolume' does not exist on type 'Read... Remove this comment to see the full error message - if (newProps.callVolume !== this.props.callVolume) { - this.setState({ - callVolume: newProps.callVolume, - }); - } - if ( - // @ts-expect-error TS(2339): Property 'inputDeviceId' does not exist on type 'R... Remove this comment to see the full error message - newProps.inputDeviceId !== this.props.inputDeviceId || - all( - // @ts-expect-error TS(2571): Object is of type 'unknown'. - (device) => device.deviceId !== this.state.inputDeviceId, - newProps.availableInputDevices, - ) - ) { - this.setState({ - inputDeviceId: newProps.inputDeviceId, - }); - } - if ( - // @ts-expect-error TS(2339): Property 'outputDeviceId' does not exist on type '... Remove this comment to see the full error message - newProps.outputDeviceId !== this.props.outputDeviceId || - all( - // @ts-expect-error TS(2571): Object is of type 'unknown'. - (device) => device.deviceId !== this.state.outputDeviceId, - newProps.availableOutputDevices, - ) - ) { - this.setState({ - outputDeviceId: newProps.outputDeviceId, - }); - } - } - - // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message - componentDidMount() { - // @ts-expect-error TS(2339): Property 'userMedia' does not exist on type 'Reado... Remove this comment to see the full error message - if (!this.props.userMedia) { - return; - } - if ( - // @ts-expect-error TS(2339): Property 'availableInputDevices' does not exist on... Remove this comment to see the full error message - this.props.availableInputDevices.length > 0 && - // @ts-expect-error TS(2339): Property 'availableInputDevices' does not exist on... Remove this comment to see the full error message - this.props.availableInputDevices[0].label === '' - ) { - // @ts-expect-error TS(2339): Property 'checkUserMedia' does not exist on type '... Remove this comment to see the full error message - this.props.checkUserMedia(); - } - } - - onSave = () => { - // @ts-expect-error TS(2339): Property 'onSave' does not exist on type 'Readonly... Remove this comment to see the full error message - if (typeof this.props.onSave === 'function') { - const { - // @ts-expect-error TS(2339): Property 'dialButtonVolume' does not exist on type... Remove this comment to see the full error message - dialButtonVolume, - // @ts-expect-error TS(2339): Property 'dialButtonMuted' does not exist on type ... Remove this comment to see the full error message - dialButtonMuted, - // @ts-expect-error TS(2339): Property 'ringtoneVolume' does not exist on type '... Remove this comment to see the full error message - ringtoneVolume, - // @ts-expect-error TS(2339): Property 'ringtoneMuted' does not exist on type 'R... Remove this comment to see the full error message - ringtoneMuted, - // @ts-expect-error TS(2339): Property 'callVolume' does not exist on type 'Read... Remove this comment to see the full error message - callVolume, - // @ts-expect-error TS(2339): Property 'inputDeviceId' does not exist on type 'R... Remove this comment to see the full error message - inputDeviceId, - // @ts-expect-error TS(2339): Property 'outputDeviceId' does not exist on type '... Remove this comment to see the full error message - outputDeviceId, - } = this.state; - // @ts-expect-error TS(2339): Property 'onSave' does not exist on type 'Readonly... Remove this comment to see the full error message - this.props.onSave({ - dialButtonVolume, - dialButtonMuted, - ringtoneVolume, - ringtoneMuted, - callVolume, - inputDeviceId, - outputDeviceId, - }); - } - }; - - onReset = () => { - const { - // @ts-expect-error TS(2339): Property 'dialButtonVolume' does not exist on type... Remove this comment to see the full error message - dialButtonVolume, - // @ts-expect-error TS(2339): Property 'dialButtonMuted' does not exist on type ... Remove this comment to see the full error message - dialButtonMuted, - // @ts-expect-error TS(2339): Property 'ringtoneVolume' does not exist on type '... Remove this comment to see the full error message - ringtoneVolume, - // @ts-expect-error TS(2339): Property 'ringtoneMuted' does not exist on type 'R... Remove this comment to see the full error message - ringtoneMuted, - // @ts-expect-error TS(2339): Property 'callVolume' does not exist on type 'Read... Remove this comment to see the full error message - callVolume, - // @ts-expect-error TS(2339): Property 'inputDeviceId' does not exist on type 'R... Remove this comment to see the full error message - inputDeviceId, - // @ts-expect-error TS(2339): Property 'outputDeviceId' does not exist on type '... Remove this comment to see the full error message - outputDeviceId, - } = this.props; - this.setState({ - dialButtonVolume, - dialButtonMuted, - ringtoneVolume, - ringtoneMuted, - callVolume, - inputDeviceId, - outputDeviceId, - }); - }; - - onDialButtonVolumeChange = (dialButtonVolume: any) => { - this.setState({ - dialButtonVolume, - }); - }; - - onDialButtonMutedChange = (dialButtonMuted: any) => { - this.setState({ - dialButtonMuted, - }); - }; - - onRingtoneVolumeChange = (ringtoneVolume: any) => { - this.setState({ - ringtoneVolume, - }); - }; - - onRingtoneMutedChange = (ringtoneMuted: any) => { - this.setState({ - ringtoneMuted, - }); - }; - - onCallVolumeChange = (callVolume: any) => { - this.setState({ - callVolume, - }); - }; - - onOutputDeviceIdChange = (device: any) => { - this.setState({ - outputDeviceId: device.deviceId, - }); - }; - - onInputDeviceIdChange = (device: any) => { - this.setState({ - inputDeviceId: device.deviceId, - }); - }; - - renderDeviceOption = (device: any, index: any) => { - // @ts-expect-error TS(2339): Property 'availableInputDevices' does not exist on... Remove this comment to see the full error message - const { availableInputDevices, availableOutputDevices, currentLocale } = - this.props; - const noLabel = i18n.getString('noLabel', currentLocale); - if (device.kind === 'audioinput' && availableInputDevices.length > 1) { - return device.label || `${noLabel} ${index + 1}`; - } - if (device.kind === 'audiooutput' && availableOutputDevices.length > 1) { - return device.label || `${noLabel} ${index + 1}`; - } - return device.label || noLabel; - }; - - renderDeviceValue(device: any) { - return device.deviceId; - } - - renderOutputDevice = (value: any) => { - // @ts-expect-error TS(2339): Property 'availableOutputDevices' does not exist o... Remove this comment to see the full error message - const { availableOutputDevices, currentLocale } = this.props; - if (value === null) { - return i18n.getString('noDevice', currentLocale); - } - const device = find( - // @ts-expect-error TS(2571): Object is of type 'unknown'. - (device) => device.deviceId === value, - availableOutputDevices, - ); - let noLabel = i18n.getString('noLabel', currentLocale); - if (availableOutputDevices.length > 1) { - const index = availableOutputDevices.indexOf(device); - if (index >= 0) { - noLabel = `${noLabel} ${index + 1}`; - } - } - // @ts-expect-error TS(2571): Object is of type 'unknown'. - return (device && device.label) || noLabel; - }; - - renderInputDevice = (value: any) => { - // @ts-expect-error TS(2339): Property 'availableInputDevices' does not exist on... Remove this comment to see the full error message - const { availableInputDevices, currentLocale } = this.props; - if (value === null) { - return i18n.getString('noDevice', currentLocale); - } - const device = find( - // @ts-expect-error TS(2571): Object is of type 'unknown'. - (device) => device.deviceId === value, - availableInputDevices, - ); - let noLabel = i18n.getString('noLabel', currentLocale); - if (availableInputDevices.length > 1) { - const index = availableInputDevices.indexOf(device); - if (index >= 0) { - noLabel = `${noLabel} ${index + 1}`; - } - } - // @ts-expect-error TS(2571): Object is of type 'unknown'. - return (device && device.label) || noLabel; - }; - - isNoLabel() { - // @ts-expect-error TS(2339): Property 'availableInputDevices' does not exist on... Remove this comment to see the full error message - const { availableInputDevices } = this.props; - - let noLabel = false; - - if (availableInputDevices && availableInputDevices.length) { - noLabel = availableInputDevices[0].label === ''; - } else { - noLabel = this._isFirefox; - } - - return noLabel; - } - - // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message - render() { - const { - // @ts-expect-error TS(2339): Property 'currentLocale' does not exist on type 'R... Remove this comment to see the full error message - currentLocale, - // @ts-expect-error TS(2339): Property 'onBackButtonClick' does not exist on typ... Remove this comment to see the full error message - onBackButtonClick, - // @ts-expect-error TS(2339): Property 'className' does not exist on type 'Reado... Remove this comment to see the full error message - className, - // @ts-expect-error TS(2339): Property 'availableOutputDevices' does not exist o... Remove this comment to see the full error message - availableOutputDevices, - // @ts-expect-error TS(2339): Property 'availableInputDevices' does not exist on... Remove this comment to see the full error message - availableInputDevices, - // @ts-expect-error TS(2339): Property 'supportDevices' does not exist on type '... Remove this comment to see the full error message - supportDevices, - // @ts-expect-error TS(2339): Property 'userMedia' does not exist on type 'Reado... Remove this comment to see the full error message - userMedia, - // @ts-expect-error TS(2339): Property 'isWebRTC' does not exist on type 'Readon... Remove this comment to see the full error message - isWebRTC, - // @ts-expect-error TS(2339): Property 'checkUserMedia' does not exist on type '... Remove this comment to see the full error message - checkUserMedia, - // @ts-expect-error TS(2339): Property 'outputDeviceDisabled' does not exist on ... Remove this comment to see the full error message - outputDeviceDisabled, - // @ts-expect-error TS(2339): Property 'inputDeviceDisabled' does not exist on t... Remove this comment to see the full error message - inputDeviceDisabled, - } = this.props; - const { - // @ts-expect-error TS(2339): Property 'dialButtonVolume' does not exist on type... Remove this comment to see the full error message - dialButtonVolume, - // @ts-expect-error TS(2339): Property 'dialButtonMuted' does not exist on type ... Remove this comment to see the full error message - dialButtonMuted, - // @ts-expect-error TS(2339): Property 'ringtoneVolume' does not exist on type '... Remove this comment to see the full error message - ringtoneVolume, - // @ts-expect-error TS(2339): Property 'ringtoneMuted' does not exist on type 'R... Remove this comment to see the full error message - ringtoneMuted, - // @ts-expect-error TS(2339): Property 'callVolume' does not exist on type 'Read... Remove this comment to see the full error message - callVolume, - // @ts-expect-error TS(2339): Property 'outputDeviceId' does not exist on type '... Remove this comment to see the full error message - outputDeviceId, - // @ts-expect-error TS(2339): Property 'inputDeviceId' does not exist on type 'R... Remove this comment to see the full error message - inputDeviceId, - } = this.state; - const hasChanges = - // @ts-expect-error TS(2339): Property 'dialButtonVolume' does not exist on type... Remove this comment to see the full error message - this.props.dialButtonVolume !== dialButtonVolume || - // @ts-expect-error TS(2339): Property 'dialButtonMuted' does not exist on type ... Remove this comment to see the full error message - this.props.dialButtonMuted !== dialButtonMuted || - // @ts-expect-error TS(2339): Property 'ringtoneVolume' does not exist on type '... Remove this comment to see the full error message - this.props.ringtoneVolume !== ringtoneVolume || - // @ts-expect-error TS(2339): Property 'ringtoneMuted' does not exist on type 'R... Remove this comment to see the full error message - this.props.ringtoneMuted !== ringtoneMuted || - // @ts-expect-error TS(2339): Property 'callVolume' does not exist on type 'Read... Remove this comment to see the full error message - this.props.callVolume !== callVolume || - // @ts-expect-error TS(2339): Property 'inputDeviceId' does not exist on type 'R... Remove this comment to see the full error message - this.props.inputDeviceId !== inputDeviceId || - // @ts-expect-error TS(2339): Property 'outputDeviceId' does not exist on type '... Remove this comment to see the full error message - this.props.outputDeviceId !== outputDeviceId; - - // TODO: improve UI - const permission = !userMedia ? ( - - {i18n.getString('checkMicPermission')} - - } - > - {i18n.getString('micNoPermissionMessage')} - - ) : null; - - // const webphoneVolume = isWebRTC ? - // ( - //
- // - // {`${ringtoneVolume * 100}%`} - // - // - // {`${callVolume * 100}%`} - // - //
- // ) : null; - - const outputDeviceDropdown = supportDevices ? ( - {i18n.getString('outputDevice', currentLocale)}} - // @ts-expect-error TS(2322): Type '{ children: Element[]; label: Element; noBor... Remove this comment to see the full error message - noBorder - > - -
{ - this.inputTooltipContainner = tooltipContainer; - }} - /> - - ) : null; - - return ( -
- - {i18n.getString('title', currentLocale)} - - - { - // - // {`${dialButtonVolume * 100}%`} - // - // webphoneVolume - } - {outputDevice} - {inputDevice} - {permission} - - -
- ); - } -} - -const devicePropType = { - deviceId: PropTypes.string.isRequired, - label: PropTypes.string.isRequired, -}; - -// @ts-expect-error TS(2339): Property 'propTypes' does not exist on type 'typeo... Remove this comment to see the full error message -AudioSettingsPanel.propTypes = { - className: PropTypes.string, - currentLocale: PropTypes.string.isRequired, - dialButtonVolume: PropTypes.number.isRequired, - ringtoneVolume: PropTypes.number.isRequired, - ringtoneMuted: PropTypes.bool.isRequired, - callVolume: PropTypes.number.isRequired, - dialButtonMuted: PropTypes.bool.isRequired, - onBackButtonClick: PropTypes.func.isRequired, - availableInputDevices: PropTypes.arrayOf(PropTypes.shape(devicePropType)) - .isRequired, - inputDeviceId: PropTypes.string.isRequired, - availableOutputDevices: PropTypes.arrayOf(PropTypes.shape(devicePropType)) - .isRequired, - outputDeviceId: PropTypes.string.isRequired, - supportDevices: PropTypes.bool.isRequired, - onSave: PropTypes.func.isRequired, - userMedia: PropTypes.bool.isRequired, - isWebRTC: PropTypes.bool.isRequired, - checkUserMedia: PropTypes.func.isRequired, - outputDeviceDisabled: PropTypes.bool, - inputDeviceDisabled: PropTypes.bool, -}; - -// @ts-expect-error TS(2339): Property 'defaultProps' does not exist on type 'ty... Remove this comment to see the full error message -AudioSettingsPanel.defaultProps = { - className: null, - outputDeviceDisabled: false, - inputDeviceDisabled: false, -}; - -export default AudioSettingsPanel; +export * from './AudioSettingsPanel.interface'; +export * from './AudioSettingsPanel'; diff --git a/packages/ringcentral-widgets/components/AudioSettingsPanelV2/AudioSettingsPanel.interface.ts b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/AudioSettingsPanel.interface.ts new file mode 100644 index 0000000000..8bea78918f --- /dev/null +++ b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/AudioSettingsPanel.interface.ts @@ -0,0 +1,46 @@ +import type { + AudioInfo, + RingtoneItem, +} from '@ringcentral-integration/commons/modules/RingtoneConfiguration'; +import type { AUDIO_TYPE } from '@ringcentral-integration/commons/modules/VolumeInspector'; +import type { OmitFunctions } from '@ringcentral-integration/utils/src/typeFunctions/OmitFunctions'; + +export interface AudioSettingsPanelProps { + isAGCEnabled: boolean; + hasUserMedia: boolean; + showAGCEnabled: boolean; + availableInputDevices: OmitFunctions[]; + availableOutputDevices: OmitFunctions[]; + availableRingtoneDevices: OmitFunctions[]; + checkUserMedia: () => Promise; + showAlert: () => void; + className?: string | null; + inputDeviceDisabled?: boolean; + ringtoneSelectDisabled?: boolean; + inputDeviceId: string; + isWebRTC: boolean; + onBackButtonClick: (...args: any) => unknown; + onSave: (...args: any) => unknown; + outputDeviceDisabled?: boolean; + outputDeviceId: string; + ringtoneVolume: number; + callVolume: number; + ringtoneDeviceId: string; + handleTestMicroClick: (...args: any) => unknown; + handleTestSpeakerClick: (...args: any) => unknown; + isUploadRingtoneDisabled?: boolean; + volumeTestData: { + volume: number; + countDown: number; + testState: number; + isRecording: boolean; + type: AUDIO_TYPE | null; + }; + fullRingtoneList: RingtoneItem[]; + selectedRingtoneId: string; + enableCustomRingtone?: boolean; + updateCurrentRingtone: (id: string) => void; + removeCustomRingtone: (id: string) => void; + uploadCustomRingtone: (audioInfo: AudioInfo) => void; + showDangerAlert: (message: string) => void; +} diff --git a/packages/ringcentral-widgets/components/AudioSettingsPanelV2/AudioSettingsPanel.tsx b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/AudioSettingsPanel.tsx new file mode 100644 index 0000000000..c895356020 --- /dev/null +++ b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/AudioSettingsPanel.tsx @@ -0,0 +1,184 @@ +import { TEST_TYPE } from '@ringcentral-integration/commons/modules/VolumeInspector'; +import { RcSwitch, RcTypography } from '@ringcentral/juno'; +import clsx from 'clsx'; +import React, { useEffect } from 'react'; +import type { FC } from 'react'; + +import { + PageHeader, + PageHeaderBack, + PageHeaderRemain, + PageHeaderTitle, +} from '../BackHeader/PageHeader'; +import { RingtoneSelection } from '../RingtoneSelection'; + +import type { AudioSettingsPanelProps } from './AudioSettingsPanel.interface'; +import { + VolumeTester, + AudioDeviceSelect, + Section, + VolumeSlider, +} from './components'; +import { t } from './i18n'; +import styles from './styles.scss'; + +export const AudioSettingsPanel: FC = ({ + availableInputDevices, + availableOutputDevices, + availableRingtoneDevices, + callVolume, + showDangerAlert, + className = null, + inputDeviceDisabled = false, + inputDeviceId, + onBackButtonClick, + onSave, + outputDeviceDisabled = false, + ringtoneSelectDisabled = false, + outputDeviceId, + ringtoneVolume, + isAGCEnabled, + showAGCEnabled, + hasUserMedia, + ringtoneDeviceId, + handleTestMicroClick, + handleTestSpeakerClick, + showAlert, + volumeTestData, + fullRingtoneList, + selectedRingtoneId, + isUploadRingtoneDisabled, + enableCustomRingtone, + uploadCustomRingtone, + updateCurrentRingtone, + removeCustomRingtone, +}) => { + useEffect(() => { + showAlert(); + }, []); + + return ( +
+ + + {t('title')} + + +
+
+ { + onSave({ + inputDeviceId: deviceId, + }); + }} + /> + { + handleTestMicroClick(volumeTestData.testState); + }} + /> + {showAGCEnabled && ( + + {t('autoAdjustMicLevel')} + + } + checked={isAGCEnabled} + onChange={(_, checked) => { + onSave({ + isAGCEnabled: checked, + }); + }} + /> + )} +
+
+ { + onSave({ + outputDeviceId: deviceId, + }); + }} + label={t('speakerSource')} + /> + { + handleTestSpeakerClick(volumeTestData.testState); + }} + /> + { + onSave({ + callVolume: volume, + }); + }} + /> + { + onSave({ + ringtoneDeviceId: deviceId, + }); + }} + label={t('ringtoneSource')} + /> + + { + onSave({ + ringtoneVolume: volume, + }); + }} + /> +
+
+
+ ); +}; diff --git a/packages/ringcentral-widgets/components/AudioSettingsPanelV2/components/AudioDeviceSelect.tsx b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/components/AudioDeviceSelect.tsx new file mode 100644 index 0000000000..d1d9f9d3b6 --- /dev/null +++ b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/components/AudioDeviceSelect.tsx @@ -0,0 +1,99 @@ +import { type OmitFunctions } from '@ringcentral-integration/utils/src/typeFunctions/OmitFunctions'; +import { + RcSelect, + RcMenuItem, + RcListItemText, + type RcSelectProps, + RcTypography, + RcTooltip, +} from '@ringcentral/juno'; +import React from 'react'; +import type { FC } from 'react'; + +import { t } from '../i18n'; +import styles from '../styles.scss'; + +export const SelectDevice = ({ children, label, ...props }: RcSelectProps) => ( + + {label} + + } + {...props} + > + {children} + +); + +export const getFallbackLabel = (device: OmitFunctions) => { + const fallbackLabel = t('noLabel'); + const deviceId = device.deviceId ? `(${device.deviceId.slice(-4)})` : ''; + return `${fallbackLabel}${deviceId}`; +}; + +export const getDeviceOptionRenderer = ( + device: OmitFunctions, +) => { + if (device && device.label) { + return device.label; + } + if (device && device.deviceId === 'off') { + return t('off'); + } + return getFallbackLabel(device); +}; + +export const AudioDeviceSelect: FC<{ + availableDevices: OmitFunctions[]; + isDisabled?: boolean; + deviceId: string; + onChange: (deviceId: string) => void; + label: string; + dataSign: string; +}> = ({ + availableDevices = [], + onChange, + isDisabled, + deviceId, + label, + dataSign, +}) => { + const allDevicesAreEmpty = availableDevices.every( + (item) => + (item.label === '' && item.deviceId === '') || + (item.label === '' && item.deviceId === 'off'), + ); + if (!availableDevices.length || allDevicesAreEmpty) { + return ( + + + + + + ); + } + return ( + { + const deviceId = e.target.value; + onChange(deviceId as string); + }} + disabled={isDisabled} + label={label} + > + {availableDevices.map((device) => ( + + + + + + ))} + + ); +}; diff --git a/packages/ringcentral-widgets/components/AudioSettingsPanelV2/components/AudioSection.tsx b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/components/AudioSection.tsx new file mode 100644 index 0000000000..50ae0ba317 --- /dev/null +++ b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/components/AudioSection.tsx @@ -0,0 +1,29 @@ +import { RcTypography, RcCard, RcCardContent } from '@ringcentral/juno'; +import React from 'react'; + +import styles from '../styles.scss'; + +interface SectionProps { + label: string; + dataSign: string; + children: React.ReactNode; +} + +export function Section({ label, children, dataSign }: SectionProps) { + return ( +
+ + {label} + + + + {children} + + +
+ ); +} diff --git a/packages/ringcentral-widgets/components/AudioSettingsPanelV2/components/VolumeGauge.tsx b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/components/VolumeGauge.tsx new file mode 100644 index 0000000000..f18cb37821 --- /dev/null +++ b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/components/VolumeGauge.tsx @@ -0,0 +1,84 @@ +import { styled, palette2 } from '@ringcentral/juno'; +import React, { useMemo } from 'react'; + +type VolumeGaugeProps = { + size?: number; + volume: number; + isRecording?: boolean; +}; + +const inactiveDotColor = palette2('neutral', 'l02'); +const activeDotColor = palette2('success', 'b05'); +const recordingDotColor = palette2('danger', 'b04'); +const dotDistance = 4; +const dotWidth = 4; +const dotHeight = 14; +const dotCompleteWidth = dotWidth + dotDistance; + +const computeWidthFn = ({ size }: { size: number }) => { + return size * dotCompleteWidth; +}; + +const GaugeWrapper = styled.div` + display: inline-block; + vertical-align: middle; + height: ${dotHeight + 2}px; + margin: 0; + overflow: hidden; + width: ${computeWidthFn}px; +`; + +const computeVolumeFn = ({ volume }: { volume: number }) => { + return -50 + volume / 2; +}; + +const Gauge = styled.div` + display: inline-block; + white-space: nowrap; + width: auto; + transform: translateX(${computeVolumeFn}%); +`; + +const computeBackgroundFn = ({ + inactive, + isRecording, +}: { + inactive?: boolean; + isRecording?: boolean; +}) => { + if (!inactive && !isRecording) { + return activeDotColor; + } + return isRecording ? recordingDotColor : inactiveDotColor; +}; + +const Dot = styled.div` + display: inline-block; + width: ${dotWidth}px; + height: ${dotHeight}px; + margin: 1px ${dotDistance / 2}px; + border-radius: ${dotWidth / 2}px; + background: ${computeBackgroundFn}; +`; + +export const VolumeGauge = ({ + size = 16, + volume = 0, + isRecording = false, + ...rest +}: VolumeGaugeProps) => { + const dots = useMemo(() => Array(size).fill(0), [size]); + + return ( + + + {dots.map((_, i) => ( + + ))} + {dots.map((_, i) => ( + + ))} + + + ); +}; diff --git a/packages/ringcentral-widgets/components/AudioSettingsPanelV2/components/VolumeSlider.tsx b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/components/VolumeSlider.tsx new file mode 100644 index 0000000000..3c63f053aa --- /dev/null +++ b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/components/VolumeSlider.tsx @@ -0,0 +1,54 @@ +import { RcIcon, RcSlider, RcTypography } from '@ringcentral/juno'; +import { SpeakerDown, SpeakerUp } from '@ringcentral/juno-icon'; +import React, { type FC } from 'react'; + +import styles from '../styles.scss'; + +function toPercentValue(value: number) { + return Math.floor(value * 100); +} +function toValue(percent: number) { + return percent / 100; +} + +export const VolumeSlider: FC<{ + volume: number; + minVolume?: number; + maxVolume?: number; + onChange: (volume: number) => void; + dataSign: string; + label?: string; +}> = ({ volume, minVolume = 0, maxVolume = 1, onChange, label, dataSign }) => { + return ( +
+ {label ? ( + + {label} + + ) : null} +
+
+ +
+ onChange(toValue(value as number))} + /> +
+ +
+
+
+ ); +}; diff --git a/packages/ringcentral-widgets/components/AudioSettingsPanelV2/components/VolumeTester.tsx b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/components/VolumeTester.tsx new file mode 100644 index 0000000000..48949285cd --- /dev/null +++ b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/components/VolumeTester.tsx @@ -0,0 +1,119 @@ +import { + AUDIO_TYPE, + TEST_STATE, + TEST_TYPE, +} from '@ringcentral-integration/commons/modules/VolumeInspector'; +import { RcTypography, RcButton } from '@ringcentral/juno'; +import React, { useMemo } from 'react'; + +import { t } from '../i18n'; +import styles from '../styles.scss'; + +import { VolumeGauge } from './VolumeGauge'; + +type VolumeTesterProps = { + type: AUDIO_TYPE | null; + audioType: AUDIO_TYPE; + volume: number; + testState: number; + gaugeSize?: number; + handleButtonClick: (...args: any[]) => any; + isRecording?: boolean; + countDown?: number; + disabled?: boolean; +}; + +export const VolumeTester = (props: VolumeTesterProps) => { + const { + type, + audioType, + handleButtonClick, + gaugeSize = 16, + volume, + testState, + countDown, + isRecording, + disabled, + } = props; + + const isInactive = useMemo(() => { + return type && audioType !== type; + }, [audioType, type]); + + const convertVolumeForGauge = useMemo(() => { + if (isInactive) { + return 0; + } + return Math.floor(gaugeSize * volume) * (100 / gaugeSize); + }, [gaugeSize, volume, isInactive]); + + const buttonText = useMemo(() => { + const defaultText = t( + audioType === TEST_TYPE.microphone + ? 'startRecordButton' + : 'startTestButton', + ); + if (isInactive) { + return defaultText; + } + switch (testState) { + case TEST_STATE.RECORDS_AUDIO: + return t('stopRecordButton'); + case TEST_STATE.PLAYS_AUDIO: + return t('stopPlaybackButton'); + default: + return defaultText; + } + }, [testState, audioType, isInactive]); + + const hintText = useMemo(() => { + if (isInactive) { + return null; + } + if (testState === TEST_STATE.RECORDS_AUDIO && countDown) { + return t('stopRecordDescription', { + countDown, + }); + } + if (testState === TEST_STATE.PLAYS_AUDIO) { + return t( + audioType === TEST_TYPE.microphone + ? 'stopPlaybackDescription' + : 'stopTestDescription', + ); + } + return null; + }, [countDown, testState, audioType, isInactive]); + + return ( +
+ {hintText ? ( + + {hintText} + + ) : null} + + {buttonText} + + +
+ ); +}; diff --git a/packages/ringcentral-widgets/components/AudioSettingsPanelV2/components/index.tsx b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/components/index.tsx new file mode 100644 index 0000000000..764f6b5ab0 --- /dev/null +++ b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/components/index.tsx @@ -0,0 +1,4 @@ +export * from './AudioSection'; +export * from './AudioDeviceSelect'; +export * from './VolumeSlider'; +export * from './VolumeTester'; diff --git a/packages/ringcentral-widgets/components/AudioSettingsPanelV2/i18n/en-US.ts b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/i18n/en-US.ts new file mode 100644 index 0000000000..b85aedf569 --- /dev/null +++ b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/i18n/en-US.ts @@ -0,0 +1,23 @@ +export default { + title: 'Audio', + input: 'Input', + microphone: 'Microphone', + autoAdjustMicLevel: 'Automatically adjust my mic level', + output: 'Output', + speakerSource: 'Speaker source', + speakerVolume: 'Speaker volume', + ringtoneSource: 'Ringtone and notification source', + ringtoneVolume: 'Ringtone and notification volume', + ringtones: 'Ringtones', + noDevices: 'No devices', + off: 'Off', + noLabel: 'Unknown device', + stopRecordDescription: + 'Say something to check your mic level ({countDown} sec)', + stopPlaybackDescription: 'Playing your recording...', + startRecordButton: 'Test microphone', + stopRecordButton: 'Stop recording', + stopPlaybackButton: 'Stop test', + startTestButton: 'Test speaker', + stopTestDescription: 'Playing sample sound...', +} as const; diff --git a/packages/ringcentral-widgets/components/AudioSettingsPanelV2/i18n/index.ts b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/i18n/index.ts new file mode 100644 index 0000000000..3a13838340 --- /dev/null +++ b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/i18n/index.ts @@ -0,0 +1,14 @@ +import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; + +import type enUS from './en-US'; +// @ts-expect-error +import loadLocale from './loadLocale'; + +const i18n = new I18n(loadLocale); + +export const t = getTranslateFn(i18n); + +export type I18nKey = keyof typeof enUS; + +export default i18n; diff --git a/packages/ringcentral-widgets/components/AudioSettingsPanelV2/i18n/loadLocale.ts b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/i18n/loadLocale.ts new file mode 100644 index 0000000000..12b11cfa2e --- /dev/null +++ b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/i18n/loadLocale.ts @@ -0,0 +1 @@ +/* loadLocale */ diff --git a/packages/ringcentral-widgets/components/AudioSettingsPanelV2/index.tsx b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/index.tsx new file mode 100644 index 0000000000..fad292a3ca --- /dev/null +++ b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/index.tsx @@ -0,0 +1,2 @@ +export * from './AudioSettingsPanel.interface'; +export * from './AudioSettingsPanel'; diff --git a/packages/ringcentral-widgets/components/AudioSettingsPanelV2/styles.scss b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/styles.scss new file mode 100644 index 0000000000..fe53172e0b --- /dev/null +++ b/packages/ringcentral-widgets/components/AudioSettingsPanelV2/styles.scss @@ -0,0 +1,93 @@ +@import '../../lib/commonStyles/full-size.scss'; +@import '../../lib/commonStyles/layout.scss'; +@import '../../lib/commonStyles/colors.scss'; + +.root { + @include full-size; +} + +.content { + @include full-size; + overflow-y: auto; + height: 94%; + max-height: calc(100% - $header-height); + box-sizing: border-box; + background-color: white; + padding: 20px; +} + +/* RcSwitch */ +.switch { + margin-top: 0 !important; +} + +/* InputDevice & OutputDevice */ +.select { + margin-bottom: 16px; + + > div { + margin-top: 28px !important; + } +} + +/* AudioSection */ +.section { + display: flex; + flex-direction: column; + margin-bottom: 20px; +} + +.sectionTitle { + line-height: 24px !important; + margin-bottom: 4px; +} + +.sectionContent { + padding-bottom: 16px !important; +} + +/* VolumeSlider */ +.sliderContainer { + display: flex; + flex-direction: column; + margin-bottom: 16px; +} + +.sliderVolume { + display: flex; + flex-direction: row; +} + +.sliderVolumeIconContainer { + display: flex; + justify-content: center; + align-items: center; +} + +.sliderLabel { + margin-bottom: 16px; + display: inline-block; +} + +/* VolumeTester */ + +.testVolumeContainer { + margin-bottom: 10px; + flex-direction: column; + align-items: start; + display: flex; + gap: 16px; +} + +.testVolumeHint { + margin-top: 8px; +} + +.testVolumeButton { + button { + padding: 0 6px; + margin-right: 8px; + min-width: 104px; + min-width: 112px; + } +} diff --git a/packages/ringcentral-widgets/components/BackButton/index.tsx b/packages/ringcentral-widgets/components/BackButton/index.tsx index cf889b85c6..43cd1c06d7 100644 --- a/packages/ringcentral-widgets/components/BackButton/index.tsx +++ b/packages/ringcentral-widgets/components/BackButton/index.tsx @@ -1,21 +1,21 @@ +import clsx from 'clsx'; import React from 'react'; -import classnames from 'classnames'; - import dynamicsFont from '../../assets/DynamicsFont/DynamicsFont.scss'; + import styles from './styles.scss'; type BackButtonProps = { label?: string; showIcon?: boolean; }; -const BackButton: React.SFC = ({ label, showIcon }) => { +const BackButton: React.FC = ({ label, showIcon }) => { return ( {showIcon ? ( ) : null} {label ? ( diff --git a/packages/ringcentral-widgets/components/BackHeader/PageHeader/PageHeader.tsx b/packages/ringcentral-widgets/components/BackHeader/PageHeader/PageHeader.tsx new file mode 100644 index 0000000000..80830bacad --- /dev/null +++ b/packages/ringcentral-widgets/components/BackHeader/PageHeader/PageHeader.tsx @@ -0,0 +1,65 @@ +import { + palette2, + RcIconButton, + type RcIconButtonProps, + RcText, + RcTextProps, + spacing, + styled, +} from '@ringcentral/juno'; +import { ChevronLeft } from '@ringcentral/juno-icon'; +import type { ComponentProps, FunctionComponent } from 'react'; +import React from 'react'; + +// ! not modify this file directly, wait all components migrate to latest version +// TODO: this file be copy from libs/next-widgets/components/PageHeader, should sync file from there + +const _PageHeaderBack: FunctionComponent = (props) => { + return ( + + ); +}; + +export const PageHeaderBack = styled(_PageHeaderBack)``; + +const _PageHeaderRemain: FunctionComponent<{ className?: string }> = ({ + className, +}) => { + return ; +}; + +export const PageHeaderRemain = styled(_PageHeaderRemain)` + visibility: hidden; +`; + +export const PageHeader = styled.header.attrs( + (props: ComponentProps<'header'>) => ({ + 'data-sign': 'header', + ...props, + }), +)` + display: flex; + align-items: center; + background-color: ${palette2('neutral', 'b02')}; + border-bottom: 1px solid ${palette2('neutral', 'l02')}; + min-height: 38px; + + ${PageHeaderRemain}, + ${PageHeaderBack} { + margin-left: ${spacing(2)}; + } +`; + +export const PageHeaderTitle = styled(RcText).attrs((props: RcTextProps) => ({ + 'data-sign': 'headerTitle', + display: 'block', + ...props, +}))` + flex: 1 1 auto; + text-align: center; +`; diff --git a/packages/ringcentral-widgets/components/BackHeader/PageHeader/index.ts b/packages/ringcentral-widgets/components/BackHeader/PageHeader/index.ts new file mode 100644 index 0000000000..ba434ed109 --- /dev/null +++ b/packages/ringcentral-widgets/components/BackHeader/PageHeader/index.ts @@ -0,0 +1 @@ +export * from './PageHeader'; diff --git a/packages/ringcentral-widgets/components/BackHeader/index.tsx b/packages/ringcentral-widgets/components/BackHeader/index.tsx index cff39064d7..bed25f7065 100644 --- a/packages/ringcentral-widgets/components/BackHeader/index.tsx +++ b/packages/ringcentral-widgets/components/BackHeader/index.tsx @@ -1,9 +1,9 @@ +import clsx from 'clsx'; import React from 'react'; -import classnames from 'classnames'; - import dynamicsFont from '../../assets/DynamicsFont/DynamicsFont.scss'; import { Header } from '../Header'; + import styles from './styles.scss'; type BackHeaderProps = { @@ -16,12 +16,12 @@ type BackHeaderProps = { }[]; onBackClick?: (...args: any[]) => any; }; -const BackHeader: React.SFC = (props) => { +const BackHeader: React.FC = (props) => { const buttons = props.buttons || []; const defaultBackButton = ( ); buttons.push({ diff --git a/packages/ringcentral-widgets/components/BackHeaderV2/BackHeaderV2.tsx b/packages/ringcentral-widgets/components/BackHeaderV2/BackHeaderV2.tsx index 5f722262dd..2e305105ac 100644 --- a/packages/ringcentral-widgets/components/BackHeaderV2/BackHeaderV2.tsx +++ b/packages/ringcentral-widgets/components/BackHeaderV2/BackHeaderV2.tsx @@ -1,11 +1,9 @@ -import type { FunctionComponent } from 'react'; -import React, { useEffect, useRef, useState } from 'react'; - -import classnames from 'classnames'; - import type { RcIconButtonProps } from '@ringcentral/juno'; import { RcIconButton, RcTypography, styled } from '@ringcentral/juno'; import { ChevronLeft as chevronLeftSvg } from '@ringcentral/juno-icon'; +import clsx from 'clsx'; +import type { FunctionComponent } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import styles from './styles.scss'; @@ -25,6 +23,8 @@ const Title = styled(RcTypography)<{ $maxWidth?: number }>` max-width: ${({ $maxWidth }) => $maxWidth}px; `; +// TODO: use PageHeader to replace those + const BackHeader: FunctionComponent = ({ onBackClick, title = '', @@ -43,19 +43,15 @@ const BackHeader: FunctionComponent = ({ setMaxWidth(initWidth - (rightRef.current.clientWidth - 62)); } }, [currentLocale, isClassic]); - const rootClass = classnames( - styles.root, - isClassic && styles.classic, - className, - ); + const rootClass = clsx(styles.root, isClassic && styles.classic, className); // if right icon is empty then should occupy position to make title actually center align - const rightIconClass = classnames(styles.rightIcon, { + const rightIconClass = clsx(styles.rightIcon, { [styles.emptyRightIcon]: !rightIcon, }); return (
any; className?: string; name: string; + dataSign?: string; }; -const Badge: React.SFC = ({ +const Badge: React.FC = ({ className, name, children, onClick, + dataSign, }) => { return (
{children} diff --git a/packages/ringcentral-widgets/components/BasicCallInfo/BasicCallInfo.tsx b/packages/ringcentral-widgets/components/BasicCallInfo/BasicCallInfo.tsx index a8532b6674..a9f7ac0c65 100644 --- a/packages/ringcentral-widgets/components/BasicCallInfo/BasicCallInfo.tsx +++ b/packages/ringcentral-widgets/components/BasicCallInfo/BasicCallInfo.tsx @@ -1,17 +1,16 @@ -import type { FunctionComponent, MutableRefObject } from 'react'; -import React, { useEffect, useState } from 'react'; - -import classNames from 'classnames'; - import { px, RcIconButton } from '@ringcentral/juno'; import { ChevronLeft as chevronLeftSvg, ChevronRight as chevronRight, } from '@ringcentral/juno-icon'; +import clsx from 'clsx'; +import type { FunctionComponent, MutableRefObject } from 'react'; +import React, { useEffect, useState } from 'react'; import { AnimationPanel } from '../AnimationPanel'; import type { ShinyBarProps } from '../LogBasicInfoV2/ShinyBar'; import { ShinyBar } from '../LogBasicInfoV2/ShinyBar'; + import type { BasicCallInfoMainProps } from './BasicCallInfoMain'; import { BasicCallInfoMain } from './BasicCallInfoMain'; import type { CallInfoProps } from './CallInfo'; @@ -98,7 +97,7 @@ export const BasicCallInfo: FunctionComponent = ({ = ({ className, }) => { return ( -
+
diff --git a/packages/ringcentral-widgets/components/BasicCallInfo/CallIcon/CallIcon.tsx b/packages/ringcentral-widgets/components/BasicCallInfo/CallIcon/CallIcon.tsx index 9ddb71f2b3..7864e32cee 100644 --- a/packages/ringcentral-widgets/components/BasicCallInfo/CallIcon/CallIcon.tsx +++ b/packages/ringcentral-widgets/components/BasicCallInfo/CallIcon/CallIcon.tsx @@ -3,6 +3,7 @@ import React from 'react'; import InboundCall from '../../../assets/images/InboundCall.svg'; import OutboundCall from '../../../assets/images/OutboundCall.svg'; + import styles from './styles.scss'; interface CallIconProps { diff --git a/packages/ringcentral-widgets/components/BasicCallInfo/CallInfo/CallInfo.tsx b/packages/ringcentral-widgets/components/BasicCallInfo/CallInfo/CallInfo.tsx index 8d77b3b9d8..1eb6ea9869 100644 --- a/packages/ringcentral-widgets/components/BasicCallInfo/CallInfo/CallInfo.tsx +++ b/packages/ringcentral-widgets/components/BasicCallInfo/CallInfo/CallInfo.tsx @@ -3,6 +3,7 @@ import React from 'react'; import { TOOLTIP_LONG_DELAY_TIME } from '../../../lib/toolTipDelayTime'; import { Tooltip } from '../../Rcui/Tooltip'; + import styles from './styles.scss'; export interface CallInfoProps { diff --git a/packages/ringcentral-widgets/components/BasicCallInfo/CallInfoList/CallInfoList.tsx b/packages/ringcentral-widgets/components/BasicCallInfo/CallInfoList/CallInfoList.tsx index b28e2d46b3..38e5f74c14 100644 --- a/packages/ringcentral-widgets/components/BasicCallInfo/CallInfoList/CallInfoList.tsx +++ b/packages/ringcentral-widgets/components/BasicCallInfo/CallInfoList/CallInfoList.tsx @@ -1,14 +1,13 @@ +import { RcList, RcListItem } from '@ringcentral/juno'; +import clsx from 'clsx'; import type { FunctionComponent } from 'react'; import React from 'react'; -import classnames from 'classnames'; - -import { RcList, RcListItem } from '@ringcentral/juno'; - import copyButton from '../../CopyButton/CopyButton'; import CopyToClipboard from '../../CopyToClipboard'; import type { CallInfoProps } from '../CallInfo'; import { CallInfo } from '../CallInfo'; + import styles from './styles.scss'; export interface CallInfoListProps { @@ -30,11 +29,7 @@ export const CallInfoList: FunctionComponent = ({
{callInfos.map(({ attr, name, content, enableCopy }, i) => ( - + {enableCopy && (
diff --git a/packages/ringcentral-widgets/components/BasicCallInfo/CallSubject/CallSubject.tsx b/packages/ringcentral-widgets/components/BasicCallInfo/CallSubject/CallSubject.tsx index b139f829e2..617fe46ee8 100644 --- a/packages/ringcentral-widgets/components/BasicCallInfo/CallSubject/CallSubject.tsx +++ b/packages/ringcentral-widgets/components/BasicCallInfo/CallSubject/CallSubject.tsx @@ -3,6 +3,7 @@ import React from 'react'; import { TOOLTIP_LONG_DELAY_TIME } from '../../../lib/toolTipDelayTime'; import { Tooltip } from '../../Rcui/Tooltip'; + import styles from './styles.scss'; export interface CallSubjectProps { diff --git a/packages/ringcentral-widgets/components/BasicCallInfo/FollowInfo/FollowInfo.tsx b/packages/ringcentral-widgets/components/BasicCallInfo/FollowInfo/FollowInfo.tsx index da5162a05f..b8077e9ed7 100644 --- a/packages/ringcentral-widgets/components/BasicCallInfo/FollowInfo/FollowInfo.tsx +++ b/packages/ringcentral-widgets/components/BasicCallInfo/FollowInfo/FollowInfo.tsx @@ -3,6 +3,7 @@ import React from 'react'; import { TOOLTIP_LONG_DELAY_TIME } from '../../../lib/toolTipDelayTime'; import { Tooltip } from '../../Rcui/Tooltip'; + import styles from './styles.scss'; export interface FollowInfoProps { diff --git a/packages/ringcentral-widgets/components/BlockPanel/BlockPanel.tsx b/packages/ringcentral-widgets/components/BlockPanel/BlockPanel.tsx index 3c8baa6936..91ca5836c1 100644 --- a/packages/ringcentral-widgets/components/BlockPanel/BlockPanel.tsx +++ b/packages/ringcentral-widgets/components/BlockPanel/BlockPanel.tsx @@ -1,11 +1,11 @@ +import { combineProps, RcCircularProgress } from '@ringcentral/juno'; import type { FunctionComponent } from 'react'; import React from 'react'; -import { combineProps, RcCircularProgress } from '@ringcentral/juno'; - import type { BlockItem } from '../../modules/Block'; import type { SpinnerOverlayProps } from '../SpinnerOverlay'; import { SpinnerOverlay } from '../SpinnerOverlay'; + import styles from './styles.scss'; export type BlockPanelProps = { diff --git a/packages/ringcentral-widgets/components/Button/Button.tsx b/packages/ringcentral-widgets/components/Button/Button.tsx index 726b28167a..7632e1dd17 100644 --- a/packages/ringcentral-widgets/components/Button/Button.tsx +++ b/packages/ringcentral-widgets/components/Button/Button.tsx @@ -1,10 +1,8 @@ +import { useMountState } from '@ringcentral/juno'; +import clsx from 'clsx'; import type { FunctionComponent } from 'react'; import React, { useState } from 'react'; -import classnames from 'classnames'; - -import { useMountState } from '@ringcentral/juno'; - import styles from './styles.scss'; export interface ButtonProps { @@ -31,11 +29,7 @@ export const Button: FunctionComponent = ({ return (
= ({ }) => { const [avatarUrl, setAvatarUrl] = useState(null); - const svgCls = classnames( + const svgCls = clsx( styles.callAvatar, onClick ? styles.autoPointerEvents : styles.disabledPointerEvents, className, diff --git a/packages/ringcentral-widgets/components/CallBadge/i18n/en-US.ts b/packages/ringcentral-widgets/components/CallBadge/i18n/en-US.ts index a0add83bba..3eefe4c733 100644 --- a/packages/ringcentral-widgets/components/CallBadge/i18n/en-US.ts +++ b/packages/ringcentral-widgets/components/CallBadge/i18n/en-US.ts @@ -1,3 +1,3 @@ export default { activeCall: 'Active Call', -}; +} as const; diff --git a/packages/ringcentral-widgets/components/CallBadge/i18n/index.ts b/packages/ringcentral-widgets/components/CallBadge/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/ringcentral-widgets/components/CallBadge/i18n/index.ts +++ b/packages/ringcentral-widgets/components/CallBadge/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/ringcentral-widgets/components/CallBadge/index.tsx b/packages/ringcentral-widgets/components/CallBadge/index.tsx index 2b32c702db..2e0fbae451 100644 --- a/packages/ringcentral-widgets/components/CallBadge/index.tsx +++ b/packages/ringcentral-widgets/components/CallBadge/index.tsx @@ -1,9 +1,9 @@ -import React, { Component } from 'react'; - import callDirections from '@ringcentral-integration/commons/enums/callDirections'; import sessionStatus from '@ringcentral-integration/commons/modules/Webphone/sessionStatus'; +import React, { Component } from 'react'; import ActiveCallBadge from '../ActiveCallBadge'; + import i18n from './i18n'; import type { CallBadgeProps, CallBadgeState } from './types'; diff --git a/packages/ringcentral-widgets/components/CallCtrlContainer/CallCtrlContainer.tsx b/packages/ringcentral-widgets/components/CallCtrlContainer/CallCtrlContainer.tsx index d538be2d9d..1205ce9d1e 100644 --- a/packages/ringcentral-widgets/components/CallCtrlContainer/CallCtrlContainer.tsx +++ b/packages/ringcentral-widgets/components/CallCtrlContainer/CallCtrlContainer.tsx @@ -1,13 +1,13 @@ -import type { PropsWithChildren } from 'react'; -import React, { Component } from 'react'; - import callDirections from '@ringcentral-integration/commons/enums/callDirections'; import calleeTypes from '@ringcentral-integration/commons/enums/calleeTypes'; +import { sessionStatus } from '@ringcentral-integration/commons/modules/Webphone/sessionStatus'; import { sleep } from '@ringcentral-integration/commons/utils'; -import sessionStatus from '@ringcentral-integration/commons/modules/Webphone/sessionStatus'; +import type { PropsWithChildren } from 'react'; +import React, { Component } from 'react'; -import CallCtrlPanel from '../CallCtrlPanel'; import { callCtrlLayouts } from '../../enums/callCtrlLayouts'; +import CallCtrlPanel from '../CallCtrlPanel'; + import i18n from './i18n'; export type CallCtrlContainerProps = PropsWithChildren<{ @@ -73,6 +73,7 @@ export type CallCtrlContainerProps = PropsWithChildren<{ phoneNumber?: string; showPark?: boolean; controlBusy?: boolean; + callerIdName?: string; }>; type CallCtrlContainerState = { layout: any; @@ -163,8 +164,7 @@ export class CallCtrlContainer extends Component< static isLastCallEnded({ lastCallInfo }: any) { return !!(lastCallInfo && lastCallInfo.status === sessionStatus.finished); } - // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message - componentDidMount() { + override componentDidMount() { this._mounted = true; this._updateAvatarAndMatchIndex(this.props); this._updateCurrentConferenceCall(this.props); @@ -227,14 +227,25 @@ export class CallCtrlContainer extends Component< UNSAFE_componentWillReceiveProps(nextProps: any, nextState: any) { this._updateMergingPairToSessionId(nextProps, nextState); let layout = this.state.layout; - if (nextProps.session.id !== this.props.session.id) { + const sessionIdChanged = nextProps.session.id !== this.props.session.id; + + if ( + sessionIdChanged || + // FIX: RCINT-38564, when previous call end, layout should be updated + (layout === callCtrlLayouts.completeTransferCtrl && + !nextProps.session.warmTransferSessionId) + ) { layout = this.getLayout(this.props, nextProps); this.setState({ layout, }); - if (layout === callCtrlLayouts.normalCtrl) { - this._updateAvatarAndMatchIndex(nextProps); - } + } + + const beNormalCtrl = layout === callCtrlLayouts.normalCtrl; + const nameMatchesChanged = nextProps.nameMatches !== this.props.nameMatches; + + if (beNormalCtrl && (nameMatchesChanged || sessionIdChanged)) { + this._updateAvatarAndMatchIndex(nextProps); } else if ( layout === callCtrlLayouts.mergeCtrl && CallCtrlContainer.isLastCallEnded(this.props) === false && @@ -387,6 +398,7 @@ export class CallCtrlContainer extends Component< afterConfirmMerge={this.props.afterConfirmMerge} afterOnMerge={this.props.afterOnMerge} controlBusy={this.props.controlBusy} + callerIdName={this.props.callerIdName} > {this.props.children} @@ -422,4 +434,5 @@ CallCtrlContainer.defaultProps = { phoneNumber: null, showPark: false, controlBusy: false, + callerIdName: undefined, }; diff --git a/packages/ringcentral-widgets/components/CallCtrlContainer/i18n/en-US.ts b/packages/ringcentral-widgets/components/CallCtrlContainer/i18n/en-US.ts index 291bbbb81e..7e99c89930 100644 --- a/packages/ringcentral-widgets/components/CallCtrlContainer/i18n/en-US.ts +++ b/packages/ringcentral-widgets/components/CallCtrlContainer/i18n/en-US.ts @@ -2,4 +2,4 @@ export default { unknown: 'Unknown', anonymous: 'Anonymous', activeCalls: 'All Calls', -}; +} as const; diff --git a/packages/ringcentral-widgets/components/CallCtrlContainer/i18n/index.ts b/packages/ringcentral-widgets/components/CallCtrlContainer/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/ringcentral-widgets/components/CallCtrlContainer/i18n/index.ts +++ b/packages/ringcentral-widgets/components/CallCtrlContainer/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/ringcentral-widgets/components/CallCtrlPanel/index.tsx b/packages/ringcentral-widgets/components/CallCtrlPanel/index.tsx index ed7d2000e4..b56ebed2ed 100644 --- a/packages/ringcentral-widgets/components/CallCtrlPanel/index.tsx +++ b/packages/ringcentral-widgets/components/CallCtrlPanel/index.tsx @@ -1,3 +1,4 @@ +import type { LastCallInfo } from '@ringcentral-integration/commons/modules/ConferenceCall'; import React, { Component, memo } from 'react'; import callCtrlLayouts from '../../enums/callCtrlLayouts'; @@ -53,7 +54,7 @@ type CallCtrlPanelProps = { mergeDisabled?: boolean; conferenceCallEquipped?: boolean; hasConferenceCall?: boolean; - lastCallInfo?: object; + lastCallInfo?: LastCallInfo; conferenceCallParties?: any[]; getAvatarUrl?: (...args: any[]) => any; gotoParticipantsCtrl?: (...args: any[]) => any; @@ -62,6 +63,7 @@ type CallCtrlPanelProps = { afterOnMerge?: (...args: any[]) => any; actions?: any[]; controlBusy?: boolean; + callerIdName?: string; callQueueName?: string; showPark?: boolean; onCompleteTransfer?: (...args: any[]) => any; @@ -174,6 +176,7 @@ class CallCtrlPanel extends Component { conferenceCallEquipped, conferenceCallParties, controlBusy, + callerIdName, countryCode, currentLocale, direction, @@ -231,6 +234,7 @@ class CallCtrlPanel extends Component { } return ( = ({ diff --git a/packages/ringcentral-widgets/components/CallHistoryPanel/CallHistoryActions/CallHistoryActions.tsx b/packages/ringcentral-widgets/components/CallHistoryPanel/CallHistoryActions/CallHistoryActions.tsx index 08f066bddc..6d063a1e2d 100644 --- a/packages/ringcentral-widgets/components/CallHistoryPanel/CallHistoryActions/CallHistoryActions.tsx +++ b/packages/ringcentral-widgets/components/CallHistoryPanel/CallHistoryActions/CallHistoryActions.tsx @@ -1,9 +1,9 @@ +import clsx from 'clsx'; import type { FunctionComponent } from 'react'; import React from 'react'; -import classnames from 'classnames'; - import type { CallLogMenu } from '../CallHistoryPanel.interface'; + import { ActionButton } from './ActionButton'; import { MenuButton } from './MenuButton'; import styles from './styles.scss'; @@ -22,7 +22,7 @@ export const CallHistoryActions: FunctionComponent = ({ return (
{displayedButtons.map( diff --git a/packages/ringcentral-widgets/components/CallHistoryPanel/CallHistoryActions/MenuButton.tsx b/packages/ringcentral-widgets/components/CallHistoryPanel/CallHistoryActions/MenuButton.tsx index 234acbf77e..629dac3e9b 100644 --- a/packages/ringcentral-widgets/components/CallHistoryPanel/CallHistoryActions/MenuButton.tsx +++ b/packages/ringcentral-widgets/components/CallHistoryPanel/CallHistoryActions/MenuButton.tsx @@ -1,6 +1,3 @@ -import type { FunctionComponent } from 'react'; -import React, { memo, useState } from 'react'; - import { RcIcon, RcIconButton, @@ -8,6 +5,8 @@ import { RcMenuItem, RcSubMenu, } from '@ringcentral/juno'; +import type { FunctionComponent } from 'react'; +import React, { memo, useState } from 'react'; import type { CallLogMenuItem } from '../CallHistoryPanel.interface'; diff --git a/packages/ringcentral-widgets/components/CallHistoryPanel/CallHistoryItem/CallHistoryItem.tsx b/packages/ringcentral-widgets/components/CallHistoryPanel/CallHistoryItem/CallHistoryItem.tsx index a54cb0195b..8feb002aa2 100644 --- a/packages/ringcentral-widgets/components/CallHistoryPanel/CallHistoryItem/CallHistoryItem.tsx +++ b/packages/ringcentral-widgets/components/CallHistoryPanel/CallHistoryItem/CallHistoryItem.tsx @@ -1,8 +1,3 @@ -import type { FunctionComponent } from 'react'; -import React from 'react'; - -import classnames from 'classnames'; - import { callDirection } from '@ringcentral-integration/commons/enums/callDirections'; import { palette2, @@ -11,10 +6,14 @@ import { spacing, styled, } from '@ringcentral/juno'; +import clsx from 'clsx'; +import type { FunctionComponent } from 'react'; +import React from 'react'; import { CallHistoryActions } from '../CallHistoryActions'; import type { CallLog, CallLogMenu } from '../CallHistoryPanel.interface'; import { CallIcon } from '../CallIcon'; + import styles from './styles.scss'; export type CallHistoryItemProps = { @@ -43,9 +42,9 @@ export const CallHistoryItem: FunctionComponent = ({ return ( -
+
-
+
(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/ringcentral-widgets/components/CallIcon/index.tsx b/packages/ringcentral-widgets/components/CallIcon/index.tsx index 5e01b9aeff..c5cc1b6ffd 100644 --- a/packages/ringcentral-widgets/components/CallIcon/index.tsx +++ b/packages/ringcentral-widgets/components/CallIcon/index.tsx @@ -1,15 +1,15 @@ -import React from 'react'; - -import classnames from 'classnames'; - import callDirections from '@ringcentral-integration/commons/enums/callDirections'; import { IncallBorder as InCall, OutcallBorder as OutCall, + ConferenceBorder, } from '@ringcentral/juno-icon'; +import clsx from 'clsx'; +import React from 'react'; import dynamicsFont from '../../assets/DynamicsFont/DynamicsFont.scss'; import CallAvatar from '../CallAvatar'; + import styles from './styles.scss'; const callIconMap = { @@ -30,7 +30,7 @@ type CallIconProps = { avatarUrl?: string; newCallIcon?: boolean; }; -const CallIcon: React.SFC = ({ +const CallIcon: React.FC = ({ direction, ringing, inboundTitle, @@ -46,10 +46,10 @@ const CallIcon: React.SFC = ({ direction === callDirections.inbound ? inboundTitle : outboundTitle; let symbol; // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message - const CallDirectionIco = newCallIconMap[direction]; + const CallDirectionIco = newCallIconMap[direction || callDirections.outbound]; if (showAvatar) { symbol = ( -
+
= ({ } else if (newCallIcon) { symbol = (
- - - + {isOnConferenceCall ? ( + + + + ) : ( + + + + )}
); } else { symbol = (
= ({ + direction, + missed = false, + active = false, + ringing = false, + inboundTitle = '', + outboundTitle = '', + missedTitle = '', + type = '', +}) => { + let icon = null; + switch (type) { + case messageTypes.fax: { + icon = + direction === messageDirection.inbound ? ( + + + + ) : ( + + + + ); + break; + } + default: { + let title = null; + if (missed) { + title = missedTitle; + } else if (direction === callDirections.inbound) { + title = inboundTitle; + } else { + title = outboundTitle; + } + icon = ( + + ); + } + } + return
{icon}
; +}; diff --git a/packages/ringcentral-widgets/components/CallItem/CallItem.tsx b/packages/ringcentral-widgets/components/CallItem/CallItem.tsx new file mode 100644 index 0000000000..bd5ac35d66 --- /dev/null +++ b/packages/ringcentral-widgets/components/CallItem/CallItem.tsx @@ -0,0 +1,503 @@ +import callDirections from '@ringcentral-integration/commons/enums/callDirections'; +import type { Call } from '@ringcentral-integration/commons/interfaces/Call.interface'; +import { + getTelephoneDisplayName, + isInbound, + isMissed, + isRinging, +} from '@ringcentral-integration/commons/lib/callLogHelpers'; +import { formatDuration } from '@ringcentral-integration/commons/lib/formatDuration'; +import { formatNumber } from '@ringcentral-integration/commons/lib/formatNumber'; +import { parseNumber } from '@ringcentral-integration/commons/lib/parseNumber'; +import { useEventCallback, usePrevious } from '@ringcentral/juno'; +import clsx from 'clsx'; +import React, { FunctionComponent, useEffect, useRef, useState } from 'react'; + +import { checkShouldHideContactUser } from '../../lib/checkShouldHideContactUser'; +import { checkShouldHidePhoneNumber } from '../../lib/checkShouldHidePhoneNumber'; +import usePromise from '../../react-hooks/usePromise'; +import ActionMenu from '../ActionMenu'; +import { ContactDisplay } from '../ContactDisplay'; +import { CountdownTimer } from '../CountdownTimer'; +import DurationCounter from '../DurationCounter'; + +import { CallIcon } from './CallIcon'; +import i18n from './i18n'; +import styles from './styles.scss'; + +type CallItemProps = { + renderIndex?: number; + extended?: boolean; + // TODO: find correct type + call: Call & { + offset: number; + type: string; + }; + areaCode: string; + brand: string; + countryCode: string; + currentLocale: string; + onLogCall?: (...args: any[]) => any; + onViewContact?: (...args: any[]) => any; + onCreateContact?: (...args: any[]) => any; + createEntityTypes?: any[]; + onClickToDial?: (...args: any[]) => any; + onClickToSms?: (...args: any[]) => any; + isLoggedContact?: (...args: any[]) => any; + disableLinks?: boolean; + disableCallButton?: boolean; + disableClickToDial?: boolean; + outboundSmsPermission?: boolean; + internalSmsPermission?: boolean; + active: boolean; + dateTimeFormatter: (...args: any[]) => any; + isLogging?: boolean; + enableContactFallback?: boolean; + autoLog?: boolean; + showContactDisplayPlaceholder?: boolean; + sourceIcons?: any; + phoneTypeRenderer?: (...args: any[]) => any; + phoneSourceNameRenderer?: (...args: any[]) => any; + renderContactName?: (...args: any[]) => any; + renderSubContactName?: (...args: any[]) => any; + renderExtraButton?: (...args: any[]) => any; + contactDisplayStyle?: string; + externalViewEntity?: (...args: any[]) => any; + externalHasEntity?: (...args: any[]) => any; + shouldHideEntityButton?: (...args: any[]) => boolean; + readTextPermission?: boolean; + onSizeChanged?: (...args: any[]) => any; + onItemHeightChanged?: (renderIndex?: number) => void; + withAnimation?: boolean; + currentSiteCode?: string; + isMultipleSiteEnabled?: boolean; + showCallerIdName?: boolean; + showChooseEntityModal?: boolean; + enableCDC?: boolean; + maxExtensionNumberLength?: number; + formatPhone?: (...args: any[]) => any; + currentDelaySavingState?: any; +}; + +export const CallItem: FunctionComponent = ({ + currentSiteCode = '', + isMultipleSiteEnabled = false, + extended: extendedProp = false, + isLoggedContact = () => false, + isLogging: isLoggingProp = false, + disableClickToDial = false, + outboundSmsPermission = false, + internalSmsPermission = false, + disableLinks = false, + disableCallButton = false, + showContactDisplayPlaceholder = true, + showCallerIdName = false, + autoLog = false, + readTextPermission = true, + withAnimation = true, + showChooseEntityModal = true, + enableCDC = false, + maxExtensionNumberLength = 6, + formatPhone = (phoneNumber: string) => phoneNumber, + onSizeChanged, + renderIndex, + call, + brand, + currentLocale, + areaCode, + countryCode, + active, + onViewContact, + onCreateContact, + createEntityTypes, + onLogCall, + onClickToDial, + onClickToSms, + dateTimeFormatter, + enableContactFallback, + sourceIcons, + phoneSourceNameRenderer, + renderContactName, + renderSubContactName, + renderExtraButton, + contactDisplayStyle, + externalViewEntity: externalViewEntityProp, + externalHasEntity, + shouldHideEntityButton, + currentDelaySavingState, + onItemHeightChanged, +}) => { + const { + direction, + telephonyStatus, + result, + startTime, + duration, + activityMatches = [], + offset, + type, + toName, + } = call; + + const { delayUpdatingStartTime, delayUpdatingMinutes } = + currentDelaySavingState ?? {}; + + useEffect(() => { + if (onItemHeightChanged) { + onItemHeightChanged(renderIndex); + } + }, [currentDelaySavingState, onItemHeightChanged, renderIndex]); + + const getInitialContactIndex = useEventCallback(() => { + const contactMatches = getContactMatches()!; + + const activityMatches = call.activityMatches || []; + for (const activity of activityMatches) { + const index = contactMatches.findIndex((contact) => + isLoggedContact?.(call, activity, contact), + ); + if (index > -1) return index; + } + if (call.toNumberEntity) { + const index = contactMatches.findIndex( + (contact) => contact.id === call.toNumberEntity, + ); + return index; + } + return showContactDisplayPlaceholder ? -1 : 0; + }); + + const onSelectContact = (value: any, idx: any) => { + const selected = showContactDisplayPlaceholder + ? parseInt(idx, 10) - 1 + : parseInt(idx, 10); + userSelectionRef.current = true; + setSelected(selected); + if (autoLog) { + logCall(false, selected); + } + }; + + const toggleExtended = (e: any) => { + const contactDisplay = contactDisplayRef.current; + if (contactDisplay && contactDisplay.contains(e.target)) { + return; + } + if (onSizeChanged) { + onSizeChanged(renderIndex); + } else { + setExtended((extended) => !extended); + } + }; + + const getSelectedContact = (isSelected = selected) => { + const contactMatches = getContactMatches(); + return ( + (isSelected > -1 && contactMatches[isSelected]) || + (contactMatches.length === 1 && contactMatches[0]) || + null + ); + }; + + const getPhoneNumber = () => { + return ( + isInbound(call) + ? call.from!.phoneNumber || call.from!.extensionNumber + : call.to!.phoneNumber || call.to!.extensionNumber + ) as string; + }; + + const getContactMatches = () => { + return (isInbound(call) ? call.fromMatches : call.toMatches) || []; + }; + + const getFallbackContactName = () => { + return isInbound(call) ? call.from!.name : call.to?.name; + }; + + const logCall = async (redirect = true, isSelected = selected) => { + if (typeof onLogCall === 'function' && !isLogging) { + setIsLogging(true); + await mounted( + onLogCall({ + contact: getSelectedContact(isSelected), + call: call, + redirect, + }), + ); + + setIsLogging(false); + } + }; + + const viewSelectedContact = () => { + if (typeof onViewContact !== 'function') return; + + const activityMatches = (call && call.activityMatches) || []; + onViewContact({ + activityMatches, + contactMatches: getContactMatches(), + contact: getSelectedContact(), + phoneNumber: getPhoneNumber(), + }); + }; + + const createSelectedContact = async (entityType: any) => { + if (typeof onCreateContact === 'function' && !isCreating) { + setIsCreating(true); + const phoneNumber = getPhoneNumber(); + await mounted( + onCreateContact({ + phoneNumber, + name: enableContactFallback ? getFallbackContactName() : '', + entityType, + }), + ); + + setIsCreating(false); + } + }; + + const clickToSms = ({ countryCode, areaCode }: any) => { + if (!onClickToSms) return; + + const phoneNumber = getPhoneNumber()!; + const contact = getSelectedContact(); + if (contact) { + onClickToSms({ + ...contact, + phoneNumber, + }); + } else { + const formatted = formatNumber({ + phoneNumber, + countryCode, + areaCode, + maxExtensionLength: maxExtensionNumberLength, + }); + onClickToSms( + { + name: enableContactFallback ? getFallbackContactName() : formatted, + phoneNumber, + }, + true, + ); + } + }; + + const clickToDial = () => { + if (onClickToDial) { + const contact = getSelectedContact() || {}; + const phoneNumber = getPhoneNumber(); + + if (phoneNumber) { + onClickToDial({ + ...contact, + phoneNumber, + }); + } + } + }; + + const mounted = usePromise(); + const [selected, setSelected] = useState(getInitialContactIndex()); + const [extended, setExtended] = useState(extendedProp); + const [isLogging, setIsLogging] = useState(isLoggingProp); + const [isCreating, setIsCreating] = useState(false); + + const contactDisplayRef = useRef(null); + const userSelectionRef = useRef(false); + const previousCall = usePrevious(() => call); + + useEffect(() => { + setExtended(extendedProp); + }, [extendedProp]); + + useEffect(() => { + setIsLogging(isLoggingProp); + }, [isLoggingProp]); + + useEffect(() => { + if ( + !userSelectionRef.current && + previousCall && + (call.activityMatches !== previousCall?.activityMatches || + call.fromMatches !== previousCall?.fromMatches || + call.toMatches !== previousCall?.toMatches) + ) { + setSelected(getInitialContactIndex()); + } + }, [call, getInitialContactIndex, previousCall]); + + const phoneNumber = getPhoneNumber(); + const contactMatches = getContactMatches(); + const shouldHideNumber = + enableCDC && checkShouldHidePhoneNumber(phoneNumber, contactMatches); + const isContactMatchesHidden = + enableCDC && checkShouldHideContactUser(contactMatches); + const fallbackContactName = getFallbackContactName(); + + const ringing = isRinging(call); + + const missed = isInbound(call) && isMissed(call); + const parsedInfo = parseNumber({ + phoneNumber, + countryCode: countryCode as never, + areaCode, + }); + const isExtension = + !parsedInfo.hasPlus && + parsedInfo.number && + parsedInfo.number.length <= maxExtensionNumberLength; + const disableClickToSms = !( + onClickToSms && + (isExtension ? internalSmsPermission : outboundSmsPermission) + ); + + const durationEl = + typeof duration === 'undefined' ? ( + disableLinks ? ( + i18n.getString('unavailable', currentLocale) + ) : ( + + ) + ) : ( + formatDuration(duration) + ); + const dateEl = !active ? dateTimeFormatter({ utcTimestamp: startTime }) : ''; + const statusEl = active + ? i18n.getString((result || telephonyStatus) as never, currentLocale) + : ''; + + const contactName = renderContactName?.(call); + const subContactName = renderSubContactName?.(call); + const extraButton = renderExtraButton?.(call); + + const menuExtended = extended; + const selectedMatchContactType = getSelectedContact()?.type ?? ''; + const callerIdName = showCallerIdName + ? getTelephoneDisplayName(call) + : undefined; + + return ( +
+
+ +
+ { + contactDisplayRef.current = ref; + }} + className={clsx( + styles.contactDisplay, + contactDisplayStyle, + missed && styles.missed, + active && styles.active, + )} + selectClassName={styles.dropdownSelect} + brand={brand} + sourceIcons={sourceIcons} + phoneSourceNameRenderer={phoneSourceNameRenderer} + // TODO: find correct type + contactMatches={contactMatches as never} + selected={selected} + onSelectContact={onSelectContact} + disabled={disableLinks} + isLogging={isLogging} + fallBackName={fallbackContactName} + enableContactFallback={enableContactFallback} + areaCode={areaCode} + countryCode={countryCode} + phoneNumber={shouldHideNumber ? null : phoneNumber} + currentLocale={currentLocale} + stopPropagation={false} + showType={false} + showPlaceholder={showContactDisplayPlaceholder} + currentSiteCode={currentSiteCode} + isMultipleSiteEnabled={isMultipleSiteEnabled} + /> +
+ {durationEl} + {` | ${dateEl}${statusEl}`} +
+ {delayUpdatingStartTime && delayUpdatingMinutes && ( + + )} +
+ {extraButton} +
+ clickToSms({ countryCode, areaCode }) + : undefined + } + phoneNumber={phoneNumber} + disableLinks={disableLinks} + shouldHideEntityButton={() => { + if (shouldHideEntityButton) { + return shouldHideEntityButton(call); + } + return isContactMatchesHidden; + }} + disableCallButton={disableCallButton} + disableClickToDial={disableClickToDial} + isLogging={isLogging || isLogging} + isLogged={activityMatches.length > 0} + isCreating={isCreating} + addLogTitle={i18n.getString('addLog', currentLocale)} + editLogTitle={i18n.getString('editLog', currentLocale)} + textTitle={i18n.getString('text', currentLocale)} + callTitle={i18n.getString('call', currentLocale)} + createEntityTitle={i18n.getString('addEntity', currentLocale)} + viewEntityTitle={i18n.getString('viewDetails', currentLocale)} + externalViewEntity={ + externalViewEntityProp + ? () => externalViewEntityProp?.(call) + : undefined + } + externalHasEntity={externalHasEntity && externalHasEntity(call)} + disableClickToSms={disableClickToSms} + withAnimation={withAnimation} + showChooseEntityModal={showChooseEntityModal} + /> +
+ ); +}; diff --git a/packages/ringcentral-widgets/components/CallItem/i18n/en-US.ts b/packages/ringcentral-widgets/components/CallItem/i18n/en-US.ts index 76086d5ddc..28bac68904 100644 --- a/packages/ringcentral-widgets/components/CallItem/i18n/en-US.ts +++ b/packages/ringcentral-widgets/components/CallItem/i18n/en-US.ts @@ -17,4 +17,4 @@ export default { missedCall: 'Missed', inboundCall: 'Inbound', outboundCall: 'Outbound', -}; +} as const; diff --git a/packages/ringcentral-widgets/components/CallItem/i18n/es-419.ts b/packages/ringcentral-widgets/components/CallItem/i18n/es-419.ts index 541ee5efe2..effd77cd04 100644 --- a/packages/ringcentral-widgets/components/CallItem/i18n/es-419.ts +++ b/packages/ringcentral-widgets/components/CallItem/i18n/es-419.ts @@ -1,5 +1,5 @@ export default { - logging: "Iniciando sesión...", + logging: "Iniciando sesión…", logCall: "Registrar llamada", editLog: "Editar registro", select: "Seleccionar una grabación que coincida", @@ -13,7 +13,7 @@ export default { addEntity: "Crear nuevo", addLog: "Registro", text: "Mensaje", - call: "Llamada", + call: "Llamar", missedCall: "Perdida", inboundCall: "Entrante", outboundCall: "Saliente" diff --git a/packages/ringcentral-widgets/components/CallItem/i18n/es-ES.ts b/packages/ringcentral-widgets/components/CallItem/i18n/es-ES.ts index 683236e11d..a02c9c532d 100644 --- a/packages/ringcentral-widgets/components/CallItem/i18n/es-ES.ts +++ b/packages/ringcentral-widgets/components/CallItem/i18n/es-ES.ts @@ -13,7 +13,7 @@ export default { addEntity: "Crear elemento nuevo", addLog: "Registro", text: "Mensaje", - call: "Llamada", + call: "Llamar", missedCall: "Perdida", inboundCall: "Entrante", outboundCall: "Saliente" diff --git a/packages/ringcentral-widgets/components/CallItem/i18n/fi-FI.ts b/packages/ringcentral-widgets/components/CallItem/i18n/fi-FI.ts index 0a2d20ab34..d69fae35d0 100644 --- a/packages/ringcentral-widgets/components/CallItem/i18n/fi-FI.ts +++ b/packages/ringcentral-widgets/components/CallItem/i18n/fi-FI.ts @@ -13,7 +13,7 @@ export default { addEntity: "Luo uusi", addLog: "Loki", text: "Tekstiviesti", - call: "Puhelu", + call: "Soita", missedCall: "Vastaamaton", inboundCall: "Saapuva", outboundCall: "Lähtevät" diff --git a/packages/ringcentral-widgets/components/CallItem/i18n/index.ts b/packages/ringcentral-widgets/components/CallItem/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/ringcentral-widgets/components/CallItem/i18n/index.ts +++ b/packages/ringcentral-widgets/components/CallItem/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/ringcentral-widgets/components/CallItem/i18n/it-IT.ts b/packages/ringcentral-widgets/components/CallItem/i18n/it-IT.ts index 1397fadb61..ebf682d675 100644 --- a/packages/ringcentral-widgets/components/CallItem/i18n/it-IT.ts +++ b/packages/ringcentral-widgets/components/CallItem/i18n/it-IT.ts @@ -4,10 +4,10 @@ export default { editLog: "Modifica registro", select: "Seleziona un record corrispondente", OnHold: "In attesa", - Ringing: "Chiamata in corso", + Ringing: "Chiamata in arrivo", CallConnected: "Chiamata connessa", unknownUser: "Utente sconosciuto", - unknownNumber: "Anonimo", + unknownNumber: "Numero anonimo", unavailable: "Non disponibile", viewDetails: "Visualizza dettagli", addEntity: "Crea nuovo", diff --git a/packages/ringcentral-widgets/components/CallItem/index.ts b/packages/ringcentral-widgets/components/CallItem/index.ts new file mode 100644 index 0000000000..67e4f4da82 --- /dev/null +++ b/packages/ringcentral-widgets/components/CallItem/index.ts @@ -0,0 +1 @@ +export { CallItem as default } from './CallItem'; diff --git a/packages/ringcentral-widgets/components/CallItem/index.tsx b/packages/ringcentral-widgets/components/CallItem/index.tsx deleted file mode 100644 index 6b1e640673..0000000000 --- a/packages/ringcentral-widgets/components/CallItem/index.tsx +++ /dev/null @@ -1,774 +0,0 @@ -import React, { Component } from 'react'; - -import classnames from 'classnames'; -import PropTypes from 'prop-types'; - -/* eslint-disable react/destructuring-assignment */ -import callDirections from '@ringcentral-integration/commons/enums/callDirections'; -import messageDirection from '@ringcentral-integration/commons/enums/messageDirection'; -import messageTypes from '@ringcentral-integration/commons/enums/messageTypes'; -import { - isInbound, - isMissed, - isRinging, -} from '@ringcentral-integration/commons/lib/callLogHelpers'; -import { formatDuration } from '@ringcentral-integration/commons/lib/formatDuration'; -import { formatNumber } from '@ringcentral-integration/commons/lib/formatNumber'; -import parseNumber from '@ringcentral-integration/commons/lib/parseNumber'; - -import dynamicsFont from '../../assets/DynamicsFont/DynamicsFont.scss'; -import FaxInboundIcon from '../../assets/images/FaxInbound.svg'; -import FaxOutboundIcon from '../../assets/images/FaxOutbound.svg'; -import { checkShouldHideContactUser } from '../../lib/checkShouldHideContactUser'; -import { checkShouldHidePhoneNumber } from '../../lib/checkShouldHidePhoneNumber'; -import ActionMenu from '../ActionMenu'; -import ContactDisplay from '../ContactDisplay'; -import DurationCounter from '../DurationCounter'; -import i18n from './i18n'; -import styles from './styles.scss'; - -const callIconMap = { - [callDirections.inbound]: dynamicsFont.inbound, - [callDirections.outbound]: dynamicsFont.outbound, - missed: dynamicsFont.missed, -}; - -const CallIcon = ({ - direction, - missed, - active, - ringing, - inboundTitle, - outboundTitle, - missedTitle, - type, -}: any) => { - let icon = null; - switch (type) { - case messageTypes.fax: { - icon = - direction === messageDirection.inbound ? ( - - - - ) : ( - - - - ); - break; - } - default: { - let title = null; - if (missed) { - title = missedTitle; - } else if (direction === callDirections.inbound) { - title = inboundTitle; - } else { - title = outboundTitle; - } - icon = ( - - ); - } - } - return
{icon}
; -}; -CallIcon.propTypes = { - direction: PropTypes.string.isRequired, - missed: PropTypes.bool, - active: PropTypes.bool, - ringing: PropTypes.bool, - inboundTitle: PropTypes.string, - outboundTitle: PropTypes.string, - missedTitle: PropTypes.string, - type: PropTypes.string, -}; -CallIcon.defaultProps = { - missed: false, - active: false, - ringing: false, - inboundTitle: '', - outboundTitle: '', - missedTitle: '', - type: '', -}; - -class CallItem extends Component { - _loadingTimeout: any; - _mounted: any; - _userSelection: any; - contactDisplay: any; - constructor(props: any) { - super(props); - - this.state = { - selected: this.getInitialContactIndex(), - isLogging: false, - isCreating: false, - loading: true, - extended: false, - }; - this._userSelection = false; - } - - // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message - componentDidMount() { - this._mounted = true; - this._loadingTimeout = setTimeout(() => { - // clear timeout is probably not necessary - if (this._mounted) { - this.setState({ - loading: false, - }); - } - }, 10); - } - - // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message - UNSAFE_componentWillReceiveProps(nextProps: any) { - // @ts-expect-error TS(2339): Property 'call' does not exist on type 'Readonly<{... Remove this comment to see the full error message - const { call, extended } = this.props; - if ( - !this._userSelection && - (nextProps.call.activityMatches !== call.activityMatches || - nextProps.call.fromMatches !== call.fromMatches || - nextProps.call.toMatches !== call.toMatches) - ) { - this.setState({ - selected: this.getInitialContactIndex(nextProps), - }); - } - if (extended !== nextProps.extended && extended !== nextProps.extended) { - this.setState({ - extended: nextProps.extended, - }); - } - } - - // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message - componentWillUnmount() { - this._mounted = false; - if (this._loadingTimeout) { - clearTimeout(this._loadingTimeout); - this._loadingTimeout = null; - } - } - - onSelectContact = (value: any, idx: any) => { - // @ts-expect-error TS(2339): Property 'showContactDisplayPlaceholder' does not ... Remove this comment to see the full error message - const { showContactDisplayPlaceholder, autoLog } = this.props; - const selected = showContactDisplayPlaceholder - ? parseInt(idx, 10) - 1 - : parseInt(idx, 10); - this._userSelection = true; - this.setState({ - selected, - }); - if (autoLog) { - // @ts-expect-error TS(2345): Argument of type '{ redirect: boolean; selected: n... Remove this comment to see the full error message - this.logCall({ redirect: false, selected }); - } - }; - - toggleExtended = (e: any) => { - if (this.contactDisplay && this.contactDisplay.contains(e.target)) { - return; - } - // @ts-expect-error TS(2339): Property 'onSizeChanged' does not exist on type 'R... Remove this comment to see the full error message - const { onSizeChanged, renderIndex } = this.props; - if (onSizeChanged) { - onSizeChanged(renderIndex); - } else { - this.setState((state) => ({ - // @ts-expect-error TS(2339): Property 'extended' does not exist on type 'Readon... Remove this comment to see the full error message - extended: !state.extended, - })); - } - }; - - getInitialContactIndex(nextProps = this.props) { - const contactMatches = this.getContactMatches(nextProps); - // @ts-expect-error TS(2339): Property 'isLoggedContact' does not exist on type ... Remove this comment to see the full error message - const { isLoggedContact, showContactDisplayPlaceholder } = this.props; - // @ts-expect-error TS(2339): Property 'call' does not exist on type 'Readonly<{... Remove this comment to see the full error message - const activityMatches = nextProps.call.activityMatches; - // console.log('getInitialContactIndex:', nextProps.call.toNumberEntity); - for (const activity of activityMatches) { - const index = contactMatches.findIndex( - ( - contact: any, // TODO: find a better name or mechanism... - // @ts-expect-error TS(2339): Property 'call' does not exist on type 'Readonly<{... Remove this comment to see the full error message - ) => isLoggedContact(nextProps.call, activity, contact), - ); - if (index > -1) return index; - } - // @ts-expect-error TS(2339): Property 'call' does not exist on type 'Readonly<{... Remove this comment to see the full error message - if (nextProps.call.toNumberEntity) { - const index = contactMatches.findIndex( - // @ts-expect-error TS(2339): Property 'call' does not exist on type 'Readonly<{... Remove this comment to see the full error message - (contact: any) => contact.id === nextProps.call.toNumberEntity, - ); - return index; - } - return showContactDisplayPlaceholder ? -1 : 0; - } - - // @ts-expect-error TS(2339): Property 'selected' does not exist on type 'Readon... Remove this comment to see the full error message - getSelectedContact = (selected = this.state.selected) => { - const contactMatches = this.getContactMatches(); - return ( - (selected > -1 && contactMatches[selected]) || - (contactMatches.length === 1 && contactMatches[0]) || - null - ); - }; - - getPhoneNumber() { - // @ts-expect-error TS(2339): Property 'call' does not exist on type 'Readonly<{... Remove this comment to see the full error message - return isInbound(this.props.call) - ? // @ts-expect-error TS(2339): Property 'call' does not exist on type 'Readonly<{... Remove this comment to see the full error message - this.props.call.from.phoneNumber || this.props.call.from.extensionNumber - : // @ts-expect-error TS(2339): Property 'call' does not exist on type 'Readonly<{... Remove this comment to see the full error message - this.props.call.to.phoneNumber || this.props.call.to.extensionNumber; - } - - getContactMatches(nextProps = this.props) { - // @ts-expect-error TS(2339): Property 'call' does not exist on type 'Readonly<{... Remove this comment to see the full error message - return isInbound(nextProps.call) - ? // @ts-expect-error TS(2339): Property 'call' does not exist on type 'Readonly<{... Remove this comment to see the full error message - nextProps.call.fromMatches - : // @ts-expect-error TS(2339): Property 'call' does not exist on type 'Readonly<{... Remove this comment to see the full error message - nextProps.call.toMatches; - } - - getFallbackContactName() { - // @ts-expect-error TS(2339): Property 'call' does not exist on type 'Readonly<{... Remove this comment to see the full error message - return isInbound(this.props.call) - ? // @ts-expect-error TS(2339): Property 'call' does not exist on type 'Readonly<{... Remove this comment to see the full error message - this.props.call.from.name - : // @ts-expect-error TS(2339): Property 'call' does not exist on type 'Readonly<{... Remove this comment to see the full error message - this.props.call.to.name; - } - - // @ts-expect-error TS(2339): Property 'selected' does not exist on type 'Readon... Remove this comment to see the full error message - async logCall(redirect = true, selected = this.state.selected) { - if ( - // @ts-expect-error TS(2339): Property 'onLogCall' does not exist on type 'Reado... Remove this comment to see the full error message - typeof this.props.onLogCall === 'function' && - this._mounted && - // @ts-expect-error TS(2339): Property 'isLogging' does not exist on type 'Reado... Remove this comment to see the full error message - !this.state.isLogging - ) { - this.setState({ - isLogging: true, - }); - // @ts-expect-error TS(2339): Property 'onLogCall' does not exist on type 'Reado... Remove this comment to see the full error message - await this.props.onLogCall({ - contact: this.getSelectedContact(selected), - // @ts-expect-error TS(2339): Property 'call' does not exist on type 'Readonly<{... Remove this comment to see the full error message - call: this.props.call, - redirect, - }); - if (this._mounted) { - this.setState({ - isLogging: false, - }); - } - } - } - - // @ts-expect-error TS(2300): Duplicate identifier 'logCall'. - logCall = this.logCall.bind(this); - - viewSelectedContact = () => { - // @ts-expect-error TS(2339): Property 'onViewContact' does not exist on type 'R... Remove this comment to see the full error message - if (typeof this.props.onViewContact === 'function') { - // @ts-expect-error TS(2339): Property 'call' does not exist on type 'Readonly<{... Remove this comment to see the full error message - const { call } = this.props; - const activityMatches = (call && call.activityMatches) || []; - // @ts-expect-error TS(2339): Property 'onViewContact' does not exist on type 'R... Remove this comment to see the full error message - this.props.onViewContact({ - activityMatches, - contactMatches: this.getContactMatches(), - contact: this.getSelectedContact(), - phoneNumber: this.getPhoneNumber(), - }); - } - }; - - createSelectedContact = async (entityType: any) => { - // console.log('click createSelectedContact!!', entityType); - if ( - // @ts-expect-error TS(2339): Property 'onCreateContact' does not exist on type ... Remove this comment to see the full error message - typeof this.props.onCreateContact === 'function' && - this._mounted && - // @ts-expect-error TS(2339): Property 'isCreating' does not exist on type 'Read... Remove this comment to see the full error message - !this.state.isCreating - ) { - this.setState({ - isCreating: true, - }); - // console.log('start to create: isCreating...', this.state.isCreating); - const phoneNumber = this.getPhoneNumber(); - // @ts-expect-error TS(2339): Property 'onCreateContact' does not exist on type ... Remove this comment to see the full error message - await this.props.onCreateContact({ - phoneNumber, - // @ts-expect-error TS(2339): Property 'enableContactFallback' does not exist on... Remove this comment to see the full error message - name: this.props.enableContactFallback - ? this.getFallbackContactName() - : '', - entityType, - }); - - if (this._mounted) { - this.setState({ - isCreating: false, - }); - // console.log('created: isCreating...', this.state.isCreating); - } - } - }; - - clickToSms = ({ countryCode, areaCode }: any) => { - // @ts-expect-error TS(2339): Property 'onClickToSms' does not exist on type 'Re... Remove this comment to see the full error message - if (this.props.onClickToSms) { - const phoneNumber = this.getPhoneNumber(); - const contact = this.getSelectedContact(); - if (contact) { - // @ts-expect-error TS(2339): Property 'onClickToSms' does not exist on type 'Re... Remove this comment to see the full error message - this.props.onClickToSms({ - ...contact, - phoneNumber, - }); - } else { - const formatted = formatNumber({ - phoneNumber, - countryCode, - areaCode, - // @ts-expect-error TS(2339): Property 'maxExtensionNumberLength' does not exist... Remove this comment to see the full error message - maxExtensionLength: this.props.maxExtensionNumberLength, - }); - // @ts-expect-error TS(2339): Property 'onClickToSms' does not exist on type 'Re... Remove this comment to see the full error message - this.props.onClickToSms( - { - // @ts-expect-error TS(2339): Property 'enableContactFallback' does not exist on... Remove this comment to see the full error message - name: this.props.enableContactFallback - ? this.getFallbackContactName() - : formatted, - phoneNumber, - }, - true, - ); - } - } - }; - - clickToDial = () => { - // @ts-expect-error TS(2339): Property 'onClickToDial' does not exist on type 'R... Remove this comment to see the full error message - if (this.props.onClickToDial) { - const contact = this.getSelectedContact() || {}; - const phoneNumber = this.getPhoneNumber(); - - if (phoneNumber) { - // @ts-expect-error TS(2339): Property 'onClickToDial' does not exist on type 'R... Remove this comment to see the full error message - this.props.onClickToDial({ - ...contact, - phoneNumber, - }); - } - } - }; - - // @ts-expect-error TS(2339): Property 'externalViewEntity' does not exist on ty... Remove this comment to see the full error message - externalViewEntity = () => this.props.externalViewEntity(this.props.call); - - // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message - render() { - // @ts-expect-error TS(2339): Property 'loading' does not exist on type 'Readonl... Remove this comment to see the full error message - if (this.state.loading) { - return
; - } - const { - // @ts-expect-error TS(2339): Property 'call' does not exist on type 'Readonly<{... Remove this comment to see the full error message - call: { - direction, - telephonyStatus, - result, - startTime, - duration, - activityMatches, - offset, - type, - toName, - }, - // @ts-expect-error TS(2339): Property 'brand' does not exist on type 'Readonly<... Remove this comment to see the full error message - brand, - // @ts-expect-error TS(2339): Property 'currentLocale' does not exist on type 'R... Remove this comment to see the full error message - currentLocale, - // @ts-expect-error TS(2339): Property 'currentSiteCode' does not exist on type ... Remove this comment to see the full error message - currentSiteCode, - // @ts-expect-error TS(2339): Property 'isMultipleSiteEnabled' does not exist on... Remove this comment to see the full error message - isMultipleSiteEnabled, - // @ts-expect-error TS(2339): Property 'areaCode' does not exist on type 'Readon... Remove this comment to see the full error message - areaCode, - // @ts-expect-error TS(2339): Property 'countryCode' does not exist on type 'Rea... Remove this comment to see the full error message - countryCode, - // @ts-expect-error TS(2339): Property 'disableLinks' does not exist on type 'Re... Remove this comment to see the full error message - disableLinks, - // @ts-expect-error TS(2339): Property 'disableCallButton' does not exist on typ... Remove this comment to see the full error message - disableCallButton, - // @ts-expect-error TS(2339): Property 'disableClickToDial' does not exist on ty... Remove this comment to see the full error message - disableClickToDial, - // @ts-expect-error TS(2339): Property 'outboundSmsPermission' does not exist on... Remove this comment to see the full error message - outboundSmsPermission, - // @ts-expect-error TS(2339): Property 'internalSmsPermission' does not exist on... Remove this comment to see the full error message - internalSmsPermission, - // @ts-expect-error TS(2339): Property 'active' does not exist on type 'Readonly... Remove this comment to see the full error message - active, - // @ts-expect-error TS(2339): Property 'onViewContact' does not exist on type 'R... Remove this comment to see the full error message - onViewContact, - // @ts-expect-error TS(2339): Property 'onCreateContact' does not exist on type ... Remove this comment to see the full error message - onCreateContact, - // @ts-expect-error TS(2339): Property 'createEntityTypes' does not exist on typ... Remove this comment to see the full error message - createEntityTypes, - // @ts-expect-error TS(2339): Property 'onLogCall' does not exist on type 'Reado... Remove this comment to see the full error message - onLogCall, - // @ts-expect-error TS(2339): Property 'onClickToDial' does not exist on type 'R... Remove this comment to see the full error message - onClickToDial, - // @ts-expect-error TS(2339): Property 'onClickToSms' does not exist on type 'Re... Remove this comment to see the full error message - onClickToSms, - // @ts-expect-error TS(2339): Property 'dateTimeFormatter' does not exist on typ... Remove this comment to see the full error message - dateTimeFormatter, - // @ts-expect-error TS(2339): Property 'isLogging' does not exist on type 'Reado... Remove this comment to see the full error message - isLogging, - // @ts-expect-error TS(2339): Property 'enableContactFallback' does not exist on... Remove this comment to see the full error message - enableContactFallback, - // @ts-expect-error TS(2339): Property 'showContactDisplayPlaceholder' does not ... Remove this comment to see the full error message - showContactDisplayPlaceholder, - // @ts-expect-error TS(2339): Property 'sourceIcons' does not exist on type 'Rea... Remove this comment to see the full error message - sourceIcons, - // @ts-expect-error TS(2339): Property 'phoneTypeRenderer' does not exist on typ... Remove this comment to see the full error message - phoneTypeRenderer, - // @ts-expect-error TS(2339): Property 'phoneSourceNameRenderer' does not exist ... Remove this comment to see the full error message - phoneSourceNameRenderer, - // @ts-expect-error TS(2339): Property 'renderContactName' does not exist on typ... Remove this comment to see the full error message - renderContactName, - // @ts-expect-error TS(2339): Property 'renderSubContactName' does not exist on ... Remove this comment to see the full error message - renderSubContactName, - // @ts-expect-error TS(2339): Property 'renderExtraButton' does not exist on typ... Remove this comment to see the full error message - renderExtraButton, - // @ts-expect-error TS(2339): Property 'contactDisplayStyle' does not exist on t... Remove this comment to see the full error message - contactDisplayStyle, - // @ts-expect-error TS(2339): Property 'externalViewEntity' does not exist on ty... Remove this comment to see the full error message - externalViewEntity, - // @ts-expect-error TS(2339): Property 'externalHasEntity' does not exist on typ... Remove this comment to see the full error message - externalHasEntity, - // @ts-expect-error TS(2339): Property 'readTextPermission' does not exist on ty... Remove this comment to see the full error message - readTextPermission, - // @ts-expect-error TS(2339): Property 'withAnimation' does not exist on type 'R... Remove this comment to see the full error message - withAnimation, - // @ts-expect-error TS(2339): Property 'showChooseEntityModal' does not exist on... Remove this comment to see the full error message - showChooseEntityModal, - // @ts-expect-error TS(2339): Property 'enableCDC' does not exist on type 'Reado... Remove this comment to see the full error message - enableCDC, - // @ts-expect-error TS(2339): Property 'maxExtensionNumberLength' does not exist... Remove this comment to see the full error message - maxExtensionNumberLength, - // @ts-expect-error TS(2339): Property 'formatPhone' does not exist on type 'Rea... Remove this comment to see the full error message - formatPhone, - } = this.props; - const phoneNumber = this.getPhoneNumber(); - const contactMatches = this.getContactMatches(); - const shouldHideNumber = - enableCDC && checkShouldHidePhoneNumber(phoneNumber, contactMatches); - const isContactMatchesHidden = - enableCDC && checkShouldHideContactUser(contactMatches); - const fallbackContactName = this.getFallbackContactName(); - // @ts-expect-error TS(2339): Property 'call' does not exist on type 'Readonly<{... Remove this comment to see the full error message - const ringing = isRinging(this.props.call); - // @ts-expect-error TS(2339): Property 'call' does not exist on type 'Readonly<{... Remove this comment to see the full error message - const missed = isInbound(this.props.call) && isMissed(this.props.call); - const parsedInfo = parseNumber({ - phoneNumber, - countryCode, - areaCode, - }); - const isExtension = - !parsedInfo.hasPlus && - parsedInfo.number && - parsedInfo.number.length <= maxExtensionNumberLength; - const disableClickToSms = !( - onClickToSms && - (isExtension ? internalSmsPermission : outboundSmsPermission) - ); - - let durationEl = null; - if (typeof duration === 'undefined') { - durationEl = disableLinks ? ( - i18n.getString('unavailable', currentLocale) - ) : ( - - ); - } else { - durationEl = formatDuration(duration); - } - let dateEl = ''; - if (!active) { - dateEl = dateTimeFormatter({ utcTimestamp: startTime }); - } - let statusEl = ''; - if (active) { - statusEl = i18n.getString(result || telephonyStatus, currentLocale); - } - const contactName = - typeof renderContactName === 'function' - ? // @ts-expect-error TS(2339): Property 'call' does not exist on type 'Readonly<{... Remove this comment to see the full error message - renderContactName(this.props.call) - : undefined; - const subContactName = - typeof renderSubContactName === 'function' - ? // @ts-expect-error TS(2339): Property 'call' does not exist on type 'Readonly<{... Remove this comment to see the full error message - renderSubContactName(this.props.call) - : undefined; - const extraButton = - typeof renderExtraButton === 'function' - ? // @ts-expect-error TS(2339): Property 'call' does not exist on type 'Readonly<{... Remove this comment to see the full error message - renderExtraButton(this.props.call) - : undefined; - // @ts-expect-error TS(2339): Property 'extended' does not exist on type 'Readon... Remove this comment to see the full error message - const menuExtended = this.props.extended || this.state.extended; - const selectedMatchContactType = this.getSelectedContact()?.type ?? ''; - - return ( -
-
- -
- { - this.contactDisplay = ref; - }} - className={classnames( - styles.contactDisplay, - contactDisplayStyle, - missed && styles.missed, - active && styles.active, - )} - selectClassName={styles.dropdownSelect} - brand={brand} - sourceIcons={sourceIcons} - // @ts-expect-error TS(2322): Type '{ formatPhone: any; missed: boolean; isOnCon... Remove this comment to see the full error message - phoneTypeRenderer={phoneTypeRenderer} - phoneSourceNameRenderer={phoneSourceNameRenderer} - contactMatches={contactMatches} - // @ts-expect-error TS(2339): Property 'selected' does not exist on type 'Readon... Remove this comment to see the full error message - selected={this.state.selected} - onSelectContact={this.onSelectContact} - disabled={disableLinks} - // @ts-expect-error TS(2339): Property 'isLogging' does not exist on type 'Reado... Remove this comment to see the full error message - isLogging={isLogging || this.state.isLogging} - fallBackName={fallbackContactName} - enableContactFallback={enableContactFallback} - areaCode={areaCode} - countryCode={countryCode} - phoneNumber={shouldHideNumber ? null : phoneNumber} - currentLocale={currentLocale} - stopPropagation={false} - showType={false} - showPlaceholder={showContactDisplayPlaceholder} - currentSiteCode={currentSiteCode} - isMultipleSiteEnabled={isMultipleSiteEnabled} - /> -
- {durationEl} - {` | ${dateEl}${statusEl}`} -
-
- {extraButton} -
- this.clickToSms({ countryCode, areaCode }) - : undefined - } - phoneNumber={phoneNumber} - disableLinks={disableLinks} - shouldHideEntityButton={isContactMatchesHidden} - disableCallButton={disableCallButton} - disableClickToDial={disableClickToDial} - // @ts-expect-error TS(2339): Property 'isLogging' does not exist on type 'Reado... Remove this comment to see the full error message - isLogging={isLogging || this.state.isLogging} - isLogged={activityMatches.length > 0} - // @ts-expect-error TS(2339): Property 'isCreating' does not exist on type 'Read... Remove this comment to see the full error message - isCreating={this.state.isCreating} - addLogTitle={i18n.getString('addLog', currentLocale)} - editLogTitle={i18n.getString('editLog', currentLocale)} - textTitle={i18n.getString('text', currentLocale)} - callTitle={i18n.getString('call', currentLocale)} - createEntityTitle={i18n.getString('addEntity', currentLocale)} - viewEntityTitle={i18n.getString('viewDetails', currentLocale)} - externalViewEntity={externalViewEntity && this.externalViewEntity} - externalHasEntity={ - // @ts-expect-error TS(2339): Property 'call' does not exist on type 'Readonly<{... Remove this comment to see the full error message - externalHasEntity && externalHasEntity(this.props.call) - } - disableClickToSms={disableClickToSms} - withAnimation={withAnimation} - showChooseEntityModal={showChooseEntityModal} - /> -
- ); - } -} - -// @ts-expect-error TS(2339): Property 'propTypes' does not exist on type 'typeo... Remove this comment to see the full error message -CallItem.propTypes = { - renderIndex: PropTypes.number, - extended: PropTypes.bool, - call: PropTypes.shape({ - result: PropTypes.string, - duration: PropTypes.number, - offset: PropTypes.number, - type: PropTypes.string, - toName: PropTypes.string, - direction: PropTypes.string.isRequired, - telephonyStatus: PropTypes.string, - startTime: PropTypes.number.isRequired, - activityMatches: PropTypes.array.isRequired, - fromMatches: PropTypes.array.isRequired, - toMatches: PropTypes.array.isRequired, - from: PropTypes.shape({ - phoneNumber: PropTypes.string, - extensionNumber: PropTypes.string, - name: PropTypes.string, - }).isRequired, - to: PropTypes.shape({ - phoneNumber: PropTypes.string, - extensionNumber: PropTypes.string, - name: PropTypes.string, - }), - webphoneSession: PropTypes.object, - }).isRequired, - areaCode: PropTypes.string.isRequired, - brand: PropTypes.string.isRequired, - countryCode: PropTypes.string.isRequired, - currentLocale: PropTypes.string.isRequired, - onLogCall: PropTypes.func, - onViewContact: PropTypes.func, - onCreateContact: PropTypes.func, - createEntityTypes: PropTypes.array, - onClickToDial: PropTypes.func, - onClickToSms: PropTypes.func, - isLoggedContact: PropTypes.func, - disableLinks: PropTypes.bool, - disableCallButton: PropTypes.bool, - disableClickToDial: PropTypes.bool, - outboundSmsPermission: PropTypes.bool, - internalSmsPermission: PropTypes.bool, - active: PropTypes.bool.isRequired, - dateTimeFormatter: PropTypes.func.isRequired, - isLogging: PropTypes.bool, - enableContactFallback: PropTypes.bool, - autoLog: PropTypes.bool, - showContactDisplayPlaceholder: PropTypes.bool, - sourceIcons: PropTypes.object, - phoneTypeRenderer: PropTypes.func, - phoneSourceNameRenderer: PropTypes.func, - renderContactName: PropTypes.func, - renderSubContactName: PropTypes.func, - renderExtraButton: PropTypes.func, - contactDisplayStyle: PropTypes.string, - externalViewEntity: PropTypes.func, - externalHasEntity: PropTypes.func, - readTextPermission: PropTypes.bool, - onSizeChanged: PropTypes.func, - withAnimation: PropTypes.bool, - currentSiteCode: PropTypes.string, - isMultipleSiteEnabled: PropTypes.bool, - showChooseEntityModal: PropTypes.bool, - enableCDC: PropTypes.bool, - maxExtensionNumberLength: PropTypes.number, - formatPhone: PropTypes.func, -}; - -// @ts-expect-error TS(2339): Property 'defaultProps' does not exist on type 'ty... Remove this comment to see the full error message -CallItem.defaultProps = { - currentSiteCode: '', - isMultipleSiteEnabled: false, - renderIndex: undefined, - extended: false, - onLogCall: undefined, - onClickToDial: undefined, - onClickToSms: undefined, - onViewContact: undefined, - onCreateContact: undefined, - createEntityTypes: undefined, - isLoggedContact: () => false, - isLogging: false, - disableClickToDial: false, - outboundSmsPermission: false, - internalSmsPermission: false, - disableLinks: false, - disableCallButton: false, - enableContactFallback: undefined, - showContactDisplayPlaceholder: true, - autoLog: false, - sourceIcons: undefined, - phoneTypeRenderer: undefined, - phoneSourceNameRenderer: undefined, - renderContactName: undefined, - renderSubContactName: undefined, - renderExtraButton: undefined, - contactDisplayStyle: undefined, - externalViewEntity: undefined, - externalHasEntity: undefined, - readTextPermission: true, - onSizeChanged: undefined, - withAnimation: true, - showChooseEntityModal: true, - enableCDC: false, - maxExtensionNumberLength: 6, - formatPhone: (phoneNumber: string) => phoneNumber, -}; - -export default CallItem; diff --git a/packages/ringcentral-widgets/components/CallList/index.tsx b/packages/ringcentral-widgets/components/CallList/index.tsx index dbe1a56db0..2ea1c3250a 100644 --- a/packages/ringcentral-widgets/components/CallList/index.tsx +++ b/packages/ringcentral-widgets/components/CallList/index.tsx @@ -20,20 +20,17 @@ type CallListProps = { onClickToDial?: (...args: any[]) => any; onClickToSms?: (...args: any[]) => any; isLoggedContact?: (...args: any[]) => any; - loggingMap?: object; + loggingMap?: any; disableLinks?: boolean; disableCallButton?: boolean; disableClickToDial?: boolean; outboundSmsPermission?: boolean; internalSmsPermission?: boolean; dateTimeFormatter: (...args: any[]) => any; - webphoneAnswer?: (...args: any[]) => any; - webphoneReject?: (...args: any[]) => any; - webphoneHangup?: (...args: any[]) => any; - webphoneResume?: (...args: any[]) => any; enableContactFallback?: boolean; autoLog?: boolean; showContactDisplayPlaceholder?: boolean; + showCallerIdName?: boolean; sourceIcons?: object; phoneTypeRenderer?: (...args: any[]) => any; phoneSourceNameRenderer?: (...args: any[]) => any; @@ -42,153 +39,113 @@ type CallListProps = { contactDisplayStyle?: string; externalViewEntity?: (...args: any[]) => any; externalHasEntity?: (...args: any[]) => any; + shouldHideEntityButton?: (...args: any[]) => boolean; readTextPermission?: boolean; enableCDC?: boolean; - maxExtensionNumberLength: number; + maxExtensionNumberLength?: number; + callsDelaySavingState?: Record; }; -class CallList extends PureComponent { - // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message - render() { - const { - className, - brand, - currentLocale, - currentSiteCode, - isMultipleSiteEnabled, - calls, - areaCode, - countryCode, - onViewContact, - onCreateContact, - createEntityTypes, - onLogCall, - onClickToDial, - onClickToSms, - isLoggedContact, - disableLinks, - disableCallButton, - disableClickToDial, - outboundSmsPermission, - internalSmsPermission, - active, - dateTimeFormatter, - loggingMap, - webphoneAnswer, - webphoneReject, - webphoneHangup, - webphoneResume, - enableContactFallback, - autoLog, - showContactDisplayPlaceholder, - sourceIcons, - phoneTypeRenderer, - phoneSourceNameRenderer, - renderContactName, - renderExtraButton, - contactDisplayStyle, - externalViewEntity, - externalHasEntity, - readTextPermission, - enableCDC, - maxExtensionNumberLength, - } = this.props; - if (calls && calls.length) { - return ( -
- {calls.map((call, index) => ( - - ))} -
- ); - } + +const CallList: React.FC = ({ + className, + brand, + currentLocale, + calls, + areaCode, + countryCode, + onViewContact, + onCreateContact, + createEntityTypes, + onLogCall, + onClickToDial, + onClickToSms, + isLoggedContact, + dateTimeFormatter, + enableContactFallback, + showCallerIdName, + sourceIcons, + phoneTypeRenderer, + phoneSourceNameRenderer, + renderContactName, + renderExtraButton, + contactDisplayStyle, + externalViewEntity, + externalHasEntity, + shouldHideEntityButton, + maxExtensionNumberLength, + currentSiteCode = '', + isMultipleSiteEnabled = false, + active = false, + disableLinks = false, + disableCallButton = false, + disableClickToDial = false, + outboundSmsPermission = false, + internalSmsPermission = false, + loggingMap = {}, + showContactDisplayPlaceholder = true, + autoLog = false, + readTextPermission = true, + enableCDC = false, + callsDelaySavingState, +}) => { + if (calls && calls.length) { return (
- {/* @ts-expect-error TS(2322): Type 'boolean | undefined' is not */} - + {calls.map((call, index) => ( + + ))}
); } -} -// @ts-expect-error TS(2339): Property 'defaultProps' does not exist on type 'ty... Remove this comment to see the full error message -CallList.defaultProps = { - currentSiteCode: '', - isMultipleSiteEnabled: false, - className: null, - active: false, - disableLinks: false, - disableCallButton: false, - disableClickToDial: false, - outboundSmsPermission: false, - internalSmsPermission: false, - onViewContact: undefined, - onCreateContact: undefined, - createEntityTypes: undefined, - onLogCall: undefined, - isLoggedContact: undefined, - onClickToDial: undefined, - onClickToSms: undefined, - loggingMap: {}, - webphoneAnswer: undefined, - webphoneReject: undefined, - webphoneHangup: undefined, - webphoneResume: undefined, - enableContactFallback: undefined, - showContactDisplayPlaceholder: true, - autoLog: false, - sourceIcons: undefined, - phoneTypeRenderer: undefined, - phoneSourceNameRenderer: undefined, - renderContactName: undefined, - renderExtraButton: undefined, - contactDisplayStyle: undefined, - externalViewEntity: undefined, - externalHasEntity: undefined, - readTextPermission: true, - enableCDC: false, + return ( +
+ +
+ ); }; + export default CallList; diff --git a/packages/ringcentral-widgets/components/CallListV2/index.tsx b/packages/ringcentral-widgets/components/CallListV2/index.tsx index 499b9464c1..daa1331912 100644 --- a/packages/ringcentral-widgets/components/CallListV2/index.tsx +++ b/packages/ringcentral-widgets/components/CallListV2/index.tsx @@ -1,5 +1,4 @@ import React from 'react'; - import { List } from 'react-virtualized'; import CallItem from '../CallItem'; @@ -24,7 +23,7 @@ type CallListV2Props = { onClickToDial?: (...args: any[]) => any; onClickToSms?: (...args: any[]) => any; isLoggedContact?: (...args: any[]) => any; - loggingMap?: object; + loggingMap?: any; disableLinks?: boolean; disableCallButton?: boolean; disableClickToDial?: boolean; @@ -38,6 +37,7 @@ type CallListV2Props = { enableContactFallback?: boolean; autoLog?: boolean; showContactDisplayPlaceholder?: boolean; + showCallerIdName?: boolean; sourceIcons?: object; phoneTypeRenderer?: (...args: any[]) => any; phoneSourceNameRenderer?: (...args: any[]) => any; @@ -47,6 +47,7 @@ type CallListV2Props = { contactDisplayStyle?: string; externalViewEntity?: (...args: any[]) => any; externalHasEntity?: (...args: any[]) => any; + shouldHideEntityButton?: (...args: any[]) => boolean; readTextPermission?: boolean; rowHeight?: number; extendedRowHeight?: number; @@ -54,10 +55,13 @@ type CallListV2Props = { enableCDC?: boolean; maxExtensionNumberLength: number; formatPhone: (phoneNumber: string) => string | undefined; + callsDelaySavingState: Record; }; + type CallListV2State = { - extendedIndex: null; + extendedIndex: number | null; }; + class CallListV2 extends React.PureComponent { _list: any; constructor(props: any) { @@ -73,20 +77,24 @@ class CallListV2 extends React.PureComponent { const { calls } = this.props; if ( extendedIndex !== null && - calls[extendedIndex] !== nextProps.calls[extendedIndex] + calls[extendedIndex].sessionId !== + nextProps.calls[extendedIndex].sessionId ) { this._setExtendedIndex(null); } } + _recomputeRowHeight = (index = 0) => { + if (this._list && this._list.current) { + this._list.current.recomputeRowHeights(index); + } + }; _setExtendedIndex = (extendedIndex: any) => { this.setState( { extendedIndex, }, () => { - if (this._list && this._list.current) { - this._list.current.recomputeRowHeights(0); - } + this._recomputeRowHeight(0); }, ); }; @@ -101,11 +109,23 @@ class CallListV2 extends React.PureComponent { _renderRowHeight = ({ index }: any) => { // If we don't add extra height for the last item // the toggle button will be cut off - const { calls, extendedRowHeight, rowHeight } = this.props; + const { + calls, + extendedRowHeight = 130, + rowHeight = 65, + callsDelaySavingState, + } = this.props; + const call = calls[index]; + const isDelaySavingState = + callsDelaySavingState && callsDelaySavingState[call.sessionId]; const { extendedIndex } = this.state; + const margin = index === calls.length - 1 ? 15 : 0; - const height = index === extendedIndex ? extendedRowHeight : rowHeight; - // @ts-expect-error TS(2532): Object is possibly 'undefined'. + const delaySavingStateHeight = isDelaySavingState ? 13 : 0; + const height = + index === extendedIndex + ? extendedRowHeight + : rowHeight + delaySavingStateHeight; return height + margin; }; _rowRender = ({ index, key, style }: any) => { @@ -138,6 +158,7 @@ class CallListV2 extends React.PureComponent { enableContactFallback, autoLog, showContactDisplayPlaceholder, + showCallerIdName, sourceIcons, phoneTypeRenderer, phoneSourceNameRenderer, @@ -147,6 +168,7 @@ class CallListV2 extends React.PureComponent { contactDisplayStyle, externalViewEntity, externalHasEntity, + shouldHideEntityButton, readTextPermission, currentSiteCode, isMultipleSiteEnabled, @@ -154,6 +176,7 @@ class CallListV2 extends React.PureComponent { enableCDC, maxExtensionNumberLength, formatPhone, + callsDelaySavingState, } = this.props; const { extendedIndex } = this.state; let content; @@ -168,12 +191,9 @@ class CallListV2 extends React.PureComponent { const call = calls[index]; content = ( stri... Remove this comment to see the full error message - formatPhone={formatPhone} key={call.id} renderIndex={index} extended={extendedIndex === index} - style={style} call={call} currentLocale={currentLocale} currentSiteCode={currentSiteCode} @@ -183,6 +203,7 @@ class CallListV2 extends React.PureComponent { countryCode={countryCode} onViewContact={onViewContact} onCreateContact={onCreateContact} + shouldHideEntityButton={shouldHideEntityButton} createEntityTypes={createEntityTypes} onLogCall={onLogCall} onClickToDial={onClickToDial} @@ -193,17 +214,13 @@ class CallListV2 extends React.PureComponent { disableClickToDial={disableClickToDial} outboundSmsPermission={outboundSmsPermission} internalSmsPermission={internalSmsPermission} - active={active} + active={!!active} dateTimeFormatter={dateTimeFormatter} - // @ts-expect-error TS(2532): Object is possibly 'undefined'. isLogging={!!loggingMap[call.sessionId]} - webphoneAnswer={webphoneAnswer} - webphoneReject={webphoneReject} - webphoneHangup={webphoneHangup} - webphoneResume={webphoneResume} enableContactFallback={enableContactFallback} autoLog={autoLog} showContactDisplayPlaceholder={showContactDisplayPlaceholder} + showCallerIdName={showCallerIdName} sourceIcons={sourceIcons} phoneTypeRenderer={phoneTypeRenderer} phoneSourceNameRenderer={phoneSourceNameRenderer} @@ -215,11 +232,16 @@ class CallListV2 extends React.PureComponent { externalHasEntity={externalHasEntity} readTextPermission={readTextPermission} onSizeChanged={this._onSizeChanged} + onItemHeightChanged={this._recomputeRowHeight} // disable animation when rendered with react-virtualized withAnimation={false} showChooseEntityModal={showChooseEntityModal} enableCDC={enableCDC} maxExtensionNumberLength={maxExtensionNumberLength} + formatPhone={formatPhone} + currentDelaySavingState={ + callsDelaySavingState && callsDelaySavingState[call.sessionId] + } /> ); } @@ -281,6 +303,7 @@ CallListV2.defaultProps = { webphoneResume: undefined, enableContactFallback: undefined, showContactDisplayPlaceholder: true, + showCallerIdName: false, autoLog: false, sourceIcons: undefined, phoneTypeRenderer: undefined, diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/CallLogDialpad/CallLogDialpad.tsx b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/CallLogDialpad/CallLogDialpad.tsx index eeb5cf658a..c25deb4388 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/CallLogDialpad/CallLogDialpad.tsx +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/CallLogDialpad/CallLogDialpad.tsx @@ -1,11 +1,11 @@ +import { RcDialerPadSoundsMPEG } from '@ringcentral/juno'; +import clsx from 'clsx'; import type { FunctionComponent } from 'react'; import React from 'react'; -import classnames from 'classnames'; - import CloseDialpadIcon from '../../../assets/images/CloseDialpad.svg'; -import { audios } from '../../DialButton/audios'; import DialPad from '../../DialPad'; + import type { CallLogDialpadProps, DtmfValue, @@ -40,17 +40,15 @@ const CallLogDialpad: FunctionComponent = ({ }, []); const playAudio = (value: DtmfValue) => { - if ( - audioRef.current && - audioRef.current.canPlayType('audio/ogg') !== '' && - audios[value] - ) { + if (audioRef.current && RcDialerPadSoundsMPEG[value]) { if (!audioRef.current.paused) { audioRef.current.pause(); } - audioRef.current.src = audios[value]; + audioRef.current.src = RcDialerPadSoundsMPEG[value]; audioRef.current.currentTime = 0; - audioRef.current.play(); + audioRef.current.play().catch((error: any) => { + console.error('playAudio error:', error); + }); } }; @@ -100,16 +98,14 @@ const CallLogDialpad: FunctionComponent = ({ return (
-
+
-
+
= ({ />
diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/MoreActionComponent.tsx b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/MoreActionComponent.tsx index c4f1d3fa18..27d7ba275f 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/MoreActionComponent.tsx +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/MoreActionComponent.tsx @@ -1,124 +1,127 @@ -import type { FunctionComponent } from 'react'; -import React from 'react'; - -import classnames from 'classnames'; - import { RcIcon, RcIconButton, RcMenuItem, RcMenuList, RcPopover, + styled, } from '@ringcentral/juno'; +import clsx from 'clsx'; +import type { FunctionComponent } from 'react'; +import React from 'react'; + +import { CircleButtonWithTitle } from '../CircleButton'; -import CircleButton from '../CircleButton'; import type { MoreActionComponentProps } from './MoreActionComponent.interface'; import styles from './styles.scss'; -export const MoreActionComponent: FunctionComponent = - ({ - actionsList, - disabled = false, - rootButtonProps, - withSubText, - anchorEl, - handleClick, - handleClose, - popoverClasses, - dataSign, - useJunoIcon = false, - }) => { - if (!Array.isArray(actionsList) || actionsList.length === 0) { - return <>; - } +export const StyledMenuItem = styled(RcMenuItem)` + max-width: 170px; +`; + +export const MoreActionComponent: FunctionComponent< + MoreActionComponentProps +> = ({ + actionsList, + disabled = false, + rootButtonProps, + withSubText, + anchorEl, + handleClick, + handleClose, + popoverClasses, + dataSign, + useJunoIcon = false, +}) => { + if (!Array.isArray(actionsList) || actionsList.length === 0) { + return <>; + } - return ( - <> + return ( + <> + {!useJunoIcon ? ( + + ) : ( - {!useJunoIcon ? ( - - ) : ( - - )} + - handleClose()} - classes={popoverClasses} - > -
- - {actionsList.map( - ({ - icon, - text, - subText, - onClick, - disabled, - iconClassName, - key, - }) => ( - handleClose()} + classes={popoverClasses} + > +
+ + {actionsList.map( + ({ + icon, + text, + subText, + onClick, + disabled, + iconClassName, + key, + }) => ( + +
-
- {icon && ( - - )} - {text && ( - - {text} - - )} - {withSubText && subText && ( - {subText} - )} -
- - ), - )} - -
- - - ); - }; + {icon && ( + + )} + {text && {text}} + {withSubText && subText && ( + {subText} + )} +
+ + ), + )} +
+
+
+ + ); +}; diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/MoreActionWithIncomingCall/MoreActionWithIncomingCall.tsx b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/MoreActionWithIncomingCall/MoreActionWithIncomingCall.tsx index b78d6b19ce..d8176cebab 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/MoreActionWithIncomingCall/MoreActionWithIncomingCall.tsx +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/MoreActionWithIncomingCall/MoreActionWithIncomingCall.tsx @@ -1,8 +1,3 @@ -import type { FunctionComponent } from 'react'; -import React, { useState } from 'react'; - -import classnames from 'classnames'; - import { RcMenuItem, RcMenuList, RcPopover, RcText } from '@ringcentral/juno'; import { Ignore as IgnoreIcon, @@ -10,108 +5,138 @@ import { Forwardcall, Forwarding as ReplyIcon, } from '@ringcentral/juno-icon'; +import clsx from 'clsx'; +import type { FunctionComponent } from 'react'; +import React, { useState } from 'react'; import MoreIcon from '../../../assets/images/MoreIcon.svg'; -import CircleButton from '../../CircleButton'; +import { CircleButtonWithTitle } from '../../CircleButton'; import i18n from '../i18n'; import rootStyles from '../styles.scss'; + import type { MoreActionWithIncomingCallProps } from './MoreActionWithIncomingCall.interface'; -import styles from './styles.scss'; import { StyledArrowIcon, StyledActionIcon, StyledMenuList, StyledReplyIcon, } from './StyledMoreAction'; +import styles from './styles.scss'; -const MoreActionWithIncomingCall: FunctionComponent = - (props) => { - const { - disabled, - currentLocale, - forwardingNumbers, - forward, - ignore, - reply, - clickForwardTrack, - enableReply, - isWebRTCNotification = false, - disableIgnore, - } = props; - const [anchorEl, setAnchorEl] = useState(null); - const [forwardListEl, setForwardListEl] = useState(null); - const handleClick = (event: React.MouseEvent) => { - // @ts-expect-error TS(2345): Argument of type 'EventTarget & HTMLButtonElement'... Remove this comment to see the full error message - setAnchorEl(event.currentTarget); - }; - const handleClose = () => { - setAnchorEl(null); - }; - const handleForwardListClick = ( - event: React.MouseEvent, - ) => { - clickForwardTrack(); - // @ts-expect-error TS(2345): Argument of type 'Element' is not assignable to pa... Remove this comment to see the full error message - setForwardListEl(event.currentTarget.children?.[0]); - }; - const handleForwardListClose = () => { +const MoreActionWithIncomingCall: FunctionComponent< + MoreActionWithIncomingCallProps +> = (props) => { + const { + disabled, + currentLocale, + forwardingNumbers, + forward, + ignore, + reply, + clickForwardTrack, + enableReply, + isWebRTCNotification = false, + disableIgnore, + } = props; + const [anchorEl, setAnchorEl] = useState(null); + const [forwardListEl, setForwardListEl] = useState(null); + const handleClick = (event: React.MouseEvent) => { + // @ts-expect-error TS(2345): Argument of type 'EventTarget & HTMLButtonElement'... Remove this comment to see the full error message + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; + const handleForwardListClick = ( + event: React.MouseEvent, + ) => { + clickForwardTrack(); + // @ts-expect-error TS(2345): Argument of type 'Element' is not assignable to pa... Remove this comment to see the full error message + setForwardListEl(event.currentTarget.children?.[0]); + }; + const handleForwardListClose = () => { + setForwardListEl(null); + }; + + const onForward = (event: React.MouseEvent) => { + // @ts-expect-error TS(7015): Element implicitly has an 'any' type because index... Remove this comment to see the full error message + const selectedValue = event.currentTarget.attributes['data-value'].value; + if (selectedValue === 'custom') { setForwardListEl(null); - }; + setAnchorEl(null); + } + forward(selectedValue); + }; + const title = i18n.getString('more', currentLocale); + return ( + <> + + + + )... Remove this comment to see the full error message + onClick={handleForwardListClick} + data-sign="forward" + > + - const onForward = (event: React.MouseEvent) => { - // @ts-expect-error TS(7015): Element implicitly has an 'any' type because index... Remove this comment to see the full error message - const selectedValue = event.currentTarget.attributes['data-value'].value; - if (selectedValue === 'custom') { - setForwardListEl(null); - setAnchorEl(null); - } - forward(selectedValue); - }; - return ( - <> - - - - - + + {i18n.getString('forward', currentLocale)} + + + + + {enableReply && ( )... Remove this comment to see the full error message - onClick={handleForwardListClick} - data-sign="forward" + onClick={() => { + handleClose(); + reply?.(); + }} + data-sign="reply" > - - {i18n.getString('forward', currentLocale)} + {i18n.getString('reply', currentLocale)} - {enableReply && ( - { - handleClose(); - reply?.(); - }} - data-sign="reply" - > - - - - {i18n.getString('reply', currentLocale)} - + )} + {ignore && ( + + - - - )} - {ignore && ( + + {i18n.getString('ignore', currentLocale)} + + + )} + + + + + {[ + ...forwardingNumbers, + { + phoneNumber: 'custom', + label: i18n.getString('custom', currentLocale), + }, + ].map((item) => { + const isCustomOption = item.phoneNumber === 'custom'; + return ( )... Remove this comment to see the full error message + onClick={onForward} + key={item.phoneNumber} + data-value={item.phoneNumber} + data-sign={item.phoneNumber} > - - - - {i18n.getString('ignore', currentLocale)} - +
+ {item.label} + {isCustomOption ? ( + + ) : ( + {item.phoneNumber} + )} +
- )} - -
- - - {[ - ...forwardingNumbers, - { - phoneNumber: 'custom', - label: i18n.getString('custom', currentLocale), - }, - ].map((item) => { - const isCustomOption = item.phoneNumber === 'custom'; - return ( - )... Remove this comment to see the full error message - onClick={onForward} - key={item.phoneNumber} - data-value={item.phoneNumber} - data-sign={item.phoneNumber} - > -
- {item.label} - {isCustomOption ? ( - - ) : ( - {item.phoneNumber} - )} -
-
- ); - })} -
-
- - ); - }; + ); + })} + + + + ); +}; export { MoreActionWithIncomingCall }; diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/MoreActionWithIncomingCall/styles.scss b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/MoreActionWithIncomingCall/styles.scss index 1f2d4b67e0..c8e9a9eeb2 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/MoreActionWithIncomingCall/styles.scss +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/MoreActionWithIncomingCall/styles.scss @@ -23,7 +23,7 @@ $width: 41px; .webRTCNotificationButton { width: 30px; height: 30px; - padding: 2px + padding: 2px; } .buttonDisabled { diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/de-DE.ts b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/de-DE.ts index 056b4417a2..023b82da58 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/de-DE.ts +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/de-DE.ts @@ -20,7 +20,9 @@ export default { answerAndHold: "Beantworten & halten", answer: "Antworten", completeTransfer: "Weiterleitung abschließen", - reply: "Antwort" + reply: "Antwort", + add: "Hinzufügen", + mergeCall: "Mit aktivem Anruf zusammenführen" }; // @key: @#@"mute"@#@ @source: @#@"Mute"@#@ @@ -45,3 +47,5 @@ export default { // @key: @#@"answer"@#@ @source: @#@"Answer"@#@ // @key: @#@"completeTransfer"@#@ @source: @#@"Complete transfer"@#@ // @key: @#@"reply"@#@ @source: @#@"Reply"@#@ +// @key: @#@"add"@#@ @source: @#@"Add"@#@ +// @key: @#@"mergeCall"@#@ @source: @#@"Merge with active call"@#@ diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/en-AU.ts b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/en-AU.ts index 0259ff43b7..90d22af925 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/en-AU.ts +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/en-AU.ts @@ -20,7 +20,9 @@ export default { answerAndHold: "Answer & Hold", answer: "Answer", completeTransfer: "Complete transfer", - reply: "Reply" + reply: "Reply", + add: "Add", + mergeCall: "Merge with active call" }; // @key: @#@"mute"@#@ @source: @#@"Mute"@#@ @@ -45,3 +47,5 @@ export default { // @key: @#@"answer"@#@ @source: @#@"Answer"@#@ // @key: @#@"completeTransfer"@#@ @source: @#@"Complete transfer"@#@ // @key: @#@"reply"@#@ @source: @#@"Reply"@#@ +// @key: @#@"add"@#@ @source: @#@"Add"@#@ +// @key: @#@"mergeCall"@#@ @source: @#@"Merge with active call"@#@ diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/en-GB.ts b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/en-GB.ts index 0259ff43b7..90d22af925 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/en-GB.ts +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/en-GB.ts @@ -20,7 +20,9 @@ export default { answerAndHold: "Answer & Hold", answer: "Answer", completeTransfer: "Complete transfer", - reply: "Reply" + reply: "Reply", + add: "Add", + mergeCall: "Merge with active call" }; // @key: @#@"mute"@#@ @source: @#@"Mute"@#@ @@ -45,3 +47,5 @@ export default { // @key: @#@"answer"@#@ @source: @#@"Answer"@#@ // @key: @#@"completeTransfer"@#@ @source: @#@"Complete transfer"@#@ // @key: @#@"reply"@#@ @source: @#@"Reply"@#@ +// @key: @#@"add"@#@ @source: @#@"Add"@#@ +// @key: @#@"mergeCall"@#@ @source: @#@"Merge with active call"@#@ diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/en-US.ts b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/en-US.ts index b59ce2874f..a0494f59c1 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/en-US.ts +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/en-US.ts @@ -21,4 +21,6 @@ export default { answer: 'Answer', completeTransfer: 'Complete transfer', reply: 'Reply', -}; + add: 'Add', + mergeCall: 'Merge with active call', +} as const; diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/es-419.ts b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/es-419.ts index 393eee34e0..af327577d4 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/es-419.ts +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/es-419.ts @@ -20,7 +20,9 @@ export default { answerAndHold: "Resp. y poner en espera", answer: "Responder", completeTransfer: "Completar transferencia", - reply: "Responder" + reply: "Responder", + add: "Agregar", + mergeCall: "Combinar con la llamada activa" }; // @key: @#@"mute"@#@ @source: @#@"Mute"@#@ @@ -45,3 +47,5 @@ export default { // @key: @#@"answer"@#@ @source: @#@"Answer"@#@ // @key: @#@"completeTransfer"@#@ @source: @#@"Complete transfer"@#@ // @key: @#@"reply"@#@ @source: @#@"Reply"@#@ +// @key: @#@"add"@#@ @source: @#@"Add"@#@ +// @key: @#@"mergeCall"@#@ @source: @#@"Merge with active call"@#@ diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/es-ES.ts b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/es-ES.ts index 062a15a4a6..3516319afb 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/es-ES.ts +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/es-ES.ts @@ -12,7 +12,7 @@ export default { showKeypad: "Teclado", hideKeypad: "Ocultar teclado de marcación", unHold: "Reanudar llamada", - forward: "Desviar", + forward: "Reenviar", ignore: "Ignorar", voicemail: "Al buzón de voz", custom: "Personalizado", @@ -20,7 +20,9 @@ export default { answerAndHold: "Responder y poner en espera", answer: "Responder", completeTransfer: "Completar transferencia", - reply: "Responder" + reply: "Responder", + add: "Añadir", + mergeCall: "Combinar con la llamada activa" }; // @key: @#@"mute"@#@ @source: @#@"Mute"@#@ @@ -45,3 +47,5 @@ export default { // @key: @#@"answer"@#@ @source: @#@"Answer"@#@ // @key: @#@"completeTransfer"@#@ @source: @#@"Complete transfer"@#@ // @key: @#@"reply"@#@ @source: @#@"Reply"@#@ +// @key: @#@"add"@#@ @source: @#@"Add"@#@ +// @key: @#@"mergeCall"@#@ @source: @#@"Merge with active call"@#@ diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/fi-FI.ts b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/fi-FI.ts index 2dcc11bb0e..797322be82 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/fi-FI.ts +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/fi-FI.ts @@ -20,7 +20,9 @@ export default { answerAndHold: "Vastaa ja aseta pitoon", answer: "Vastaa", completeTransfer: "Suorita siirto", - reply: "Vastaa" + reply: "Vastaa", + add: "Lisää", + mergeCall: "Yhdistä käynnissä olevaan puheluun" }; // @key: @#@"mute"@#@ @source: @#@"Mute"@#@ @@ -45,3 +47,5 @@ export default { // @key: @#@"answer"@#@ @source: @#@"Answer"@#@ // @key: @#@"completeTransfer"@#@ @source: @#@"Complete transfer"@#@ // @key: @#@"reply"@#@ @source: @#@"Reply"@#@ +// @key: @#@"add"@#@ @source: @#@"Add"@#@ +// @key: @#@"mergeCall"@#@ @source: @#@"Merge with active call"@#@ diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/fr-CA.ts b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/fr-CA.ts index 123fdf7ab7..9c46e77903 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/fr-CA.ts +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/fr-CA.ts @@ -20,7 +20,9 @@ export default { answerAndHold: "Répondre et mettre en attente", answer: "Répondre", completeTransfer: "Exécuter le transfert", - reply: "Répondre" + reply: "Répondre", + add: "Ajouter", + mergeCall: "Fusionner avec l’appel en cours" }; // @key: @#@"mute"@#@ @source: @#@"Mute"@#@ @@ -45,3 +47,5 @@ export default { // @key: @#@"answer"@#@ @source: @#@"Answer"@#@ // @key: @#@"completeTransfer"@#@ @source: @#@"Complete transfer"@#@ // @key: @#@"reply"@#@ @source: @#@"Reply"@#@ +// @key: @#@"add"@#@ @source: @#@"Add"@#@ +// @key: @#@"mergeCall"@#@ @source: @#@"Merge with active call"@#@ diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/fr-FR.ts b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/fr-FR.ts index 4f1203c9c4..c64512625e 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/fr-FR.ts +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/fr-FR.ts @@ -20,7 +20,9 @@ export default { answerAndHold: "Répondre et mettre en attente", answer: "Répondre", completeTransfer: "Effectuer le transfert", - reply: "Répondre" + reply: "Répondre", + add: "Ajouter", + mergeCall: "Fusionner avec l’appel actif" }; // @key: @#@"mute"@#@ @source: @#@"Mute"@#@ @@ -45,3 +47,5 @@ export default { // @key: @#@"answer"@#@ @source: @#@"Answer"@#@ // @key: @#@"completeTransfer"@#@ @source: @#@"Complete transfer"@#@ // @key: @#@"reply"@#@ @source: @#@"Reply"@#@ +// @key: @#@"add"@#@ @source: @#@"Add"@#@ +// @key: @#@"mergeCall"@#@ @source: @#@"Merge with active call"@#@ diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/index.ts b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/index.ts +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/it-IT.ts b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/it-IT.ts index 7494049fa7..6086a2475f 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/it-IT.ts +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/it-IT.ts @@ -1,5 +1,5 @@ export default { - mute: "Disattiva audio", + mute: "Disatt. audio", unmute: "Riattiva audio", hangup: "Riaggancia", reject: "Rifiuta", @@ -20,7 +20,9 @@ export default { answerAndHold: "Rispondi e attesa", answer: "Rispondi", completeTransfer: "Completa trasferimento", - reply: "Rispondi" + reply: "Rispondi", + add: "Aggiungi", + mergeCall: "Unisci a chiamata attiva" }; // @key: @#@"mute"@#@ @source: @#@"Mute"@#@ @@ -45,3 +47,5 @@ export default { // @key: @#@"answer"@#@ @source: @#@"Answer"@#@ // @key: @#@"completeTransfer"@#@ @source: @#@"Complete transfer"@#@ // @key: @#@"reply"@#@ @source: @#@"Reply"@#@ +// @key: @#@"add"@#@ @source: @#@"Add"@#@ +// @key: @#@"mergeCall"@#@ @source: @#@"Merge with active call"@#@ diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/ja-JP.ts b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/ja-JP.ts index c6961da661..8e3fb43d22 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/ja-JP.ts +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/ja-JP.ts @@ -20,7 +20,9 @@ export default { answerAndHold: "応答/保留", answer: "応答", completeTransfer: "転送完了", - reply: "返信" + reply: "返信", + add: "追加", + mergeCall: "アクティブな通話とマージ" }; // @key: @#@"mute"@#@ @source: @#@"Mute"@#@ @@ -45,3 +47,5 @@ export default { // @key: @#@"answer"@#@ @source: @#@"Answer"@#@ // @key: @#@"completeTransfer"@#@ @source: @#@"Complete transfer"@#@ // @key: @#@"reply"@#@ @source: @#@"Reply"@#@ +// @key: @#@"add"@#@ @source: @#@"Add"@#@ +// @key: @#@"mergeCall"@#@ @source: @#@"Merge with active call"@#@ diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/ko-KR.ts b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/ko-KR.ts index 65ff1ea714..5adfa8b174 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/ko-KR.ts +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/ko-KR.ts @@ -20,7 +20,9 @@ export default { answerAndHold: "응답 후 대기", answer: "응답", completeTransfer: "전달 완료", - reply: "회신" + reply: "회신", + add: "추가", + mergeCall: "진행 중인 통화와 병합" }; // @key: @#@"mute"@#@ @source: @#@"Mute"@#@ @@ -45,3 +47,5 @@ export default { // @key: @#@"answer"@#@ @source: @#@"Answer"@#@ // @key: @#@"completeTransfer"@#@ @source: @#@"Complete transfer"@#@ // @key: @#@"reply"@#@ @source: @#@"Reply"@#@ +// @key: @#@"add"@#@ @source: @#@"Add"@#@ +// @key: @#@"mergeCall"@#@ @source: @#@"Merge with active call"@#@ diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/nl-NL.ts b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/nl-NL.ts index 4bd5f46bb2..6226959f2f 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/nl-NL.ts +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/nl-NL.ts @@ -20,7 +20,9 @@ export default { answerAndHold: "Beantwoorden en in de wacht zetten", answer: "Beantwoorden", completeTransfer: "Doorverbinden voltooien", - reply: "Beantwoorden" + reply: "Beantwoorden", + add: "Toevoegen", + mergeCall: "Samenvoegen met actieve oproep" }; // @key: @#@"mute"@#@ @source: @#@"Mute"@#@ @@ -45,3 +47,5 @@ export default { // @key: @#@"answer"@#@ @source: @#@"Answer"@#@ // @key: @#@"completeTransfer"@#@ @source: @#@"Complete transfer"@#@ // @key: @#@"reply"@#@ @source: @#@"Reply"@#@ +// @key: @#@"add"@#@ @source: @#@"Add"@#@ +// @key: @#@"mergeCall"@#@ @source: @#@"Merge with active call"@#@ diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/pt-BR.ts b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/pt-BR.ts index e55d32de1e..cdaf8cedf7 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/pt-BR.ts +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/pt-BR.ts @@ -20,7 +20,9 @@ export default { answerAndHold: "Atender e colocar em espera", answer: "Atender", completeTransfer: "Concluir a transferência", - reply: "Responder" + reply: "Responder", + add: "Adicionar", + mergeCall: "Mesclar com chamada ativa" }; // @key: @#@"mute"@#@ @source: @#@"Mute"@#@ @@ -45,3 +47,5 @@ export default { // @key: @#@"answer"@#@ @source: @#@"Answer"@#@ // @key: @#@"completeTransfer"@#@ @source: @#@"Complete transfer"@#@ // @key: @#@"reply"@#@ @source: @#@"Reply"@#@ +// @key: @#@"add"@#@ @source: @#@"Add"@#@ +// @key: @#@"mergeCall"@#@ @source: @#@"Merge with active call"@#@ diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/pt-PT.ts b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/pt-PT.ts index 6ad4a66577..44f56a01c4 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/pt-PT.ts +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/pt-PT.ts @@ -20,7 +20,9 @@ export default { answerAndHold: "Atender e em espera", answer: "Atender", completeTransfer: "Concluir transferência", - reply: "Resp." + reply: "Resp.", + add: "Adicionar", + mergeCall: "Unir com chamada ativa" }; // @key: @#@"mute"@#@ @source: @#@"Mute"@#@ @@ -45,3 +47,5 @@ export default { // @key: @#@"answer"@#@ @source: @#@"Answer"@#@ // @key: @#@"completeTransfer"@#@ @source: @#@"Complete transfer"@#@ // @key: @#@"reply"@#@ @source: @#@"Reply"@#@ +// @key: @#@"add"@#@ @source: @#@"Add"@#@ +// @key: @#@"mergeCall"@#@ @source: @#@"Merge with active call"@#@ diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/zh-CN.ts b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/zh-CN.ts index 7c5c0387c0..e70511ecb3 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/zh-CN.ts +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/zh-CN.ts @@ -20,7 +20,9 @@ export default { answerAndHold: "接听并保持", answer: "接听", completeTransfer: "完成转接", - reply: "回复" + reply: "回复", + add: "添加", + mergeCall: "与进行中的通话合并" }; // @key: @#@"mute"@#@ @source: @#@"Mute"@#@ @@ -45,3 +47,5 @@ export default { // @key: @#@"answer"@#@ @source: @#@"Answer"@#@ // @key: @#@"completeTransfer"@#@ @source: @#@"Complete transfer"@#@ // @key: @#@"reply"@#@ @source: @#@"Reply"@#@ +// @key: @#@"add"@#@ @source: @#@"Add"@#@ +// @key: @#@"mergeCall"@#@ @source: @#@"Merge with active call"@#@ diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/zh-HK.ts b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/zh-HK.ts index e9d35d03a6..b67210aa14 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/zh-HK.ts +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/zh-HK.ts @@ -20,7 +20,9 @@ export default { answerAndHold: "接聽並保留", answer: "接聽", completeTransfer: "完成轉接", - reply: "回覆" + reply: "回覆", + add: "新增", + mergeCall: "與進行中的通話合併" }; // @key: @#@"mute"@#@ @source: @#@"Mute"@#@ @@ -45,3 +47,5 @@ export default { // @key: @#@"answer"@#@ @source: @#@"Answer"@#@ // @key: @#@"completeTransfer"@#@ @source: @#@"Complete transfer"@#@ // @key: @#@"reply"@#@ @source: @#@"Reply"@#@ +// @key: @#@"add"@#@ @source: @#@"Add"@#@ +// @key: @#@"mergeCall"@#@ @source: @#@"Merge with active call"@#@ diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/zh-TW.ts b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/zh-TW.ts index e9d35d03a6..b67210aa14 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/zh-TW.ts +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/i18n/zh-TW.ts @@ -20,7 +20,9 @@ export default { answerAndHold: "接聽並保留", answer: "接聽", completeTransfer: "完成轉接", - reply: "回覆" + reply: "回覆", + add: "新增", + mergeCall: "與進行中的通話合併" }; // @key: @#@"mute"@#@ @source: @#@"Mute"@#@ @@ -45,3 +47,5 @@ export default { // @key: @#@"answer"@#@ @source: @#@"Answer"@#@ // @key: @#@"completeTransfer"@#@ @source: @#@"Complete transfer"@#@ // @key: @#@"reply"@#@ @source: @#@"Reply"@#@ +// @key: @#@"add"@#@ @source: @#@"Add"@#@ +// @key: @#@"mergeCall"@#@ @source: @#@"Merge with active call"@#@ diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/index.tsx b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/index.tsx index 724cfd879c..d55fe8d890 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/index.tsx +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/index.tsx @@ -1,7 +1,6 @@ -import React, { useEffect, useState } from 'react'; - -import classnames from 'classnames'; +/* eslint-disable jsx-a11y/no-static-element-interactions */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ import callDirections from '@ringcentral-integration/commons/enums/callDirections'; import { telephonySessionStatus } from '@ringcentral-integration/commons/enums/telephonySessionStatus'; import telephonyStatuses from '@ringcentral-integration/commons/enums/telephonyStatus'; @@ -13,7 +12,10 @@ import { Ignore as IgnoreIcon, TransferCall as TransferSmallIcon, Voicemail as VoicemailIcon, + CallAdd as CallAddIcon, } from '@ringcentral/juno-icon'; +import clsx from 'clsx'; +import React, { useEffect, useState } from 'react'; import AnswerIcon from '../../assets/images/Answer.svg'; import DialpadIcon from '../../assets/images/Dialpad.svg'; @@ -22,17 +24,19 @@ import EndAnswerIcon from '../../assets/images/EndAnswer.svg'; import ForwardIcon from '../../assets/images/Forward_white.svg'; import HoldIcon from '../../assets/images/Hold.svg'; import HoldAnswerIcon from '../../assets/images/HoldAnswer.svg'; +import MergeIntoConferenceIcon from '../../assets/images/MergeIntoConferenceIcon.svg'; import MoreIcon from '../../assets/images/MoreIcon.svg'; import MuteIcon from '../../assets/images/Mute.svg'; import RecordIcon from '../../assets/images/RecordOff.svg'; import RecordIconActive from '../../assets/images/RecordOn.svg'; import TransferIcon from '../../assets/images/Transfer.svg'; import UnmuteIcon from '../../assets/images/Unmute.svg'; -import CircleButton from '../CircleButton'; +import { CircleButtonWithTitle } from '../CircleButton'; + import { CallLogDialpad } from './CallLogDialpad'; -import i18n from './i18n'; import { MoreActionComponent } from './MoreActionComponent'; import { MoreActionWithIncomingCall } from './MoreActionWithIncomingCall'; +import i18n from './i18n'; import { CompleteTransferButton } from './style'; import styles from './styles.scss'; @@ -43,6 +47,7 @@ type CallLogCallCtrlComponentProps = { onHangup?: (...args: any[]) => any; onReject?: (...args: any[]) => any; onTransfer?: (...args: any[]) => any; + onAddCall?: (...args: any[]) => any; onCompleteWarmTransfer?: (...args: any[]) => any; isOnMute?: boolean; isOnHold?: boolean; @@ -58,11 +63,7 @@ type CallLogCallCtrlComponentProps = { isWide?: boolean; isCurrentDeviceCall?: boolean; warmTransferActiveTelephonySessionId?: string; - transferRef?: - | ((...args: any[]) => any) - | { - current?: any; - }; + transferRef?: any; isOnTransfer?: boolean; sendDTMF?: (...args: any[]) => any; forward?: (...args: any[]) => any; @@ -78,8 +79,11 @@ type CallLogCallCtrlComponentProps = { realOutboundCallStatus?: string; enableReply?: boolean; allowPickupCall?: boolean; + showConferenceCall?: boolean; + isCurrentCall?: boolean; + onMergeCall: () => Promise; }; -const CallLogCallCtrlComponent: React.SFC = ( +const CallLogCallCtrlComponent: React.FC = ( props, ) => { const { @@ -93,6 +97,7 @@ const CallLogCallCtrlComponent: React.SFC = ( currentLocale, callDirection, onTransfer, + onAddCall, onCompleteWarmTransfer, isOnTransfer, isOnHold, @@ -119,6 +124,9 @@ const CallLogCallCtrlComponent: React.SFC = ( warmTransferActiveTelephonySessionId, enableReply, allowPickupCall, + showConferenceCall, + isCurrentCall, + onMergeCall, } = props; // reject conditions: call direction is inbound & call status is ringing @@ -195,7 +203,7 @@ const CallLogCallCtrlComponent: React.SFC = ( icon: muteIcon, key: muteTitle, onClick: muteAction, - iconClassName: classnames({ + iconClassName: clsx({ [styles.moreActionIcon]: true, [styles.buttonDisabled]: disableLinks || disabledCtrl, [styles.moreActionIconActive]: isOnMute, @@ -210,7 +218,7 @@ const CallLogCallCtrlComponent: React.SFC = ( handleClose(); toggleDialpadShow(); }, - iconClassName: classnames({ + iconClassName: clsx({ [styles.moreActionIcon]: true, [styles.buttonDisabled]: isInComingCall || disableLinks, [styles.moreActionIconActive]: dialpadShow, @@ -222,7 +230,7 @@ const CallLogCallCtrlComponent: React.SFC = ( icon: HoldIconInAction, key: onHoldText, onClick: holdAction, - iconClassName: classnames({ + iconClassName: clsx({ [styles.moreActionIcon]: true, [styles.holdActive]: isOnHold, }), @@ -233,7 +241,7 @@ const CallLogCallCtrlComponent: React.SFC = ( icon: isRecording ? RecordIconActive : RecordIcon, key: recordingText, onClick: recordAction, - iconClassName: classnames({ + iconClassName: clsx({ [styles.moreActionIcon]: true, [styles.recordingIcon]: true, [styles.recordingDisabled]: recordPendingState, @@ -248,7 +256,7 @@ const CallLogCallCtrlComponent: React.SFC = ( handleClose(); endAction?.(); }, - iconClassName: classnames({ + iconClassName: clsx({ [styles.buttonDisabled]: disableLinks, [styles.endIcon]: true, }), @@ -256,12 +264,26 @@ const CallLogCallCtrlComponent: React.SFC = ( text: i18n.getString(endTitle, currentLocale), }, ]; + const conferenceItem = showConferenceCall + ? [ + { + icon: CallAddIcon, + key: 'add', + onClick: onAddCall, + iconClassName: clsx({ + [styles.moreActionIcon]: true, + }), + text: i18n.getString('add', currentLocale), + }, + ] + : []; const moreActions = [ + ...conferenceItem, { icon: TransferSmallIcon, key: 'transfer', onClick: onTransfer, - iconClassName: classnames({ + iconClassName: clsx({ [styles.moreActionIcon]: true, }), text: i18n.getString('transfer', currentLocale), @@ -270,7 +292,7 @@ const CallLogCallCtrlComponent: React.SFC = ( icon: HoldIconInAction, key: onHoldText, onClick: holdAction, - iconClassName: classnames({ + iconClassName: clsx({ [styles.moreActionIcon]: true, [styles.holdActive]: isOnHold, }), @@ -281,7 +303,7 @@ const CallLogCallCtrlComponent: React.SFC = ( icon: isRecording ? RecordIconActive : RecordIcon, key: recordingText, onClick: recordAction, - iconClassName: classnames({ + iconClassName: clsx({ [styles.moreActionIcon]: true, [styles.recordingIcon]: true, [styles.recordingDisabled]: recordPendingState, @@ -297,7 +319,7 @@ const CallLogCallCtrlComponent: React.SFC = ( }; const DialPadCom = dialpadShow && ( { @@ -313,10 +335,10 @@ const CallLogCallCtrlComponent: React.SFC = ( <> = ( return ( <> -
- - + {showConferenceCall && isWide && !isCurrentCall && ( + - - - - + )} + + = ( handleClose={handleClose} anchorEl={anchorEl} /> - - - +
{DialPadCom} @@ -444,7 +474,7 @@ const CallLogCallCtrlComponent: React.SFC = ( tooltip: forwardTitle, }; return ( -
+
{enableReply ? ( = ( popoverClasses={{ paper: styles.forwardPopover }} /> )} - - - - - - - - - + + +
); } @@ -538,7 +565,7 @@ const CallLogCallCtrlComponent: React.SFC = ( onGoingActiveCalls ) { return ( -
+
= ( > - - - + = ( ); } return ( -
- - - - {/* @ts-expect-error TS(2322): Type '{ current?: any; } | ((...args: any[]) => an... Remove this comment to see the full error message */} - - - - - - - - - +
+ + + +
); }; @@ -677,7 +699,7 @@ CallLogCallCtrlComponent.defaultProps = { otherActiveCalls: false, answerAndEnd() {}, answerAndHold() {}, - dialpadToggleTrack(i) {}, + dialpadToggleTrack() {}, clickForwardTrack() {}, realOutboundCallStatus: '', }; diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/style.tsx b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/style.tsx index aea76e3998..6111a54d87 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/style.tsx +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlComponent/style.tsx @@ -1,19 +1,13 @@ -import { css, RcButton, spacing, styled } from '@ringcentral/juno'; +import { RcButton, spacing, styled } from '@ringcentral/juno'; export const CompleteTransferButton = styled(RcButton)<{ - isWide?: boolean; + $isWide?: boolean; }>` white-space: initial; - margin-right: ${({ isWide }) => (isWide ? spacing(2) : spacing(1))}; - ${({ isWide }) => { - if (!isWide) { - return css` - padding: ${spacing(0, 2)}; - `; - } - }}; + margin-right: ${({ $isWide }) => ($isWide ? spacing(2) : spacing(1))}; + padding: ${({ $isWide }) => !$isWide && spacing(0, 2)}; `; CompleteTransferButton.defaultProps = { - isWide: true, + $isWide: true, }; diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlPanel/CallLogCallCtrlPanel.interface.ts b/packages/ringcentral-widgets/components/CallLogCallCtrlPanel/CallLogCallCtrlPanel.interface.ts index 86ab3d2eea..25891c2f50 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlPanel/CallLogCallCtrlPanel.interface.ts +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlPanel/CallLogCallCtrlPanel.interface.ts @@ -9,6 +9,7 @@ export interface CallLogCallCtrlPanelProps { isWide?: boolean; enableReply?: boolean; isCurrentDeviceCall?: boolean; + isCurrentCall: boolean; warmTransferActiveTelephonySessionId?: string; transferRef?: React.RefObject; isOnTransfer?: boolean; @@ -30,6 +31,7 @@ export interface CallLogCallCtrlPanelProps { stopRecord: ActiveCallControl['stopRecord']; onCompleteWarmTransfer: ActiveCallControl['completeWarmTransfer']; onTransfer: (telephonySessionId: string) => Promise; + onAddCall: () => Promise; sendDTMF: (dtmfValue: string, telephonySessionId: string) => Promise; answer: ActiveCallControl['answer']; forward: (phoneNumber: string, telephonySessionId: string) => void; @@ -40,4 +42,6 @@ export interface CallLogCallCtrlPanelProps { dialpadToggleTrack: (open: boolean) => void; clickForwardTrack: ActiveCallControl['clickForwardTrack']; allowPickupCall: boolean; + showConferenceCall?: boolean; + onMergeCall: () => Promise; } diff --git a/packages/ringcentral-widgets/components/CallLogCallCtrlPanel/CallLogCallCtrlPanel.tsx b/packages/ringcentral-widgets/components/CallLogCallCtrlPanel/CallLogCallCtrlPanel.tsx index d90e21ddbe..f4fb88f10f 100644 --- a/packages/ringcentral-widgets/components/CallLogCallCtrlPanel/CallLogCallCtrlPanel.tsx +++ b/packages/ringcentral-widgets/components/CallLogCallCtrlPanel/CallLogCallCtrlPanel.tsx @@ -2,6 +2,7 @@ import type { FunctionComponent } from 'react'; import React from 'react'; import CallLogCallCtrlComponent from '../CallLogCallCtrlComponent'; + import type { CallLogCallCtrlPanelProps } from './CallLogCallCtrlPanel.interface'; export type CurrentSession = { @@ -40,6 +41,9 @@ const CallLogCallCtrlPanel: FunctionComponent = ( clickForwardTrack, warmTransferActiveTelephonySessionId, allowPickupCall, + showConferenceCall, + isCurrentCall, + onMergeCall, } = props; if (!currentSession) { @@ -53,6 +57,7 @@ const CallLogCallCtrlPanel: FunctionComponent = ( onHangup={async () => props.hangUp(telephonySessionId)} onReject={async () => props.reject(telephonySessionId)} onTransfer={() => props.onTransfer(telephonySessionId)} + onAddCall={() => props.onAddCall()} onCompleteWarmTransfer={() => props.onCompleteWarmTransfer(telephonySessionId) } @@ -92,6 +97,9 @@ const CallLogCallCtrlPanel: FunctionComponent = ( } enableReply={enableReply} allowPickupCall={allowPickupCall} + showConferenceCall={showConferenceCall} + isCurrentCall={isCurrentCall} + onMergeCall={onMergeCall} /> ); }; diff --git a/packages/ringcentral-widgets/components/CallLogFields/CallLogFields.interface.ts b/packages/ringcentral-widgets/components/CallLogFields/CallLogFields.interface.ts index e232cf3854..5f80d5fae2 100644 --- a/packages/ringcentral-widgets/components/CallLogFields/CallLogFields.interface.ts +++ b/packages/ringcentral-widgets/components/CallLogFields/CallLogFields.interface.ts @@ -1,14 +1,13 @@ -import type { MutableRefObject, ReactNode } from 'react'; - -import type { ReactElementLike } from 'prop-types'; - import type { RcDatePickerProps } from '@ringcentral/juno'; +import type { ReactElementLike } from 'prop-types'; +import type { MutableRefObject, ReactNode } from 'react'; import type { CallLog, Task } from '../CallLogPanel'; export type CallLogFieldsProps = { currentLocale: string; currentLog?: CallLog; + currentDelaySavingState?: any; onUpdateCallLog?: (data: { task: Task }, id: string) => any; onSaveCallLog?: (...args: any[]) => any; onSelectViewVisible?: (visible: boolean, fieldName: string) => any; @@ -57,7 +56,7 @@ export interface FieldOption { rightIconRender?: (item: any) => ReactElementLike; onBackClick?: () => void; backHeaderClassName?: string; - shouldDisable?: (task?: Task) => boolean; + shouldDisable?: (task?: Task, call?: CallLog['call']) => boolean; disableReason?: ReactNode | string; getValue?: (item: any) => any; foundFromServerEntityGetter?: (currentLog: CallLog) => any[]; diff --git a/packages/ringcentral-widgets/components/CallLogFields/CallLogFields.tsx b/packages/ringcentral-widgets/components/CallLogFields/CallLogFields.tsx index 30b6b16f0a..9706a831f0 100644 --- a/packages/ringcentral-widgets/components/CallLogFields/CallLogFields.tsx +++ b/packages/ringcentral-widgets/components/CallLogFields/CallLogFields.tsx @@ -1,9 +1,10 @@ +import clsx from 'clsx'; import React, { Component } from 'react'; -import classNames from 'classnames'; - import { bindDebounce } from '../../lib/bindDebounce'; import type { Task } from '../CallLogPanel'; +import { CountdownTimer } from '../CountdownTimer'; + import type { CallLogFieldsProps } from './CallLogFields.interface'; import { FieldItem } from './FieldItem'; import styles from './styles.scss'; @@ -26,6 +27,23 @@ export default class CallLogFields extends Component { debounce = bindDebounce(this, DEFAULT_INPUT_SAVE_TIMEOUT); + renderDelaySavingTimer = () => { + const { currentLocale, currentDelaySavingState } = this.props; + const { delayUpdatingStartTime, delayUpdatingMinutes } = + currentDelaySavingState ?? {}; + return ( + delayUpdatingStartTime && + delayUpdatingMinutes && ( + + ) + ); + }; + renderFields = () => { const { currentLog: { @@ -70,8 +88,12 @@ export default class CallLogFields extends Component { return null; } return ( - // @ts-expect-error TS(2532): Object is possibly 'undefined'. -
+
+ {this.renderDelaySavingTimer()} {this.renderFields()}
); diff --git a/packages/ringcentral-widgets/components/CallLogFields/FieldItem/FieldItem.interface.ts b/packages/ringcentral-widgets/components/CallLogFields/FieldItem/FieldItem.interface.ts index f1d80e732a..c3e46cb167 100644 --- a/packages/ringcentral-widgets/components/CallLogFields/FieldItem/FieldItem.interface.ts +++ b/packages/ringcentral-widgets/components/CallLogFields/FieldItem/FieldItem.interface.ts @@ -3,6 +3,7 @@ import type { RcTextFieldProps } from '@ringcentral/juno'; export type PickListOption = { label: string; value: any; + validFor?: string[]; disabled?: boolean; title?: string; }; @@ -30,6 +31,7 @@ export type FieldItemOption = { required?: boolean; maxLength?: number; picklistOptions?: (string | number | PickListOption)[]; + controller?: string; enableScrollError?: boolean; referenceObjs?: string[]; defaultValue?: string; diff --git a/packages/ringcentral-widgets/components/CallLogFields/FieldItem/FieldItem.tsx b/packages/ringcentral-widgets/components/CallLogFields/FieldItem/FieldItem.tsx index b744108e8b..59204aea17 100644 --- a/packages/ringcentral-widgets/components/CallLogFields/FieldItem/FieldItem.tsx +++ b/packages/ringcentral-widgets/components/CallLogFields/FieldItem/FieldItem.tsx @@ -1,13 +1,13 @@ +import { RcAlert, RcDatePicker, RcTypography } from '@ringcentral/juno'; import React, { Component } from 'react'; -import { RcDatePicker, RcTypography } from '@ringcentral/juno'; - import { getDateFromUTCDay, setUTCTime } from '../../../lib/timeFormatHelper'; import InputSelect from '../../InputSelect'; import type { CallLogFieldsProps, FieldMetadata, } from '../CallLogFields.interface'; + import type { FieldItemOption, FieldsMap, @@ -134,7 +134,7 @@ export class FieldItem extends Component { ...foundFromServerEntities, ].find(currentOptionFinder(task)); const disabledReference = - currentDisabled || shouldDisable(task) || disabled; + currentDisabled || shouldDisable(task, currentLog?.call) || disabled; const title = metadata.title || label; const rightIcon = rightIconRender ? rightIconRender(phoneNumber) @@ -308,32 +308,51 @@ export class FieldItem extends Component { onChange, disabled: propsDisabled = false, placeholder, + controller, }, + currentLog, onSave, onSelectListOpen = () => {}, } = this.props; - const selectList = (picklistOptions || []).map((item) => { - let value: string = item as any; - let label = item !== null ? (item as any) : appDefaultValue; - let disabled = false; - let title; + const selectList = (picklistOptions || []).reduce( + (acc, item) => { + if ( + item && + typeof item === 'object' && + controller && + currentLog?.task && + item.validFor + ) { + // check for field dependency and filter out options that are not valid + if (!item.validFor.includes(currentLog.task[controller] as string)) { + return acc; + } + } - if (item instanceof Object) { - value = item.value; - label = item.label; - // @ts-expect-error TS(2322): Type 'boolean | undefined' is not assignable to ty... Remove this comment to see the full error message - disabled = item.disabled; - title = item?.title; - } + let value: string = item as any; + let label = item !== null ? (item as any) : appDefaultValue; + let disabled = false; + let title; - return { - label, - value, - disabled, - title, - }; - }); + if (item instanceof Object) { + value = item.value; + label = item.label; + // @ts-expect-error TS(2322): Type 'boolean | undefined' is not assignable to ty... Remove this comment to see the full error message + disabled = item.disabled; + title = item?.title; + } + acc.push({ + label, + value, + disabled, + title, + }); + + return acc; + }, + [], + ); return ( { disabled: disableAllFields, } = this.props; // @ts-expect-error TS(2339): Property 'task' does not exist on type 'CallLog | ... Remove this comment to see the full error message - const { task, disableSaveLog } = currentLog; + const { task, disableSaveLog, disableUpdateLog } = currentLog; const options = [ { // @ts-expect-error TS(2532): Object is possibly 'undefined'. @@ -392,7 +411,8 @@ export class FieldItem extends Component { !task.tickets || task.tickets?.length === 0 || (task.matches?.length > 1 && !task.whoid) || - disableAllFields + disableAllFields || + disableUpdateLog ), }, ]; @@ -442,6 +462,17 @@ export class FieldItem extends Component { ); }; + private renderAlert = () => { + const { + fieldOption: { label }, + currentLog, + } = this.props; + if (!currentLog?.shouldPromoteAlert) { + return; + } + return {label}; + }; + // this is the dropdown to render ticket lists private renderTicketSelectList = () => { const { currentLog, fieldOption, disabled } = this.props; @@ -468,7 +499,9 @@ export class FieldItem extends Component { labelClassName={styles.selectLabel} options={options} fullWidth - disabled={options.length === 0 || disabled} + disabled={ + options.length === 0 || disabled || currentLog?.disableUpdateLog + } value={task.ticketId} label={label} onChange={( @@ -537,6 +570,7 @@ export class FieldItem extends Component { long: this.renderInput, combobox: this.renderSubjectField, radio: this.renderRadio, + alert: this.renderAlert, // @ts-expect-error TS(2322): Type '() => JSX.Element | null' is not assignable ... Remove this comment to see the full error message ticketSelectList: this.renderTicketSelectList, }; diff --git a/packages/ringcentral-widgets/components/CallLogFields/FieldItem/FullSelectField/FullSelectField.tsx b/packages/ringcentral-widgets/components/CallLogFields/FieldItem/FullSelectField/FullSelectField.tsx index 83f4c028dd..cf8392cbff 100644 --- a/packages/ringcentral-widgets/components/CallLogFields/FieldItem/FullSelectField/FullSelectField.tsx +++ b/packages/ringcentral-widgets/components/CallLogFields/FieldItem/FullSelectField/FullSelectField.tsx @@ -1,10 +1,10 @@ +import type { RcTextFieldProps } from '@ringcentral/juno'; import type { FunctionComponent } from 'react'; import React from 'react'; -import type { RcTextFieldProps } from '@ringcentral/juno'; - import type { SelectListProps } from '../../../SelectList'; import { SelectList } from '../../../SelectList'; + import { SelectListTextField } from './SelectListTextField/SelectListTextField'; export type FullSelectFieldProps = { diff --git a/packages/ringcentral-widgets/components/CallLogFields/FieldItem/FullSelectField/SelectListTextField/SelectListTextField.tsx b/packages/ringcentral-widgets/components/CallLogFields/FieldItem/FullSelectField/SelectListTextField/SelectListTextField.tsx index 229bb41368..e719d03dda 100644 --- a/packages/ringcentral-widgets/components/CallLogFields/FieldItem/FullSelectField/SelectListTextField/SelectListTextField.tsx +++ b/packages/ringcentral-widgets/components/CallLogFields/FieldItem/FullSelectField/SelectListTextField/SelectListTextField.tsx @@ -1,8 +1,7 @@ -import type { FunctionComponent } from 'react'; -import React from 'react'; - import type { RcTextFieldProps } from '@ringcentral/juno'; import { RcTextField } from '@ringcentral/juno'; +import type { FunctionComponent } from 'react'; +import React from 'react'; import { CustomArrowButton } from '../../../../Rcui/CustomArrowButton'; import styles from '../styles.scss'; @@ -12,24 +11,25 @@ export type SelectListTextFieldProps = Pick< 'value' | 'disabled' | 'helperText' | 'label' | 'onClick' | 'required' >; -export const SelectListTextField: FunctionComponent = - ({ value, disabled, ...rest }) => { - return ( - , - }} - fullWidth - clearBtn={false} - /> - ); - }; +export const SelectListTextField: FunctionComponent< + SelectListTextFieldProps +> = ({ value, disabled, ...rest }) => { + return ( + , + }} + fullWidth + clearBtn={false} + /> + ); +}; diff --git a/packages/ringcentral-widgets/components/CallLogFields/FieldItem/LogFieldsInput/LogFieldsInput.tsx b/packages/ringcentral-widgets/components/CallLogFields/FieldItem/LogFieldsInput/LogFieldsInput.tsx index fdf153b456..b42dc73e86 100644 --- a/packages/ringcentral-widgets/components/CallLogFields/FieldItem/LogFieldsInput/LogFieldsInput.tsx +++ b/packages/ringcentral-widgets/components/CallLogFields/FieldItem/LogFieldsInput/LogFieldsInput.tsx @@ -1,9 +1,7 @@ -import React, { Component } from 'react'; - -import classnames from 'classnames'; - import type { RcTextFieldProps } from '@ringcentral/juno'; import { RcTextField } from '@ringcentral/juno'; +import clsx from 'clsx'; +import React, { Component } from 'react'; import { bindDebounce } from '../../../../lib/bindDebounce'; import { bindNextPropsUpdate } from '../../../../lib/bindNextPropsUpdate'; @@ -62,7 +60,7 @@ export class LogFieldsInput extends Component< const { value } = this.state; const styleRequired = required ? styles.isRequired : null; return ( -
+
= { }; export interface CallLogPanelProps extends CallLogPanelConfig { + rootLayout?: boolean; currentLog: CallLog; + currentDelaySavingState?: any; warmTransferLog?: CallLog; warmTransferActiveTelephonySessionId: string; currentLocale: string; @@ -76,6 +77,7 @@ export interface CallLogPanelProps extends CallLogPanelConfig { | 'startAdornmentRender' | 'isWide' | 'objectTypeIconsMap' + | 'currentDelaySavingState' > & { editSectionScrollBy?: (top: number) => void; }, @@ -101,7 +103,7 @@ export interface CallLogPanelProps extends CallLogPanelConfig { onExpandNotification?: (...args: any[]) => any; currentNotificationIdentify?: string; currentSession?: object; - activeSession?: object; + activeSession?: boolean | object; pushLogPageStatus?: (...args: any[]) => any; shrinkNotification?: (...args: any[]) => any; contactSearch?: ({ @@ -130,11 +132,29 @@ export interface CallLogPanelProps extends CallLogPanelConfig { answer: (telephonySession: string) => any; showRecordingIndicator?: boolean; clickForwardTrack?: () => any; + clickParticipantsIconTrack?: () => void; + clickRemoveParticipantTrack?: () => void; renderCallNotificationAvatar?: ( contact: IContact, entityType: string, ) => JSX.Element; + renderConferenceParticipantsAvatar?: (item: { + displayEntity?: IContact; + entityType?: string; + name?: string; + }) => JSX.Element; getAvatarUrl?: (contact: IContact) => Promise; + getConferenceCallParticipantName: ( + sessionId: string, + isHost?: boolean, + ) => { + logName?: string; + entityDetailLink?: string; + }; + onRemoveParticipant: ( + telephonySessionId: string, + removedPartyId: string, + ) => Promise; } export interface LogNotification { @@ -146,4 +166,5 @@ export interface LogNotification { displayEntity: any; entityType: string; entityDetailLink: string; + showLogOptions?: boolean; } diff --git a/packages/ringcentral-widgets/components/CallLogPanel/CallLogPanel.tsx b/packages/ringcentral-widgets/components/CallLogPanel/CallLogPanel.tsx index 42876efbe5..943ed382b9 100644 --- a/packages/ringcentral-widgets/components/CallLogPanel/CallLogPanel.tsx +++ b/packages/ringcentral-widgets/components/CallLogPanel/CallLogPanel.tsx @@ -1,17 +1,18 @@ -import React, { Component } from 'react'; - /** * Call log enhancement */ -import classnames from 'classnames'; +import clsx from 'clsx'; +import React, { Component } from 'react'; import { environment } from '../../lib'; import BackHeader from '../BackHeaderV2'; +import { ConferenceCallParticipants } from '../ConferenceCallParticipants'; import LogBasicInfo from '../LogBasicInfoV2'; import NotificationSection from '../NotificationSection'; import NotificationSectionV2 from '../NotificationSectionV2'; import { SpinnerOverlay } from '../SpinnerOverlay'; import WebRTCNotificationSection from '../WebRTCNotificationSection'; + import type { CallLog } from './CallLog.interface'; import type { CallLogPanelProps } from './CallLogPanel.interface'; import i18n from './i18n'; @@ -43,7 +44,25 @@ const getWarmTransferSession = ({ }; }; -export default class CallLogPanel extends Component { +export default class CallLogPanel extends Component< + CallLogPanelProps, + { showConferenceCallParticipants: boolean } +> { + constructor(props: CallLogPanelProps) { + super(props); + this.state = { + showConferenceCallParticipants: false, + }; + } + + toggleConference = (open: boolean) => { + if (open) { + this.props.clickParticipantsIconTrack?.(); + } + this.setState({ + showConferenceCallParticipants: open, + }); + }; static defaultProps: Partial = { currentLog: { nameEntities: [], @@ -136,9 +155,10 @@ export default class CallLogPanel extends Component { <> {this.renderLogNotification()} {this.renderLogBasicInfo()} + {this.renderConferenceCallParticipants()}
{renderEditLogSection && this.getEditLogSection()}
@@ -178,7 +198,7 @@ export default class CallLogPanel extends Component { return (
{renderCallLogCallControl && @@ -204,6 +224,7 @@ export default class CallLogPanel extends Component { onUpdateCallLog, onSelectViewVisible, currentLog, + currentDelaySavingState, additionalInfo, subjectDropdownsTracker, contactSearch, @@ -220,6 +241,7 @@ export default class CallLogPanel extends Component { onUpdateCallLog, onSelectViewVisible, currentLog, + currentDelaySavingState, additionalInfo, subjectDropdownsTracker, contactSearch, @@ -262,9 +284,9 @@ export default class CallLogPanel extends Component { { currentLog, isWide, showSpinner, + currentDelaySavingState, } = this.props; const loading = showSpinner || (currentLog && currentLog.showSpinner); return renderSaveLogButton({ @@ -295,6 +318,7 @@ export default class CallLogPanel extends Component { loading, isWide, disabled: currentLog?.disableSaveLog, + currentDelaySavingState, }); } @@ -347,6 +371,8 @@ export default class CallLogPanel extends Component { entityType, // @ts-expect-error TS(2339): Property 'entityDetailLink' does not exist on type... Remove this comment to see the full error message entityDetailLink, + // @ts-expect-error TS(2339): Property 'showLogOptions' does not exist on type... Remove this comment to see the full error message + showLogOptions, } = logNotification; if (!showNotification) { return null; @@ -411,6 +437,7 @@ export default class CallLogPanel extends Component { onReject={onReject} onHangup={onHangup} shrinkNotification={shrinkNotification} + showLogOptions={showLogOptions} /> ); } @@ -434,6 +461,34 @@ export default class CallLogPanel extends Component { ); } + renderConferenceCallParticipants() { + const isOpen = this.state.showConferenceCallParticipants; + const { + currentLocale, + getConferenceCallParticipantName, + currentLog, + onRemoveParticipant, + renderConferenceParticipantsAvatar, + clickRemoveParticipantTrack, + } = this.props; + const call = currentLog.call; + if (!call?.isConferenceCall || !isOpen) return null; + + return ( + + ); + } + goBack() { const { goBack } = this.props; goBack(); @@ -455,17 +510,20 @@ export default class CallLogPanel extends Component { isWide, children, getRenderLogButton, + // @ts-ignore + rootLayout, } = this.props; if (!currentIdentify || isInTransferPage) return null; - + const _root = + root ?? typeof rootLayout === 'boolean' + ? rootLayout + ? styles.callLogPanelClassLeftNav + : styles.callLogPanelClass + : undefined; return (
{header && ( { rightIcon={getRenderLogButton?.() || this.genSaveLogButtonV2()} // @ts-expect-error TS(2345): Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message title={i18n.getString(headerTitle, currentLocale)} - className={classnames(styles.header, backHeader)} + className={clsx(styles.header, backHeader)} onBackClick={() => this.goBack()} /> )} diff --git a/packages/ringcentral-widgets/components/CallLogPanel/i18n/de-DE.ts b/packages/ringcentral-widgets/components/CallLogPanel/i18n/de-DE.ts index d78f1911f0..37c5715270 100644 --- a/packages/ringcentral-widgets/components/CallLogPanel/i18n/de-DE.ts +++ b/packages/ringcentral-widgets/components/CallLogPanel/i18n/de-DE.ts @@ -1,5 +1,5 @@ export default { - createCallLog: "Anrufliste anlegen", + createCallLog: "Anrufprotokoll anlegen", viewInSalesforce: "In Salesforce anzeigen", createLog: "Protokoll erstell." }; diff --git a/packages/ringcentral-widgets/components/CallLogPanel/i18n/en-US.ts b/packages/ringcentral-widgets/components/CallLogPanel/i18n/en-US.ts index 76460052a1..7b7bf575b5 100644 --- a/packages/ringcentral-widgets/components/CallLogPanel/i18n/en-US.ts +++ b/packages/ringcentral-widgets/components/CallLogPanel/i18n/en-US.ts @@ -2,4 +2,4 @@ export default { createCallLog: 'Create call log', viewInSalesforce: 'View in Salesforce', createLog: 'Create log', -}; +} as const; diff --git a/packages/ringcentral-widgets/components/CallLogPanel/i18n/index.ts b/packages/ringcentral-widgets/components/CallLogPanel/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/ringcentral-widgets/components/CallLogPanel/i18n/index.ts +++ b/packages/ringcentral-widgets/components/CallLogPanel/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/ringcentral-widgets/components/CallLogPanel/styles.scss b/packages/ringcentral-widgets/components/CallLogPanel/styles.scss index d48428d88d..22a9118cda 100644 --- a/packages/ringcentral-widgets/components/CallLogPanel/styles.scss +++ b/packages/ringcentral-widgets/components/CallLogPanel/styles.scss @@ -62,3 +62,16 @@ $pageSpacing_classic: 10px; .spinner { top: $header-height; } + +.callLogPanelClass { + top: 0px !important; + height: 100% !important; +} + +.callLogPanelClassLeftNav { + top: 0px !important; + left: 45px; + right: 0; + width: auto; + height: 100% !important; +} diff --git a/packages/ringcentral-widgets/components/CallMonitorBar/CallInfoBar.tsx b/packages/ringcentral-widgets/components/CallMonitorBar/CallInfoBar.tsx index b190e62dfb..b1658f6639 100644 --- a/packages/ringcentral-widgets/components/CallMonitorBar/CallInfoBar.tsx +++ b/packages/ringcentral-widgets/components/CallMonitorBar/CallInfoBar.tsx @@ -2,6 +2,7 @@ import type { FunctionComponent } from 'react'; import React from 'react'; import { Button } from '../Button'; + import i18n from './i18n'; import styles from './styles.scss'; diff --git a/packages/ringcentral-widgets/components/CallMonitorBar/CallMonitorBar.tsx b/packages/ringcentral-widgets/components/CallMonitorBar/CallMonitorBar.tsx index 2acc27563c..59f27405a5 100644 --- a/packages/ringcentral-widgets/components/CallMonitorBar/CallMonitorBar.tsx +++ b/packages/ringcentral-widgets/components/CallMonitorBar/CallMonitorBar.tsx @@ -1,11 +1,11 @@ +import { emptyArray, emptyFn, format } from '@ringcentral-integration/utils'; import type { FunctionComponent } from 'react'; import React, { useState } from 'react'; -import { emptyArray, emptyFn, format } from '@ringcentral-integration/utils'; - import { Button } from '../Button'; import CarrouselBar from '../CarrouselBar'; import DurationCounter from '../DurationCounter'; + import { CallInfoBar } from './CallInfoBar'; import i18n from './i18n'; import styles from './styles.scss'; diff --git a/packages/ringcentral-widgets/components/CallMonitorBar/i18n/en-US.ts b/packages/ringcentral-widgets/components/CallMonitorBar/i18n/en-US.ts index e4fabaa96d..3e460c526d 100644 --- a/packages/ringcentral-widgets/components/CallMonitorBar/i18n/en-US.ts +++ b/packages/ringcentral-widgets/components/CallMonitorBar/i18n/en-US.ts @@ -8,4 +8,4 @@ export default { callsOnHold: '{numberOf} Calls On Hold', otherDeviceCall: '{numberOf} Call on other devices', otherDeviceCalls: '{numberOf} Calls on other devices', -}; +} as const; diff --git a/packages/ringcentral-widgets/components/CallMonitorBar/i18n/index.ts b/packages/ringcentral-widgets/components/CallMonitorBar/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/ringcentral-widgets/components/CallMonitorBar/i18n/index.ts +++ b/packages/ringcentral-widgets/components/CallMonitorBar/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/ringcentral-widgets/components/CallMonitorBar/styles.scss b/packages/ringcentral-widgets/components/CallMonitorBar/styles.scss index 190e7ee1f8..1f9325f651 100644 --- a/packages/ringcentral-widgets/components/CallMonitorBar/styles.scss +++ b/packages/ringcentral-widgets/components/CallMonitorBar/styles.scss @@ -7,22 +7,22 @@ cursor: pointer; } .currentCallBtn { - display: inline-block; - min-width: 66px; - max-width: 110px; - height: 18px; - line-height: 18px; - border-radius: 10.5px; - border: solid 1px $leaf; - color: $leaf; - font-size: 11px; - margin-left: 5px; - overflow: hidden; - text-overflow: hidden; - white-space: nowrap; - padding: 0 5px; - vertical-align: middle; - box-sizing: content-box; + display: inline-block; + min-width: 66px; + max-width: 110px; + height: 18px; + line-height: 18px; + border-radius: 10.5px; + border: solid 1px $leaf; + color: $leaf; + font-size: 11px; + margin-left: 5px; + overflow: hidden; + text-overflow: hidden; + white-space: nowrap; + padding: 0 5px; + vertical-align: middle; + box-sizing: content-box; } .currentCallBtn:hover { color: $leaf; @@ -60,24 +60,24 @@ line-height: 35px; vertical-align: middle; } -.wrap{ - height:30px; +.wrap { + height: 30px; overflow: hidden; position: absolute; - top:30; + top: 30; left: 100; - width: 100% + width: 100%; } -.content p{ +.content p { position: absolute; } -.hide{ +.hide { visibility: hidden; } -.show{ +.show { visibility: visible; } -.box{ +.box { position: absolute; width: 210px; height: 100%; @@ -90,7 +90,7 @@ display: flex; align-items: baseline; justify-content: center; - + .currentCallInfo { max-width: 160px; } diff --git a/packages/ringcentral-widgets/components/CallingSettingsPanel/CallingSettingsPanel.tsx b/packages/ringcentral-widgets/components/CallingSettingsPanel/CallingSettingsPanel.tsx index 4672cf0770..90f4705821 100644 --- a/packages/ringcentral-widgets/components/CallingSettingsPanel/CallingSettingsPanel.tsx +++ b/packages/ringcentral-widgets/components/CallingSettingsPanel/CallingSettingsPanel.tsx @@ -1,17 +1,19 @@ -import 'rc-tooltip/assets/bootstrap_white.css'; - -import type { ChangeEvent, FunctionComponent } from 'react'; -import React, { useEffect, useRef, useState } from 'react'; - -import classnames from 'classnames'; - /* eslint-disable react/destructuring-assignment */ import { callingOptions } from '@ringcentral-integration/commons/modules/CallingSettings'; import { format } from '@ringcentral-integration/utils'; import { RcIcon } from '@ringcentral/juno'; import { InfoBorder as infoSvg } from '@ringcentral/juno-icon'; +import clsx from 'clsx'; +import 'rc-tooltip/assets/bootstrap_white.css'; +import type { ChangeEvent, FunctionComponent } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; -import BackHeader from '../BackHeader'; +import { + PageHeader, + PageHeaderBack, + PageHeaderRemain, + PageHeaderTitle, +} from '../BackHeader/PageHeader'; import { DropdownSelect } from '../DropdownSelect'; import IconField from '../IconField'; import InputField from '../InputField'; @@ -21,6 +23,7 @@ import SaveButton from '../SaveButton'; import { SpinnerOverlay } from '../SpinnerOverlay'; import Switch from '../Switch'; import TextInput from '../TextInput'; + import type { CallingSettingsPanelProps, CallingSettingsProps, @@ -60,6 +63,16 @@ const CallWithSettings: FunctionComponent = ({ }) => { const tooltipContainerRef = useRef(null); + const valueRenderer = (option: string) => { + const optionName = getCallingOptionName({ + callingOption: option, + currentLocale, + jupiterAppName, + softphoneAppName, + }); + return {optionName}; + }; + const optionRenderer = (option: string) => { const optionName = getCallingOptionName({ callingOption: option, @@ -67,8 +80,9 @@ const CallWithSettings: FunctionComponent = ({ jupiterAppName, softphoneAppName, }); - return optionName; + return {optionName}; }; + const keys = [`${callWith}Tooltip`]; if ( callWith !== callingOptions.browser && @@ -116,7 +130,7 @@ const CallWithSettings: FunctionComponent = ({ options={callWithOptions} dropdownAlign="left" renderFunction={optionRenderer} - renderValue={optionRenderer} + renderValue={valueRenderer} valueFunction={(option) => option} disabled={disabled} titleEnabled @@ -169,7 +183,7 @@ const RingoutSettings: FunctionComponent = ({ > {availableNumbersWithLabel ? ( boolean) ... Remove this comment to see the full error message @@ -224,15 +238,10 @@ const CallingSettings: FunctionComponent = ({ myLocation, onSave, ringoutPrompt, - showRingToneSettings = false, incomingAudio, incomingAudioFile, outgoingAudio, outgoingAudioFile, - defaultIncomingAudio, - defaultIncomingAudioFile, - defaultOutgoingAudio, - defaultOutgoingAudioFile, jupiterAppName, softphoneAppName, }) => { @@ -263,6 +272,16 @@ const CallingSettings: FunctionComponent = ({ outgoingAudio, outgoingAudioFile, ]); + const isSaveButtonDisabled = + (callWithState === callWith && + myLocationState === myLocation && + ringoutPromptState === ringoutPrompt && + incomingAudioState === incomingAudio && + incomingAudioFileState === incomingAudioFile && + outgoingAudioState === outgoingAudio && + outgoingAudioFileState === outgoingAudioFile) || + (callWithState === callingOptions.ringout && !myLocationState); + return ( <> = ({ setMyLocationState(myLocation); setRingoutPromptState(ringoutPrompt); } else { + // * when callWith changed, set myLocation to be the first available number setMyLocationState(availableNumbersWithLabel?.[0]?.value || ''); setRingoutPromptState(defaultRingoutPrompt); } @@ -349,45 +369,37 @@ const CallingSettings: FunctionComponent = ({ outgoingAudioFile: outgoingAudioFileState, }); }, - disabled: - (callWithState === callWith && - myLocationState === myLocation && - ringoutPromptState === ringoutPrompt && - incomingAudioState === incomingAudio && - incomingAudioFileState === incomingAudioFile && - outgoingAudioState === outgoingAudio && - outgoingAudioFileState === outgoingAudioFile) || - (callWithState === callingOptions.ringout && !myLocationState), + disabled: isSaveButtonDisabled, }} /> ); }; -export const CallingSettingsPanel: FunctionComponent = - ({ - className, - onBackButtonClick, - currentLocale, - showSpinner = false, - ...props - }) => { - const content = showSpinner ? ( - - ) : ( - <> - - - ); - return ( -
- +export const CallingSettingsPanel: FunctionComponent< + CallingSettingsPanelProps +> = ({ + className, + onBackButtonClick, + currentLocale, + showSpinner = false, + ...props +}) => { + const content = showSpinner ? ( + + ) : ( + + ); + return ( +
+ + + {i18n.getString('title', currentLocale)} - - {content} -
- ); - }; + + + + {content} +
+ ); +}; diff --git a/packages/ringcentral-widgets/components/CallingSettingsPanel/i18n/en-US.ts b/packages/ringcentral-widgets/components/CallingSettingsPanel/i18n/en-US.ts index aaeed08fb4..5096e68ede 100644 --- a/packages/ringcentral-widgets/components/CallingSettingsPanel/i18n/en-US.ts +++ b/packages/ringcentral-widgets/components/CallingSettingsPanel/i18n/en-US.ts @@ -19,4 +19,4 @@ export default { 'For the call you make, this phone will ring first then the party you called.', [`${callingOptions.jupiter}Tooltip`]: 'Use this option to make and receive calls using your {brand}.', -}; +} as const; diff --git a/packages/ringcentral-widgets/components/CallingSettingsPanel/i18n/es-419.ts b/packages/ringcentral-widgets/components/CallingSettingsPanel/i18n/es-419.ts index 3c3e675dba..31359fee51 100644 --- a/packages/ringcentral-widgets/components/CallingSettingsPanel/i18n/es-419.ts +++ b/packages/ringcentral-widgets/components/CallingSettingsPanel/i18n/es-419.ts @@ -11,7 +11,7 @@ export default { [`${callingOptions.browser}Tooltip`]: "Utilice esta opción para hacer y recibir llamadas con el micrófono y los altavoces de su computadora.", [`${callingOptions.softphone}Tooltip`]: "Utilice esta opción para hacer y recibir llamadas usando su {brand}.", [`${callingOptions.ringout}Tooltip`]: "Use esta opción para hacer llamadas con su número de teléfono seleccionado o ingresado.", - [`${callingOptions.ringout}Tooltip1`]: "Para la llamada que hace, este teléfono sonará primero, y luego a quien llamó.", + [`${callingOptions.ringout}Tooltip1`]: "Para la llamada que hace, este teléfono sonará primero y, luego, a quien llamó.", [`${callingOptions.jupiter}Tooltip`]: "Utilice esta opción para hacer y recibir llamadas usando su {brand}." }; diff --git a/packages/ringcentral-widgets/components/CallingSettingsPanel/i18n/index.ts b/packages/ringcentral-widgets/components/CallingSettingsPanel/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/ringcentral-widgets/components/CallingSettingsPanel/i18n/index.ts +++ b/packages/ringcentral-widgets/components/CallingSettingsPanel/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/ringcentral-widgets/components/CallingSettingsPanel/styles.scss b/packages/ringcentral-widgets/components/CallingSettingsPanel/styles.scss index 1e2ad6eb75..eb7427b746 100644 --- a/packages/ringcentral-widgets/components/CallingSettingsPanel/styles.scss +++ b/packages/ringcentral-widgets/components/CallingSettingsPanel/styles.scss @@ -65,5 +65,5 @@ .overWidth { left: -1px; - width: calc(100% + 2px) + width: calc(100% + 2px); } diff --git a/packages/ringcentral-widgets/components/CallsListPanel/i18n/en-US.ts b/packages/ringcentral-widgets/components/CallsListPanel/i18n/en-US.ts index dc71948390..45e8db3c78 100644 --- a/packages/ringcentral-widgets/components/CallsListPanel/i18n/en-US.ts +++ b/packages/ringcentral-widgets/components/CallsListPanel/i18n/en-US.ts @@ -7,4 +7,4 @@ export default { historyCalls: 'Past Calls', otherDeviceCall: 'Ongoing calls on my other devices', searchPlaceholder: 'Search...', -}; +} as const; diff --git a/packages/ringcentral-widgets/components/CallsListPanel/i18n/es-419.ts b/packages/ringcentral-widgets/components/CallsListPanel/i18n/es-419.ts index a883dbf338..3e1a81c7a7 100644 --- a/packages/ringcentral-widgets/components/CallsListPanel/i18n/es-419.ts +++ b/packages/ringcentral-widgets/components/CallsListPanel/i18n/es-419.ts @@ -6,7 +6,7 @@ export default { onHoldCall: "Llamada en espera", historyCalls: "Llamadas anteriores", otherDeviceCall: "Llamadas en curso en mis otros dispositivos", - searchPlaceholder: "Buscar..." + searchPlaceholder: "Buscar…" }; // @key: @#@"noActiveCalls"@#@ @source: @#@"No active calls"@#@ diff --git a/packages/ringcentral-widgets/components/CallsListPanel/i18n/index.ts b/packages/ringcentral-widgets/components/CallsListPanel/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/ringcentral-widgets/components/CallsListPanel/i18n/index.ts +++ b/packages/ringcentral-widgets/components/CallsListPanel/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/ringcentral-widgets/components/CallsListPanel/index.tsx b/packages/ringcentral-widgets/components/CallsListPanel/index.tsx index 6979ee158b..82a53bc8b0 100644 --- a/packages/ringcentral-widgets/components/CallsListPanel/index.tsx +++ b/packages/ringcentral-widgets/components/CallsListPanel/index.tsx @@ -1,9 +1,7 @@ -import React from 'react'; - -import classnames from 'classnames'; -import PropTypes from 'prop-types'; - import debounce from '@ringcentral-integration/commons/lib/debounce'; +import clsx from 'clsx'; +import PropTypes from 'prop-types'; +import React from 'react'; import ActiveCallItem from '../ActiveCallItem'; import CallList from '../CallList'; @@ -13,6 +11,7 @@ import LogNotification from '../LogNotification'; import LogSection from '../LogSection'; import { SearchInput } from '../SearchInput'; import { SpinnerOverlay } from '../SpinnerOverlay'; + import i18n from './i18n'; import styles from './styles.scss'; @@ -62,7 +61,7 @@ const ActiveCallList = ({ return null; } return ( -
+
{title}
@@ -342,7 +341,6 @@ class CallsListPanel extends React.PureComponent { maskStyle={styles.maskStyle} > ) : ( +
) : ( -
+
{!onlyHistory && (
{i18n.getString('historyCalls', currentLocale)} @@ -705,16 +704,17 @@ class CallsListPanel extends React.PureComponent { return (
{children} {search}
(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/ringcentral-widgets/components/CallsOnholdPanel/index.tsx b/packages/ringcentral-widgets/components/CallsOnholdPanel/index.tsx index 937d78373f..e1b5e59ffd 100644 --- a/packages/ringcentral-widgets/components/CallsOnholdPanel/index.tsx +++ b/packages/ringcentral-widgets/components/CallsOnholdPanel/index.tsx @@ -5,6 +5,7 @@ import { ActiveCallItem } from '../ActiveCallItemV2'; import BackButton from '../BackButton'; import BackHeader from '../BackHeader'; import CircleButton from '../CircleButton'; + import i18n from './i18n'; import styles from './styles.scss'; @@ -16,6 +17,7 @@ type CallsOnholdContainerProps = { countryCode: string; brand?: string; showContactDisplayPlaceholder?: boolean; + showCallerIdName?: boolean; webphoneAnswer?: (...args: any[]) => any; webphoneReject?: (...args: any[]) => any; webphoneHangup?: (...args: any[]) => any; @@ -31,7 +33,7 @@ type CallsOnholdContainerProps = { onAdd?: (...args: any[]) => any; getAvatarUrl?: (...args: any[]) => any; }; -const CallsOnholdContainer: React.SFC = ({ +const CallsOnholdContainer: React.FC = ({ calls, currentLocale, areaCode, @@ -39,6 +41,7 @@ const CallsOnholdContainer: React.SFC = ({ brand, showContactDisplayPlaceholder, autoLog, + showCallerIdName, webphoneAnswer, webphoneReject, webphoneHangup, @@ -77,6 +80,7 @@ const CallsOnholdContainer: React.SFC = ({ areaCode={areaCode} countryCode={countryCode} brand={brand} + showCallerIdName={showCallerIdName} showContactDisplayPlaceholder={showContactDisplayPlaceholder} onMergeCall={onMerge} // @ts-expect-error TS(2322): Type '((...args: any[]) => any) | undefined' is no... Remove this comment to see the full error message diff --git a/packages/ringcentral-widgets/components/CallsPanel/index.tsx b/packages/ringcentral-widgets/components/CallsPanel/index.tsx index dd6de992e5..abebacb3ae 100644 --- a/packages/ringcentral-widgets/components/CallsPanel/index.tsx +++ b/packages/ringcentral-widgets/components/CallsPanel/index.tsx @@ -1,12 +1,12 @@ -import React from 'react'; - import debounce from '@ringcentral-integration/commons/lib/debounce'; +import React from 'react'; import CallList from '../CallList'; import CallListV2 from '../CallListV2'; import { Header } from '../Header'; import Panel from '../Panel'; import { SpinnerOverlay } from '../SpinnerOverlay'; + import styles from './styles.scss'; const HEADER_HEIGHT = 38; @@ -41,6 +41,7 @@ type CallsPanelProps = { enableContactFallback?: boolean; autoLog?: boolean; showContactDisplayPlaceholder?: boolean; + showCallerIdName?: boolean; sourceIcons?: object; phoneTypeRenderer?: (...args: any[]) => any; phoneSourceNameRenderer?: (...args: any[]) => any; @@ -130,6 +131,7 @@ class CallsPanel extends React.PureComponent { enableContactFallback, autoLog, showContactDisplayPlaceholder, + showCallerIdName, sourceIcons, phoneTypeRenderer, phoneSourceNameRenderer, @@ -170,6 +172,7 @@ class CallsPanel extends React.PureComponent { webphoneHangup={webphoneHangup} webphoneResume={webphoneResume} enableContactFallback={enableContactFallback} + showCallerIdName={showCallerIdName} autoLog={autoLog} showContactDisplayPlaceholder={showContactDisplayPlaceholder} sourceIcons={sourceIcons} @@ -205,12 +208,9 @@ class CallsPanel extends React.PureComponent { dateTimeFormatter={dateTimeFormatter} active={active} loggingMap={loggingMap} - webphoneAnswer={webphoneAnswer} - webphoneReject={webphoneReject} - webphoneHangup={webphoneHangup} - webphoneResume={webphoneResume} enableContactFallback={enableContactFallback} autoLog={autoLog} + showCallerIdName={showCallerIdName} showContactDisplayPlaceholder={showContactDisplayPlaceholder} sourceIcons={sourceIcons} phoneTypeRenderer={phoneTypeRenderer} @@ -252,6 +252,7 @@ CallsPanel.defaultProps = { enableContactFallback: undefined, autoLog: false, showContactDisplayPlaceholder: true, + showCallerIdName: false, sourceIcons: undefined, phoneTypeRenderer: undefined, phoneSourceNameRenderer: undefined, diff --git a/packages/ringcentral-widgets/components/CheckBox/index.tsx b/packages/ringcentral-widgets/components/CheckBox/index.tsx index 41fa470a8c..861974292e 100644 --- a/packages/ringcentral-widgets/components/CheckBox/index.tsx +++ b/packages/ringcentral-widgets/components/CheckBox/index.tsx @@ -1,7 +1,6 @@ +import clsx from 'clsx'; import React from 'react'; -import classnames from 'classnames'; - import styles from './styles.scss'; type CheckBoxProps = { @@ -18,7 +17,7 @@ type CheckBoxProps = { disabled?: boolean; }; // @ts-expect-error TS(2322): Type '{ ({ data, selected, onSelect, valueField, t... Remove this comment to see the full error message -const CheckBox: React.SFC = ({ +const CheckBox: React.FC = ({ data, selected, onSelect, @@ -55,15 +54,16 @@ const CheckBox: React.SFC = ({ data-sign={isSelected ? 'selectedItem' : undefined} >
-
+
{isListObject ? item[textField] : item}
@@ -76,12 +76,12 @@ const CheckBox: React.SFC = ({ ); } case 'checkbox': { - const checkboxWrapperClassNames = classnames( + const checkboxWrapperClassNames = clsx( styles.checkboxWrapper, disabled ? styles.wrapperDisabled : '', className, ); - const checkboxClassName = classnames( + const checkboxClassName = clsx( styles.checkbox, checked ? styles.checked : '', disabled ? styles.checkboxDisabled : '', diff --git a/packages/ringcentral-widgets/components/CircleButton/index.tsx b/packages/ringcentral-widgets/components/CircleButton/index.tsx index 8c40fef4c9..424ad96c93 100644 --- a/packages/ringcentral-widgets/components/CircleButton/index.tsx +++ b/packages/ringcentral-widgets/components/CircleButton/index.tsx @@ -1,6 +1,5 @@ -import React from 'react'; - -import classnames from 'classnames'; +import clsx from 'clsx'; +import React, { forwardRef } from 'react'; import styles from './styles.scss'; @@ -23,73 +22,87 @@ type CircleButtonProps = { title?: string; showRipple?: boolean; }; -const CircleButton: React.SFC = (props) => { +const CircleButton: React.FC = ({ + showBorder = true, + disabled = false, + width = '100%', + height = '100%', + x = 0, + y = 0, + iconWidth = 200, + iconHeight = 200, + title, + iconX = 150, + iconY = 150, + showRipple = false, + icon: iconProp, + onClick: onClickProp, + iconClassName, + dataSign, + className, +}) => { let icon; - if (props.icon) { - const Icon = props.icon; + if (iconProp) { + const Icon = iconProp; icon = ( ); } - const circleClass = classnames( - styles.circle, - !props.showBorder && styles.noBorder, - ); - const onClick = props.disabled ? null : props.onClick; + const circleClass = clsx(styles.circle, !showBorder && styles.noBorder); + const onClick = disabled ? null : onClickProp; + return ( { - // @ts-expect-error TS(2339): Property 'tagName' does not exist on type 'EventTa... Remove this comment to see the full error message - if (e.target && e.target.tagName !== 'svg' && onClick) { - onClick(e); + if ( + // Add NODE_ENV as a workaround for integration test env when triggering its events by data-sign + process.env.NODE_ENV === 'test' || + (e.target && (e.target as HTMLElement).tagName !== 'svg') + ) { + onClick?.(e); } }} - width={props.width} - height={props.height} - x={props.x} - y={props.y} + width={width} + height={height} + x={x} + y={y} + // TODO: add title to svg for we can check with title + // @ts-ignore + title={title} > - {props.title ? {props.title} : null} + {title ? {title} : null} {icon} - {props.showRipple ? ( + {showRipple ? ( ) : null} ); }; -CircleButton.defaultProps = { - icon: undefined, - className: undefined, - dataSign: undefined, - showBorder: true, - iconClassName: undefined, - disabled: false, - // @ts-expect-error TS(2322): Type 'null' is not assignable to type '((...args: ... Remove this comment to see the full error message - onClick: null, - width: '100%', - height: '100%', - x: 0, - y: 0, - iconWidth: 200, - iconHeight: 200, - iconX: 150, - iconY: 150, - // @ts-expect-error TS(2322): Type 'null' is not assignable to type 'string | un... Remove this comment to see the full error message - title: null, - showRipple: false, -}; + export default CircleButton; + +// TODO: that component for wrap CircleButton error write way for not support title +export const CircleButtonWithTitle = forwardRef< + HTMLSpanElement, + CircleButtonProps +>(({ title, ...rest }, ref) => { + return ( + + + + ); +}); diff --git a/packages/ringcentral-widgets/components/ComboBox/ComboBox.tsx b/packages/ringcentral-widgets/components/ComboBox/ComboBox.tsx index d5366c4711..e84b76032d 100644 --- a/packages/ringcentral-widgets/components/ComboBox/ComboBox.tsx +++ b/packages/ringcentral-widgets/components/ComboBox/ComboBox.tsx @@ -1,8 +1,8 @@ +import clsx from 'clsx'; import React, { Component, createRef } from 'react'; -import classnames from 'classnames'; - import dynamicsFont from '../../assets/DynamicsFont/DynamicsFont.scss'; + import styles from './styles.scss'; type ComboBoxProps = { @@ -217,7 +217,7 @@ class ComboBox extends Component { } return (
    { > {currentOptions.map((option, idx) => { const currentValue = this.valueFunction(option, idx); - const className = classnames( + const className = clsx( styles.dropdownItem, value === currentValue ? styles.selected : null, ); @@ -237,7 +237,7 @@ class ComboBox extends Component {
  • { icon, } = this.props; const currentLabel = label ? {label} : null; - const currentIconClassName = classnames( + const currentIconClassName = clsx( styles.icon, this.state.open ? styles.iconUp : null, iconClassName, ); - const containerClassName = classnames( + const containerClassName = clsx( styles.root, className, disabled ? styles.disabled : null, this.state.open ? styles.open : null, noPadding ? styles.noPadding : null, ); - const buttonClassName = classnames( + const buttonClassName = clsx( styles.button, disabled ? styles.disabled : null, ); @@ -297,7 +297,7 @@ class ComboBox extends Component { return (
    { if (reference) reference(ref); this.wrapper = ref; @@ -306,7 +306,7 @@ class ComboBox extends Component {
    { // @ts-expect-error TS(2322): Type 'RefObject' is not assignable to typ... Remove this comment to see the full error message ref={this.inputRef} data-sign="selectedItem" - className={classnames( + className={clsx( styles.selectedValue, ellipsis && styles.ellipsis, selectedClassName, diff --git a/packages/ringcentral-widgets/components/CommunicationSetupPanel/CommunicationSetupPanel.tsx b/packages/ringcentral-widgets/components/CommunicationSetupPanel/CommunicationSetupPanel.tsx index f9b82953c0..0dca6c7cff 100644 --- a/packages/ringcentral-widgets/components/CommunicationSetupPanel/CommunicationSetupPanel.tsx +++ b/packages/ringcentral-widgets/components/CommunicationSetupPanel/CommunicationSetupPanel.tsx @@ -1,6 +1,5 @@ -import type { FunctionComponent } from 'react'; -import React, { useMemo, useRef, useState } from 'react'; - +import type UserPhoneNumberInfo from '@rc-ex/core/lib/definitions/UserPhoneNumberInfo'; +import { trackEvents } from '@ringcentral-integration/commons/enums/trackEvents'; import { BLOCKED_ID_VALUE } from '@ringcentral-integration/commons/modules/CallingSettings'; import type { ToNumber } from '@ringcentral-integration/commons/modules/ComposeText'; import { @@ -10,18 +9,24 @@ import { RcMenuItem, RcSelect, RcText, + useDebounce, + useDepsChange, + useRefState, } from '@ringcentral/juno'; import { DeleteCircle } from '@ringcentral/juno-icon'; +import type { FunctionComponent } from 'react'; +import React, { useMemo, useRef, useState } from 'react'; import { useCommunicationSetupContext } from '../../contexts'; import type { TabsEnumType } from '../ContactSearchPanel/ContactSearchPanelEnum'; import { TabsEnum } from '../ContactSearchPanel/ContactSearchPanelEnum'; import fromFieldI18n from '../FromField/i18n'; import inputI18n from '../RecipientsInput/i18n'; + import { CommunicationSetupProvider } from './CommunicationSetupProvider'; import ContactSearchContainer from './ContactSearchContainer'; import { isSplitterKey } from './helper'; -import i18n, { I18nKey } from './i18n'; +import i18n, { type I18nKey } from './i18n'; import { CallFields, FieldLine, @@ -59,53 +64,59 @@ const FromField: FunctionComponent = ({ options, blockedLabel, inputRef, -}) => ( - - - {`${fromFieldI18n.getString('from', currentLocale)}:`} - - - val === BLOCKED_ID_VALUE ? blockedLabel : formatPhone(val) - } - disabled={disableFromField} - value={fromNumber} - InputProps={{ - // classes: customSelectInputClasses, - disableUnderline: true, - }} - MenuProps={{ - TransitionProps: { - onExited: () => { - inputRef.current?.focus(); +}) => { + return ( + + + {`${fromFieldI18n.getString('from', currentLocale)}:`} + + { + const text = + val === BLOCKED_ID_VALUE ? blockedLabel : formatPhone(val); + return {text}; + }} + disabled={disableFromField} + value={fromNumber} + InputProps={{ + // classes: customSelectInputClasses, + disableUnderline: true, + }} + MenuProps={{ + TransitionProps: { + onExited: () => { + inputRef.current?.focus(); + }, }, - }, - }} - > - {options.map((item) => ( - changeFromNumber(item)} - value={item.phoneNumber} - key={item.phoneNumber} - data-sign="selectMenuItem" - > - {item.phoneNumber === BLOCKED_ID_VALUE ? ( - - ) : ( - - )} - - ))} - - -); + }} + > + {options.map((item, index) => ( + changeFromNumber(item)} + value={item.phoneNumber} + key={item.phoneNumber} + data-sign={`selectMenuItem${index}`} + > + {item.phoneNumber === BLOCKED_ID_VALUE ? ( + + ) : ( + + )} + + ))} + + + ); +}; // TODO: keep original style, wait check with designer // const CustomSelect = styled(RcSelect)``; @@ -128,16 +139,18 @@ export type CommunicationSetupPanelProps = { placeholder?: string | null; recipients: ToNumber[]; toNumber: string; + triggerEventTracking: (eventName: string, contactType: string) => any; setRecipient: (...args: any[]) => any; clearRecipient: (...args: any[]) => any; autoFocus?: boolean; multiple?: boolean; directlyProceedType?: I18nKey; inputFullWidth: boolean; + filterCallQueueExtension?: boolean; // From field showAnonymous?: boolean; fromNumber?: string; - fromNumbers?: { phoneNumber: string; usageType?: string }[]; + fromNumbers?: UserPhoneNumberInfo[]; changeFromNumber?: (...args: any[]) => any; formatPhone?: (...args: any[]) => string; detectPhoneNumbers?: (...args: any[]) => any; @@ -146,238 +159,304 @@ export type CommunicationSetupPanelProps = { // Common currentLocale: string; defaultTab?: TabsEnumType; + ContactSearch?: FunctionComponent; }; -const CommunicationSetupWrapper: FunctionComponent = - (props) => { - const { - currentLocale, - onToNumberChange, - toNumber, - fromNumber, - fromNumbers, - changeFromNumber, - formatPhone, - recipients, - multiple = false, - directlyProceedType = 'dial', - setRecipient, - clearRecipient, - autoFocus, - showFromField = true, - disableFromField = false, - children, - showAnonymous, - label = `${inputI18n.getString('to', currentLocale)}:`, - placeholder = `${inputI18n.getString( - 'enterNameOrNumber', - currentLocale, - )}`, - detectPhoneNumbers, - defaultTab = TabsEnum.thirdParty, - inputFullWidth, - } = props; - - const inputRef = useRef(null); - - const [openSearchPage, setOpenSearchPage] = useState(false); - const showSearchPage = openSearchPage && !!toNumber.trim(); - const setRecipientByChars = (Chars: string) => { - setOpenSearchPage(false); - setRecipient({ - name: Chars, - phoneNumber: Chars, - }); - inputRef.current?.blur(); - }; - - const setRecipientHandler = (optionItem: any) => { - setOpenSearchPage(false); - setRecipient(optionItem); - }; +const CommunicationSetupWrapper: FunctionComponent< + CommunicationSetupPanelProps +> = (props) => { + const { + currentLocale, + onToNumberChange, + triggerEventTracking, + toNumber, + fromNumber, + fromNumbers, + changeFromNumber, + formatPhone, + recipients, + multiple = false, + directlyProceedType = 'dial', + setRecipient, + clearRecipient, + autoFocus, + showFromField = true, + disableFromField = false, + children, + showAnonymous, + label = `${inputI18n.getString('to', currentLocale)}:`, + placeholder = `${inputI18n.getString('enterNameOrNumber', currentLocale)}`, + detectPhoneNumbers, + defaultTab = TabsEnum.thirdParty, + inputFullWidth, + filterCallQueueExtension, + } = props; - const keyDownHandler = (e: React.KeyboardEvent) => { - if (isSplitterKey(e) && !!toNumber.trim()) { - e.preventDefault(); - setRecipientByChars(toNumber); - } - }; + const inputRef = useRef(null); - const closeSearchPage = () => { - setOpenSearchPage(false); - onToNumberChange(''); - inputRef.current?.blur(); - }; + const [innerToNumber, setInnerToNumber, { updating: startTyping }] = + useAsyncTextFieldState(toNumber); - const hasTags = recipients.length > 0; + const [openSearchPage, setOpenSearchPage] = useState(false); + const showSearchPage = openSearchPage && !!toNumber.trim(); + const setRecipientByChars = (Chars: string) => { + setOpenSearchPage(false); + setRecipient({ + name: Chars, + phoneNumber: Chars, + }); + inputRef.current?.blur(); + }; - const hiddenInput = !multiple && hasTags; + const setRecipientHandler = (optionItem: any) => { + setOpenSearchPage(false); + setRecipient(optionItem); + }; - const options = useMemo(() => { - if (showAnonymous) { - return [ - // @ts-expect-error TS(2488): Type '{ phoneNumber: string; usageType?: string | ... Remove this comment to see the full error message - ...fromNumbers, - { - phoneNumber: BLOCKED_ID_VALUE, - }, - ]; - } + const keyDownHandler = (e: React.KeyboardEvent) => { + if (isSplitterKey(e) && !!toNumber.trim()) { + e.preventDefault(); + setRecipientByChars(toNumber); + } + }; - return fromNumbers; - }, [fromNumbers, showAnonymous]); + const closeSearchPage = () => { + setInnerToNumber(''); + setOpenSearchPage(false); + onToNumberChange(''); + inputRef.current?.blur(); + }; - const blockedLabel = fromFieldI18n.getString('Blocked', currentLocale); - const { inputPropsRef } = useCommunicationSetupContext(); + const hasTags = recipients.length > 0; - const InputProps = { - onChange: (...args: any[]) => { - // @ts-expect-error TS(2532): Object is possibly 'undefined'. - inputPropsRef?.current?.onChange.apply(null, args); - }, - onKeyDown: (...args: any[]) => { - // @ts-expect-error TS(2532): Object is possibly 'undefined'. - inputPropsRef?.current?.onKeyDown.apply(null, args); - }, - }; - - const RecipientComponent = useMemo( - () => - !!recipients.length && ( - - {recipients.map((item, index) => ( - clearRecipient(item)} - label={item.name || item.phoneNumber} - key={`${item.phoneNumber} - ${index}`} - error={item.isWarning} - /> - ))} - - ), - [recipients, clearRecipient], - ); - - const inputChangeHandler = (value: string) => { - if (value.trim()) { - setOpenSearchPage(true); - } else { - setOpenSearchPage(false); - } - - onToNumberChange(value); - }; - - return ( - - - - {label} - - {RecipientComponent} - {!hiddenInput && ( - { + if (showAnonymous) { + return [ + // @ts-expect-error TS(2488): Type '{ phoneNumber: string; usageType?: string | ... Remove this comment to see the full error message + ...fromNumbers, + { + phoneNumber: BLOCKED_ID_VALUE, + }, + ]; + } + + return fromNumbers; + }, [fromNumbers, showAnonymous]); + + const blockedLabel = fromFieldI18n.getString('Blocked', currentLocale); + const { inputPropsRef } = useCommunicationSetupContext(); + + const InputProps = { + onChange: (...args: any[]) => { + // @ts-expect-error TS(2532): Object is possibly 'undefined'. + inputPropsRef?.current?.onChange.apply(null, args); + }, + onKeyDown: (...args: any[]) => { + // @ts-expect-error TS(2532): Object is possibly 'undefined'. + inputPropsRef?.current?.onKeyDown.apply(null, args); + }, + }; + + const RecipientComponent = useMemo( + () => + !!recipients.length && ( + + {recipients.map((item, index) => ( + clearRecipient(item)} + label={item.name || item.phoneNumber} + key={`${item.phoneNumber} - ${index}`} + error={item.isWarning} + /> + ))} + + ), + [recipients, clearRecipient], + ); + + const inputChangeHandler = (value: string) => { + startTyping(value); + + if (value.trim()) { + setOpenSearchPage(true); + } else { + setOpenSearchPage(false); + } + + onToNumberChange(value); + }; + + const ContactSearch = props.ContactSearch ?? ContactSearchContainer; + return ( + + + + {label} + + {RecipientComponent} + {!hiddenInput && ( + { + if ( + detectPhoneNumbers && + ev.clipboardData && + ev.clipboardData.getData + ) { + const pastedText = ev.clipboardData.getData('text/plain'); + ev.preventDefault(); + const result = await detectPhoneNumbers(pastedText); + !result && inputChangeHandler(pastedText); } - onPaste={async (ev) => { - if ( - detectPhoneNumbers && - ev.clipboardData && - ev.clipboardData.getData - ) { - const pastedText = ev.clipboardData.getData('text/plain'); - ev.preventDefault(); - const result = await detectPhoneNumbers(pastedText); - !result && inputChangeHandler(pastedText); + }} + InputProps={{ + ...InputProps, + endAdornment: !!toNumber.length && ( + { + e.preventDefault(); + closeSearchPage(); + }} + > + + + ), + onFocus: () => { + if (toNumber.trim()) { + setOpenSearchPage(true); } - }} - InputProps={{ - ...InputProps, - endAdornment: !!toNumber.length && ( - { - e.preventDefault(); - closeSearchPage(); - }} - > - - - ), - onFocus: () => { - if (toNumber.trim()) { - setOpenSearchPage(true); - } - }, - }} - inputProps={{ - 'data-sign': 'recipientsInput', - maxLength, - }} - /> - )} - - - {showFromField && ( - + )} + + + {showFromField && ( + string) | undefined' is... Remove this comment to see the full error message + formatPhone={formatPhone} + // @ts-expect-error TS(2322): Type '((...args: any[]) => any) | undefined' is no... Remove this comment to see the full error message + changeFromNumber={changeFromNumber} + // @ts-expect-error TS(2322): Type 'any[] | undefined' is not assignable to type... Remove this comment to see the full error message + options={options} + blockedLabel={blockedLabel} + /> + )} + + + {showSearchPage && ( + + string) | undefined' is... Remove this comment to see the full error message - formatPhone={formatPhone} - // @ts-expect-error TS(2322): Type '((...args: any[]) => any) | undefined' is no... Remove this comment to see the full error message - changeFromNumber={changeFromNumber} - // @ts-expect-error TS(2322): Type 'any[] | undefined' is not assignable to type... Remove this comment to see the full error message - options={options} - blockedLabel={blockedLabel} + userInput={toNumber} + defaultTab={defaultTab} + directlyProceedText={i18n.getString( + directlyProceedType, + currentLocale, + )} /> - )} - - - {showSearchPage && ( - - - - )} - {children} - - - ); + + )} + {children} + + + ); +}; + +export const CommunicationSetupPanel: FunctionComponent< + CommunicationSetupPanelProps +> = (props) => ( + + + +); + +/** + * Custom hook for managing async text field control state. + * + * use for update value in sync way when using `updating` method, + * in typing mode, that will not update the value immediately, that will wait for 500ms to update the value, to avoid too many rerender and async cause input cursor jump + * + * @param initValue - The initial value for the control state. + */ +const useAsyncTextFieldState = (initValue: string) => { + const [state, _setState] = useRefState(initValue); + + const debouncingRef = useRef(false); + const setState = (val: string, isUpdate?: boolean | undefined) => { + debouncingRef.current = false; + debounceSetState.cancel(); + _setState(val, isUpdate); }; -export const CommunicationSetupPanel: FunctionComponent = - (props) => ( - - - - ); + const debounceSetState = useDebounce(() => { + setState(initValue); + }, 500); + + useDepsChange(() => { + if (state.current === initValue) { + debouncingRef.current = false; + + return; + } + + // when be empty from outside, cancel previous debounce prevent update show again + if (state.current !== '' && initValue === '') { + setState('', false); + } else if (debouncingRef.current) { + // use debounce to avoid too many rerender and async cause input cursor jump + debounceSetState(); + } else { + setState(initValue, false); + } + }, [initValue]); + + return [ + state.current, + setState, + { + updating: (value: string) => { + debouncingRef.current = value !== ''; + _setState(value); + }, + }, + ] as const; +}; diff --git a/packages/ringcentral-widgets/components/CommunicationSetupPanel/DirectlyProceedLine.tsx b/packages/ringcentral-widgets/components/CommunicationSetupPanel/DirectlyProceedLine.tsx index d3f1bd27f7..e7a467c826 100644 --- a/packages/ringcentral-widgets/components/CommunicationSetupPanel/DirectlyProceedLine.tsx +++ b/packages/ringcentral-widgets/components/CommunicationSetupPanel/DirectlyProceedLine.tsx @@ -1,6 +1,3 @@ -import type { FunctionComponent } from 'react'; -import React, { memo } from 'react'; - import { RcAvatar, RcIcon, @@ -10,6 +7,8 @@ import { RcListItemText, } from '@ringcentral/juno'; import { DefaultAvatar, Dial } from '@ringcentral/juno-icon'; +import type { FunctionComponent } from 'react'; +import React, { memo } from 'react'; import i18n from './i18n'; diff --git a/packages/ringcentral-widgets/components/CommunicationSetupPanel/i18n/en-US.ts b/packages/ringcentral-widgets/components/CommunicationSetupPanel/i18n/en-US.ts index 463a503c94..71fff0ac04 100644 --- a/packages/ringcentral-widgets/components/CommunicationSetupPanel/i18n/en-US.ts +++ b/packages/ringcentral-widgets/components/CommunicationSetupPanel/i18n/en-US.ts @@ -2,4 +2,4 @@ export default { dial: 'Dial', message: 'Message', transfer: 'Transfer', -}; +} as const; diff --git a/packages/ringcentral-widgets/components/CommunicationSetupPanel/i18n/index.ts b/packages/ringcentral-widgets/components/CommunicationSetupPanel/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/ringcentral-widgets/components/CommunicationSetupPanel/i18n/index.ts +++ b/packages/ringcentral-widgets/components/CommunicationSetupPanel/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/ringcentral-widgets/components/ComposeTextPanel/NoSenderAlert.tsx b/packages/ringcentral-widgets/components/ComposeTextPanel/NoSenderAlert.tsx index edb09415f3..a89ab3c4c9 100644 --- a/packages/ringcentral-widgets/components/ComposeTextPanel/NoSenderAlert.tsx +++ b/packages/ringcentral-widgets/components/ComposeTextPanel/NoSenderAlert.tsx @@ -1,6 +1,5 @@ -import React, { Component } from 'react'; - import { messageSenderMessages } from '@ringcentral-integration/commons/modules/MessageSender'; +import React, { Component } from 'react'; import AlertDisplay from '../AlertDisplay'; import MessageSenderAlert from '../AlertRenderer/MessageSenderAlert'; diff --git a/packages/ringcentral-widgets/components/ComposeTextPanel/index.tsx b/packages/ringcentral-widgets/components/ComposeTextPanel/index.tsx index 74618964a1..25c6e0acaf 100644 --- a/packages/ringcentral-widgets/components/ComposeTextPanel/index.tsx +++ b/packages/ringcentral-widgets/components/ComposeTextPanel/index.tsx @@ -1,18 +1,18 @@ -import React, { Component } from 'react'; - -import classnames from 'classnames'; - import type { ToNumber } from '@ringcentral-integration/commons/modules/ComposeText'; +import clsx from 'clsx'; +import React, { Component } from 'react'; import FromField from '../FromField'; import MessageInput from '../MessageInput'; import RecipientsInput from '../RecipientsInput'; import { RecipientsInputV2 } from '../RecipientsInputV2'; import { SpinnerOverlay } from '../SpinnerOverlay'; + import NoSenderAlert from './NoSenderAlert'; import styles from './styles.scss'; export interface ComposeTextPanelProps { + triggerEventTracking: (eventName: string, contactType: string) => any; brand?: string; className?: string; send: (...args: any[]) => any; @@ -178,7 +178,7 @@ class ComposeTextPanel extends Component< ? searchContactList : []; return ( -
    +
    {showSpinner ? : null} any; formatPhone: (...args: any[]) => any; detectPhoneNumbers: (...args: any[]) => any; currentLocale: string; @@ -44,6 +46,8 @@ export interface ComposeTextPanelProps { addAttachment?: (...args: any[]) => any; removeAttachment?: (...args: any[]) => any; hintInfo?: JSX.Element; + // TODO: fix type + contactSearch?: any; } export const ComposeTextPanel: FunctionComponent = ({ @@ -75,6 +79,8 @@ export const ComposeTextPanel: FunctionComponent = ({ cleanTypingToNumber, removeToNumber, detectPhoneNumbers, + contactSearch, + triggerEventTracking, }) => { const hasSenderNumbers = senderNumbers.length > 0; const hasPersonalRecipient = toNumbers.some((x) => x && x.type !== 'company'); @@ -96,6 +102,7 @@ export const ComposeTextPanel: FunctionComponent = ({ {showSpinner && } = ({ changeFromNumber={updateSenderNumber} showFromField={hasSenderNumbers} inputFullWidth={!!hintInfo} + ContactSearch={contactSearch} + filterCallQueueExtension > void; + getContactNameInfo: ( + sessionId: string, + isHost?: boolean, + ) => { + logName?: string; + entityDetailLink?: string; + displayEntity?: IContact; + entityType?: string; + }; + onRemoveParticipant: ( + telephonySessionId: string, + removedPartyId: string, + ) => Promise; + renderAvatar?: (item: { + displayEntity?: IContact; + entityType?: string; + name?: string; + }) => JSX.Element; + clickRemoveParticipantTrack?: () => void; +} diff --git a/packages/ringcentral-widgets/components/ConferenceCallParticipants/ConferenceCallParticipants.tsx b/packages/ringcentral-widgets/components/ConferenceCallParticipants/ConferenceCallParticipants.tsx new file mode 100644 index 0000000000..be49096866 --- /dev/null +++ b/packages/ringcentral-widgets/components/ConferenceCallParticipants/ConferenceCallParticipants.tsx @@ -0,0 +1,230 @@ +import { format } from '@ringcentral-integration/utils'; +import { + RcAvatar, + RcButton, + RcDialogActions, + RcDrawer, + RcIconButton, + RcLink, + RcList, + RcListItem, + RcListItemAvatar, + RcListItemSecondaryAction, + RcListItemText, + RcTypography, + spacing, + styled, + useAvatarShortName, +} from '@ringcentral/juno'; +import { RemoveMemberBorder } from '@ringcentral/juno-icon'; +import React, { useState, type FunctionComponent } from 'react'; + +import { ConferenceCallParticipantsProps } from './ConferenceCallParticipants.interface'; +import i18n from './i18n'; + +const InnerContainer = styled.div` + display: grid; + gap: ${spacing(3)}; + padding: ${spacing(0, 4)}; + margin: ${spacing(4, 0)}; + + ${RcDialogActions} { + margin-top: ${spacing(2)}; + padding: 0; + } + + ${RcList} { + overflow: hidden; + + ${RcListItem} { + padding-left: 0; + padding-right: 0; + } + } +`; + +export const ConferenceCallParticipants: FunctionComponent< + ConferenceCallParticipantsProps +> = ({ + isOpen, + currentLocale, + participants = [], + toggleConference, + currentTelephonySessionId, + getContactNameInfo, + renderAvatar, + onRemoveParticipant, + clickRemoveParticipantTrack, +}) => { + const [removeData, setRemoveData] = useState<{ + removedPartyId: string; + name: string; + } | void>(); + const closeRemoveModal = () => { + setRemoveData(undefined); + }; + const length = participants.length; + const removedModalOpen = !!removeData && isOpen; + return ( + <> + + + + {i18n.getString('removeTitle', currentLocale)} + + + {format(i18n.getString('removeDescription', currentLocale), { + name: removeData?.name, + })} + + + { + await onRemoveParticipant( + currentTelephonySessionId, + removeData?.removedPartyId!, + ); + closeRemoveModal(); + }} + > + {i18n.getString('confirmButtonText', currentLocale)} + + + {i18n.getString('cancelButtonText', currentLocale)} + + + + + + toggleConference(false)} + > + + + {`${i18n.getString('participants', currentLocale)} (${length})`} + + + {participants.map( + ({ + telephonySessionId, + sessionId, + partyId, + isHost, + sessionName, + }) => { + const { + logName = sessionName, + entityDetailLink, + displayEntity, + entityType, + } = getContactNameInfo(sessionId, isHost); + const ConferenceAvatarIcon = renderAvatar ? ( + renderAvatar({ displayEntity, entityType, name: logName }) + ) : ( + + ); + + const displayName = isHost + ? `${logName} ${i18n.getString('host', currentLocale)}` + : logName; + return ( + + + {ConferenceAvatarIcon} + + { + window.open(entityDetailLink, '_blank'); + }} + > + {displayName} + + ) : ( + displayName + ) + } + /> + + {!isHost && ( + + { + clickRemoveParticipantTrack?.(); + setRemoveData({ + removedPartyId: partyId, + name: logName, + }); + }} + /> + + )} + + ); + }, + )} + + + + + ); +}; + +export const ConferenceAvatar: FunctionComponent<{ name: string }> = ({ + name, +}) => { + const [firstName, lastName] = name?.split(/\s+/) || []; + const presentAvatarName = useAvatarShortName({ + firstName, + lastName, + }); + return ( + + {presentAvatarName} + + ); +}; diff --git a/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/de-DE.js b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/de-DE.js new file mode 100644 index 0000000000..832f8b50c5 --- /dev/null +++ b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/de-DE.js @@ -0,0 +1,17 @@ +export default { + participants: "Teilnehmer", + host: "(Gastgeber)", + removeParticipant: "Teilnehmer entfernen", + removeTitle: "Teilnehmer entfernen", + removeDescription: "{name} aus dieser Konferenz entfernen?", + confirmButtonText: "Entfernen", + cancelButtonText: "Abbrechen" +}; + +// @key: @#@"participants"@#@ @source: @#@"Participants"@#@ +// @key: @#@"host"@#@ @source: @#@"(Host)"@#@ +// @key: @#@"removeParticipant"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeTitle"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeDescription"@#@ @source: @#@"Remove {name} from this conference?"@#@ +// @key: @#@"confirmButtonText"@#@ @source: @#@"Remove"@#@ +// @key: @#@"cancelButtonText"@#@ @source: @#@"Cancel"@#@ diff --git a/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/en-AU.js b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/en-AU.js new file mode 100644 index 0000000000..20084b08b2 --- /dev/null +++ b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/en-AU.js @@ -0,0 +1,17 @@ +export default { + participants: "Participants", + host: "(Host)", + removeParticipant: "Remove participant", + removeTitle: "Remove participant", + removeDescription: "Remove {name} from this conference?", + confirmButtonText: "Remove", + cancelButtonText: "Cancel" +}; + +// @key: @#@"participants"@#@ @source: @#@"Participants"@#@ +// @key: @#@"host"@#@ @source: @#@"(Host)"@#@ +// @key: @#@"removeParticipant"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeTitle"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeDescription"@#@ @source: @#@"Remove {name} from this conference?"@#@ +// @key: @#@"confirmButtonText"@#@ @source: @#@"Remove"@#@ +// @key: @#@"cancelButtonText"@#@ @source: @#@"Cancel"@#@ diff --git a/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/en-GB.js b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/en-GB.js new file mode 100644 index 0000000000..20084b08b2 --- /dev/null +++ b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/en-GB.js @@ -0,0 +1,17 @@ +export default { + participants: "Participants", + host: "(Host)", + removeParticipant: "Remove participant", + removeTitle: "Remove participant", + removeDescription: "Remove {name} from this conference?", + confirmButtonText: "Remove", + cancelButtonText: "Cancel" +}; + +// @key: @#@"participants"@#@ @source: @#@"Participants"@#@ +// @key: @#@"host"@#@ @source: @#@"(Host)"@#@ +// @key: @#@"removeParticipant"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeTitle"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeDescription"@#@ @source: @#@"Remove {name} from this conference?"@#@ +// @key: @#@"confirmButtonText"@#@ @source: @#@"Remove"@#@ +// @key: @#@"cancelButtonText"@#@ @source: @#@"Cancel"@#@ diff --git a/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/en-US.ts b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/en-US.ts new file mode 100644 index 0000000000..d7cf1096da --- /dev/null +++ b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/en-US.ts @@ -0,0 +1,9 @@ +export default { + participants: 'Participants', + host: '(Host)', + removeParticipant: 'Remove participant', + removeTitle: 'Remove participant', + removeDescription: 'Remove {name} from this conference?', + confirmButtonText: 'Remove', + cancelButtonText: 'Cancel', +} as const; diff --git a/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/es-419.js b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/es-419.js new file mode 100644 index 0000000000..7c89767d5c --- /dev/null +++ b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/es-419.js @@ -0,0 +1,17 @@ +export default { + participants: "Participantes", + host: "(anfitrión)", + removeParticipant: "Eliminar participante", + removeTitle: "Eliminar participante", + removeDescription: "¿Desea quitar a {name} de esta conferencia?", + confirmButtonText: "Eliminar", + cancelButtonText: "Cancelar" +}; + +// @key: @#@"participants"@#@ @source: @#@"Participants"@#@ +// @key: @#@"host"@#@ @source: @#@"(Host)"@#@ +// @key: @#@"removeParticipant"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeTitle"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeDescription"@#@ @source: @#@"Remove {name} from this conference?"@#@ +// @key: @#@"confirmButtonText"@#@ @source: @#@"Remove"@#@ +// @key: @#@"cancelButtonText"@#@ @source: @#@"Cancel"@#@ diff --git a/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/es-ES.js b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/es-ES.js new file mode 100644 index 0000000000..25c3f55033 --- /dev/null +++ b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/es-ES.js @@ -0,0 +1,17 @@ +export default { + participants: "Participantes", + host: "(Host)", + removeParticipant: "Eliminar participante", + removeTitle: "Eliminar participante", + removeDescription: "¿Eliminar a {name} de esta conferencia?", + confirmButtonText: "Eliminar", + cancelButtonText: "Cancelar" +}; + +// @key: @#@"participants"@#@ @source: @#@"Participants"@#@ +// @key: @#@"host"@#@ @source: @#@"(Host)"@#@ +// @key: @#@"removeParticipant"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeTitle"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeDescription"@#@ @source: @#@"Remove {name} from this conference?"@#@ +// @key: @#@"confirmButtonText"@#@ @source: @#@"Remove"@#@ +// @key: @#@"cancelButtonText"@#@ @source: @#@"Cancel"@#@ diff --git a/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/fi-FI.js b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/fi-FI.js new file mode 100644 index 0000000000..1226d6ed17 --- /dev/null +++ b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/fi-FI.js @@ -0,0 +1,17 @@ +export default { + participants: "Osallistujat", + host: "(ylläpitäjä)", + removeParticipant: "Poista osallistuja", + removeTitle: "Poista osallistuja", + removeDescription: "Poistetaanko {name} tästä neuvottelusta?", + confirmButtonText: "Poista", + cancelButtonText: "Peruuta" +}; + +// @key: @#@"participants"@#@ @source: @#@"Participants"@#@ +// @key: @#@"host"@#@ @source: @#@"(Host)"@#@ +// @key: @#@"removeParticipant"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeTitle"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeDescription"@#@ @source: @#@"Remove {name} from this conference?"@#@ +// @key: @#@"confirmButtonText"@#@ @source: @#@"Remove"@#@ +// @key: @#@"cancelButtonText"@#@ @source: @#@"Cancel"@#@ diff --git a/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/fr-CA.js b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/fr-CA.js new file mode 100644 index 0000000000..266f2cb3de --- /dev/null +++ b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/fr-CA.js @@ -0,0 +1,17 @@ +export default { + participants: "Participants", + host: "(Animateur)", + removeParticipant: "Retirer le participant", + removeTitle: "Retirer le participant", + removeDescription: "Retirer {name} de cette conférence?", + confirmButtonText: "Retirer", + cancelButtonText: "Annuler" +}; + +// @key: @#@"participants"@#@ @source: @#@"Participants"@#@ +// @key: @#@"host"@#@ @source: @#@"(Host)"@#@ +// @key: @#@"removeParticipant"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeTitle"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeDescription"@#@ @source: @#@"Remove {name} from this conference?"@#@ +// @key: @#@"confirmButtonText"@#@ @source: @#@"Remove"@#@ +// @key: @#@"cancelButtonText"@#@ @source: @#@"Cancel"@#@ diff --git a/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/fr-FR.js b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/fr-FR.js new file mode 100644 index 0000000000..b5b8d4adfb --- /dev/null +++ b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/fr-FR.js @@ -0,0 +1,17 @@ +export default { + participants: "Participants", + host: "(Hôte)", + removeParticipant: "Retirer un participant", + removeTitle: "Retirer un participant", + removeDescription: "Retirer {name} de cette conférence ?", + confirmButtonText: "Supprimer", + cancelButtonText: "Annuler" +}; + +// @key: @#@"participants"@#@ @source: @#@"Participants"@#@ +// @key: @#@"host"@#@ @source: @#@"(Host)"@#@ +// @key: @#@"removeParticipant"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeTitle"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeDescription"@#@ @source: @#@"Remove {name} from this conference?"@#@ +// @key: @#@"confirmButtonText"@#@ @source: @#@"Remove"@#@ +// @key: @#@"cancelButtonText"@#@ @source: @#@"Cancel"@#@ diff --git a/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/index.ts b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/index.ts new file mode 100644 index 0000000000..3a13838340 --- /dev/null +++ b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/index.ts @@ -0,0 +1,14 @@ +import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; + +import type enUS from './en-US'; +// @ts-expect-error +import loadLocale from './loadLocale'; + +const i18n = new I18n(loadLocale); + +export const t = getTranslateFn(i18n); + +export type I18nKey = keyof typeof enUS; + +export default i18n; diff --git a/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/it-IT.js b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/it-IT.js new file mode 100644 index 0000000000..aa1bec1c32 --- /dev/null +++ b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/it-IT.js @@ -0,0 +1,17 @@ +export default { + participants: "Partecipanti", + host: "(Host)", + removeParticipant: "Rimuovi partecipante", + removeTitle: "Rimuovi partecipante", + removeDescription: "Rimuovere {name} da questa conferenza?", + confirmButtonText: "Rimuovi", + cancelButtonText: "Annulla" +}; + +// @key: @#@"participants"@#@ @source: @#@"Participants"@#@ +// @key: @#@"host"@#@ @source: @#@"(Host)"@#@ +// @key: @#@"removeParticipant"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeTitle"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeDescription"@#@ @source: @#@"Remove {name} from this conference?"@#@ +// @key: @#@"confirmButtonText"@#@ @source: @#@"Remove"@#@ +// @key: @#@"cancelButtonText"@#@ @source: @#@"Cancel"@#@ diff --git a/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/ja-JP.js b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/ja-JP.js new file mode 100644 index 0000000000..304dc381a6 --- /dev/null +++ b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/ja-JP.js @@ -0,0 +1,17 @@ +export default { + participants: "参加者", + host: "(ホスト)", + removeParticipant: "参加者を削除", + removeTitle: "参加者を削除", + removeDescription: "この会議から{name}を削除しますか?", + confirmButtonText: "削除", + cancelButtonText: "キャンセル" +}; + +// @key: @#@"participants"@#@ @source: @#@"Participants"@#@ +// @key: @#@"host"@#@ @source: @#@"(Host)"@#@ +// @key: @#@"removeParticipant"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeTitle"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeDescription"@#@ @source: @#@"Remove {name} from this conference?"@#@ +// @key: @#@"confirmButtonText"@#@ @source: @#@"Remove"@#@ +// @key: @#@"cancelButtonText"@#@ @source: @#@"Cancel"@#@ diff --git a/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/ko-KR.js b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/ko-KR.js new file mode 100644 index 0000000000..2ee01938db --- /dev/null +++ b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/ko-KR.js @@ -0,0 +1,17 @@ +export default { + participants: "참가자", + host: "(호스트)", + removeParticipant: "참가자 제거", + removeTitle: "참가자 제거", + removeDescription: "이 전화 회의에서 {name}님을 제거하시겠습니까?", + confirmButtonText: "제거", + cancelButtonText: "취소" +}; + +// @key: @#@"participants"@#@ @source: @#@"Participants"@#@ +// @key: @#@"host"@#@ @source: @#@"(Host)"@#@ +// @key: @#@"removeParticipant"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeTitle"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeDescription"@#@ @source: @#@"Remove {name} from this conference?"@#@ +// @key: @#@"confirmButtonText"@#@ @source: @#@"Remove"@#@ +// @key: @#@"cancelButtonText"@#@ @source: @#@"Cancel"@#@ diff --git a/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/loadLocale.ts b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/loadLocale.ts new file mode 100644 index 0000000000..12b11cfa2e --- /dev/null +++ b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/loadLocale.ts @@ -0,0 +1 @@ +/* loadLocale */ diff --git a/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/nl-NL.js b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/nl-NL.js new file mode 100644 index 0000000000..0b629e4203 --- /dev/null +++ b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/nl-NL.js @@ -0,0 +1,17 @@ +export default { + participants: "Deelnemers", + host: "(Host)", + removeParticipant: "Deelnemer verwijderen", + removeTitle: "Deelnemer verwijderen", + removeDescription: "{name} uit deze conference verwijderen?", + confirmButtonText: "Verwijderen", + cancelButtonText: "Annuleren" +}; + +// @key: @#@"participants"@#@ @source: @#@"Participants"@#@ +// @key: @#@"host"@#@ @source: @#@"(Host)"@#@ +// @key: @#@"removeParticipant"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeTitle"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeDescription"@#@ @source: @#@"Remove {name} from this conference?"@#@ +// @key: @#@"confirmButtonText"@#@ @source: @#@"Remove"@#@ +// @key: @#@"cancelButtonText"@#@ @source: @#@"Cancel"@#@ diff --git a/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/pt-BR.js b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/pt-BR.js new file mode 100644 index 0000000000..562beb5f00 --- /dev/null +++ b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/pt-BR.js @@ -0,0 +1,17 @@ +export default { + participants: "Participantes", + host: "(Organizador)", + removeParticipant: "Remover participante", + removeTitle: "Remover participante", + removeDescription: "Remover {name} desta conferência?", + confirmButtonText: "Remover", + cancelButtonText: "Cancelar" +}; + +// @key: @#@"participants"@#@ @source: @#@"Participants"@#@ +// @key: @#@"host"@#@ @source: @#@"(Host)"@#@ +// @key: @#@"removeParticipant"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeTitle"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeDescription"@#@ @source: @#@"Remove {name} from this conference?"@#@ +// @key: @#@"confirmButtonText"@#@ @source: @#@"Remove"@#@ +// @key: @#@"cancelButtonText"@#@ @source: @#@"Cancel"@#@ diff --git a/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/pt-PT.js b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/pt-PT.js new file mode 100644 index 0000000000..a655803cf1 --- /dev/null +++ b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/pt-PT.js @@ -0,0 +1,17 @@ +export default { + participants: "Participantes", + host: "(Anfitrião)", + removeParticipant: "Remover participante", + removeTitle: "Remover participante", + removeDescription: "Remover {name} desta conferência?", + confirmButtonText: "Remover", + cancelButtonText: "Cancelar" +}; + +// @key: @#@"participants"@#@ @source: @#@"Participants"@#@ +// @key: @#@"host"@#@ @source: @#@"(Host)"@#@ +// @key: @#@"removeParticipant"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeTitle"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeDescription"@#@ @source: @#@"Remove {name} from this conference?"@#@ +// @key: @#@"confirmButtonText"@#@ @source: @#@"Remove"@#@ +// @key: @#@"cancelButtonText"@#@ @source: @#@"Cancel"@#@ diff --git a/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/zh-CN.js b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/zh-CN.js new file mode 100644 index 0000000000..6803ba9856 --- /dev/null +++ b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/zh-CN.js @@ -0,0 +1,17 @@ +export default { + participants: "参与者", + host: "(主持人)", + removeParticipant: "移除参与者", + removeTitle: "移除参与者", + removeDescription: "是否将 {name} 从此会议中移除?", + confirmButtonText: "移除", + cancelButtonText: "取消" +}; + +// @key: @#@"participants"@#@ @source: @#@"Participants"@#@ +// @key: @#@"host"@#@ @source: @#@"(Host)"@#@ +// @key: @#@"removeParticipant"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeTitle"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeDescription"@#@ @source: @#@"Remove {name} from this conference?"@#@ +// @key: @#@"confirmButtonText"@#@ @source: @#@"Remove"@#@ +// @key: @#@"cancelButtonText"@#@ @source: @#@"Cancel"@#@ diff --git a/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/zh-HK.js b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/zh-HK.js new file mode 100644 index 0000000000..76e562ba23 --- /dev/null +++ b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/zh-HK.js @@ -0,0 +1,17 @@ +export default { + participants: "參與者", + host: "(主持人)", + removeParticipant: "移除參與者", + removeTitle: "移除參與者", + removeDescription: "是否從此會議中移除 {name}?", + confirmButtonText: "移除", + cancelButtonText: "取消" +}; + +// @key: @#@"participants"@#@ @source: @#@"Participants"@#@ +// @key: @#@"host"@#@ @source: @#@"(Host)"@#@ +// @key: @#@"removeParticipant"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeTitle"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeDescription"@#@ @source: @#@"Remove {name} from this conference?"@#@ +// @key: @#@"confirmButtonText"@#@ @source: @#@"Remove"@#@ +// @key: @#@"cancelButtonText"@#@ @source: @#@"Cancel"@#@ diff --git a/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/zh-TW.js b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/zh-TW.js new file mode 100644 index 0000000000..76e562ba23 --- /dev/null +++ b/packages/ringcentral-widgets/components/ConferenceCallParticipants/i18n/zh-TW.js @@ -0,0 +1,17 @@ +export default { + participants: "參與者", + host: "(主持人)", + removeParticipant: "移除參與者", + removeTitle: "移除參與者", + removeDescription: "是否從此會議中移除 {name}?", + confirmButtonText: "移除", + cancelButtonText: "取消" +}; + +// @key: @#@"participants"@#@ @source: @#@"Participants"@#@ +// @key: @#@"host"@#@ @source: @#@"(Host)"@#@ +// @key: @#@"removeParticipant"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeTitle"@#@ @source: @#@"Remove participant"@#@ +// @key: @#@"removeDescription"@#@ @source: @#@"Remove {name} from this conference?"@#@ +// @key: @#@"confirmButtonText"@#@ @source: @#@"Remove"@#@ +// @key: @#@"cancelButtonText"@#@ @source: @#@"Cancel"@#@ diff --git a/packages/ringcentral-widgets/components/ConferenceCallParticipants/index.ts b/packages/ringcentral-widgets/components/ConferenceCallParticipants/index.ts new file mode 100644 index 0000000000..29aea4c1a4 --- /dev/null +++ b/packages/ringcentral-widgets/components/ConferenceCallParticipants/index.ts @@ -0,0 +1,2 @@ +export * from './ConferenceCallParticipants'; +export * from './ConferenceCallParticipants.interface'; diff --git a/packages/ringcentral-widgets/components/ConferenceDialerPanel/ConferenceDialerPanel.tsx b/packages/ringcentral-widgets/components/ConferenceDialerPanel/ConferenceDialerPanel.tsx index 0dc4791704..f87bf12fd4 100644 --- a/packages/ringcentral-widgets/components/ConferenceDialerPanel/ConferenceDialerPanel.tsx +++ b/packages/ringcentral-widgets/components/ConferenceDialerPanel/ConferenceDialerPanel.tsx @@ -5,6 +5,7 @@ import BackButton from '../BackButton'; import BackHeader from '../BackHeader'; import type { DialerPanelProps } from '../DialerPanel'; import { DialerPanel } from '../DialerPanel'; + import i18n from './i18n'; type ConferenceDialerPanelProps = { diff --git a/packages/ringcentral-widgets/components/ConferenceDialerPanel/i18n/en-US.ts b/packages/ringcentral-widgets/components/ConferenceDialerPanel/i18n/en-US.ts index a0add83bba..3eefe4c733 100644 --- a/packages/ringcentral-widgets/components/ConferenceDialerPanel/i18n/en-US.ts +++ b/packages/ringcentral-widgets/components/ConferenceDialerPanel/i18n/en-US.ts @@ -1,3 +1,3 @@ export default { activeCall: 'Active Call', -}; +} as const; diff --git a/packages/ringcentral-widgets/components/ConferenceDialerPanel/i18n/index.ts b/packages/ringcentral-widgets/components/ConferenceDialerPanel/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/ringcentral-widgets/components/ConferenceDialerPanel/i18n/index.ts +++ b/packages/ringcentral-widgets/components/ConferenceDialerPanel/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/ringcentral-widgets/components/ConferenceParticipantContainer/ConferenceParticipantContainer.tsx b/packages/ringcentral-widgets/components/ConferenceParticipantContainer/ConferenceParticipantContainer.tsx index 451e54b9de..d658f7621c 100644 --- a/packages/ringcentral-widgets/components/ConferenceParticipantContainer/ConferenceParticipantContainer.tsx +++ b/packages/ringcentral-widgets/components/ConferenceParticipantContainer/ConferenceParticipantContainer.tsx @@ -1,6 +1,5 @@ -import React, { Component } from 'react'; - import { sleep } from '@ringcentral-integration/commons/utils'; +import React, { Component } from 'react'; import ConferenceParticipantPanel from '../ConferenceParticipantPanel'; diff --git a/packages/ringcentral-widgets/components/ConferenceParticipantPanel/ConfirmRemoveModal.tsx b/packages/ringcentral-widgets/components/ConferenceParticipantPanel/ConfirmRemoveModal.tsx index 7afc4c8325..79a1e70b8d 100644 --- a/packages/ringcentral-widgets/components/ConferenceParticipantPanel/ConfirmRemoveModal.tsx +++ b/packages/ringcentral-widgets/components/ConferenceParticipantPanel/ConfirmRemoveModal.tsx @@ -1,8 +1,8 @@ -import React from 'react'; - import calleeTypes from '@ringcentral-integration/commons/enums/calleeTypes'; +import React from 'react'; import Modal from '../Modal'; + import i18n from './i18n'; import styles from './styles.scss'; @@ -13,7 +13,7 @@ type ConfirmRemoveModalProps = { onRemove?: (...args: any[]) => any; detail?: object; }; -const ConfirmRemoveModal: React.SFC = ({ +const ConfirmRemoveModal: React.FC = ({ currentLocale, show, onRemove, diff --git a/packages/ringcentral-widgets/components/ConferenceParticipantPanel/ParticipantItem.tsx b/packages/ringcentral-widgets/components/ConferenceParticipantPanel/ParticipantItem.tsx index 4838015b3d..c01bcc2323 100644 --- a/packages/ringcentral-widgets/components/ConferenceParticipantPanel/ParticipantItem.tsx +++ b/packages/ringcentral-widgets/components/ConferenceParticipantPanel/ParticipantItem.tsx @@ -4,6 +4,7 @@ import EndIcon from '../../assets/images/End.svg'; import CallAvatar from '../CallAvatar'; import CircleButton from '../CircleButton'; import MediaObject from '../MediaObject'; + import i18n from './i18n'; import styles from './styles.scss'; @@ -13,7 +14,7 @@ type ParticipantItemProps = { onRemove?: (...args: any[]) => any; currentLocale: string; }; -const ParticipantItem: React.SFC = ({ +const ParticipantItem: React.FC = ({ detail, avatarUrl, onRemove, diff --git a/packages/ringcentral-widgets/components/ConferenceParticipantPanel/i18n/en-US.ts b/packages/ringcentral-widgets/components/ConferenceParticipantPanel/i18n/en-US.ts index 3cd19bc338..e9066307ee 100644 --- a/packages/ringcentral-widgets/components/ConferenceParticipantPanel/i18n/en-US.ts +++ b/packages/ringcentral-widgets/components/ConferenceParticipantPanel/i18n/en-US.ts @@ -8,4 +8,4 @@ export default { participant: 'Participant', conferenceCall: 'Conference Call', unknownNumber: 'Anonymous', -}; +} as const; diff --git a/packages/ringcentral-widgets/components/ConferenceParticipantPanel/i18n/es-419.ts b/packages/ringcentral-widgets/components/ConferenceParticipantPanel/i18n/es-419.ts index 1ff3ba1dce..190076c07d 100644 --- a/packages/ringcentral-widgets/components/ConferenceParticipantPanel/i18n/es-419.ts +++ b/packages/ringcentral-widgets/components/ConferenceParticipantPanel/i18n/es-419.ts @@ -1,6 +1,6 @@ export default { - removeParticipant: "Quitar participante", - confirmStr1: "¿Está seguro de que desea quitarlo", + removeParticipant: "Eliminar participante", + confirmStr1: "¿Confirma que desea quitarlo", confirmStr2: "de la llamada?", remove: "Eliminar", cancel: "Cancelar", diff --git a/packages/ringcentral-widgets/components/ConferenceParticipantPanel/i18n/fi-FI.ts b/packages/ringcentral-widgets/components/ConferenceParticipantPanel/i18n/fi-FI.ts index d5d06cf945..aafb695470 100644 --- a/packages/ringcentral-widgets/components/ConferenceParticipantPanel/i18n/fi-FI.ts +++ b/packages/ringcentral-widgets/components/ConferenceParticipantPanel/i18n/fi-FI.ts @@ -4,7 +4,7 @@ export default { confirmStr2: "tästä puhelusta?", remove: "Poista", cancel: "Peruuta", - participants: "osallistujaa", + participants: "Osallistujat", participant: "osallistuja", conferenceCall: "Neuvottelupuhelu", unknownNumber: "Nimetön" diff --git a/packages/ringcentral-widgets/components/ConferenceParticipantPanel/i18n/index.ts b/packages/ringcentral-widgets/components/ConferenceParticipantPanel/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/ringcentral-widgets/components/ConferenceParticipantPanel/i18n/index.ts +++ b/packages/ringcentral-widgets/components/ConferenceParticipantPanel/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/ringcentral-widgets/components/ConferenceParticipantPanel/i18n/it-IT.ts b/packages/ringcentral-widgets/components/ConferenceParticipantPanel/i18n/it-IT.ts index 3cfe896d4b..712887eacc 100644 --- a/packages/ringcentral-widgets/components/ConferenceParticipantPanel/i18n/it-IT.ts +++ b/packages/ringcentral-widgets/components/ConferenceParticipantPanel/i18n/it-IT.ts @@ -7,7 +7,7 @@ export default { participants: "Partecipanti", participant: "Partecipante", conferenceCall: "Conferenza telefonica", - unknownNumber: "Anonimo" + unknownNumber: "Numero anonimo" }; // @key: @#@"removeParticipant"@#@ @source: @#@"Remove Participant"@#@ diff --git a/packages/ringcentral-widgets/components/ConferenceParticipantPanel/index.tsx b/packages/ringcentral-widgets/components/ConferenceParticipantPanel/index.tsx index 757b17c75b..51d4fd7de2 100644 --- a/packages/ringcentral-widgets/components/ConferenceParticipantPanel/index.tsx +++ b/packages/ringcentral-widgets/components/ConferenceParticipantPanel/index.tsx @@ -1,12 +1,12 @@ -import React, { Component } from 'react'; - import calleeTypes from '@ringcentral-integration/commons/enums/calleeTypes'; +import React, { Component } from 'react'; import BackButton from '../BackButton'; import BackHeader from '../BackHeader'; + import ConfirmRemoveModal from './ConfirmRemoveModal'; -import i18n from './i18n'; import ParticipantItem from './ParticipantItem'; +import i18n from './i18n'; import styles from './styles.scss'; type ParticipantsContainerProps = { @@ -42,10 +42,9 @@ class ParticipantsContainer extends Component< } formatPrticipants(props = this.props) { const { participants, formatPhone } = props; - participants.map((participant) => { + participants.forEach((participant) => { // @ts-expect-error TS(2339): Property 'partyNumber' does not exist on type 'obj... Remove this comment to see the full error message participant.partyNumber = formatPhone(participant.partyNumber); - return participant; }); } onRemoveBtnClick(participant: any) { diff --git a/packages/ringcentral-widgets/components/ConfirmMergeModal/i18n/en-US.ts b/packages/ringcentral-widgets/components/ConfirmMergeModal/i18n/en-US.ts index 2e5942fb4a..31a95931b0 100644 --- a/packages/ringcentral-widgets/components/ConfirmMergeModal/i18n/en-US.ts +++ b/packages/ringcentral-widgets/components/ConfirmMergeModal/i18n/en-US.ts @@ -3,4 +3,4 @@ export default { confirmMergeToConference: 'Add to conference call?', mergeToConference: 'Merge', conferenceCall: 'Conference Call', -}; +} as const; diff --git a/packages/ringcentral-widgets/components/ConfirmMergeModal/i18n/index.ts b/packages/ringcentral-widgets/components/ConfirmMergeModal/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/ringcentral-widgets/components/ConfirmMergeModal/i18n/index.ts +++ b/packages/ringcentral-widgets/components/ConfirmMergeModal/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/ringcentral-widgets/components/ConfirmMergeModal/i18n/ja-JP.ts b/packages/ringcentral-widgets/components/ConfirmMergeModal/i18n/ja-JP.ts index 328df62828..512ff50d3f 100644 --- a/packages/ringcentral-widgets/components/ConfirmMergeModal/i18n/ja-JP.ts +++ b/packages/ringcentral-widgets/components/ConfirmMergeModal/i18n/ja-JP.ts @@ -1,7 +1,7 @@ export default { confirmation: "確認", confirmMergeToConference: "電話会議に追加しますか?", - mergeToConference: "統合", + mergeToConference: "マージ", conferenceCall: "電話会議" }; diff --git a/packages/ringcentral-widgets/components/ConfirmMergeModal/index.tsx b/packages/ringcentral-widgets/components/ConfirmMergeModal/index.tsx index 3a0e4a2039..9884514e34 100644 --- a/packages/ringcentral-widgets/components/ConfirmMergeModal/index.tsx +++ b/packages/ringcentral-widgets/components/ConfirmMergeModal/index.tsx @@ -4,6 +4,7 @@ import MergeIntoConferenceIcon from '../../assets/images/MergeIntoConferenceIcon import CallAvatar from '../CallAvatar'; import CircleButton from '../CircleButton'; import Modal from '../Modal'; + import i18n from './i18n'; import styles from './styles.scss'; @@ -14,7 +15,7 @@ type ConfirmMergeModalProps = { onCancel?: (...args: any[]) => any; partyProfiles?: object[]; }; -const ConfirmMergeModal: React.SFC = ({ +const ConfirmMergeModal: React.FC = ({ currentLocale, show, onMerge, diff --git a/packages/ringcentral-widgets/components/ConnectivityBadge/i18n/en-US.ts b/packages/ringcentral-widgets/components/ConnectivityBadge/i18n/en-US.ts index 04986bf6ec..e0bbb06367 100644 --- a/packages/ringcentral-widgets/components/ConnectivityBadge/i18n/en-US.ts +++ b/packages/ringcentral-widgets/components/ConnectivityBadge/i18n/en-US.ts @@ -6,4 +6,4 @@ export default { [connectivityTypes.voipOnly]: 'VoIP Only', [connectivityTypes.survival]: 'Limited Mode', [connectivityTypes.connecting]: 'Connecting', -}; +} as const; diff --git a/packages/ringcentral-widgets/components/ConnectivityBadge/i18n/index.ts b/packages/ringcentral-widgets/components/ConnectivityBadge/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/ringcentral-widgets/components/ConnectivityBadge/i18n/index.ts +++ b/packages/ringcentral-widgets/components/ConnectivityBadge/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/ringcentral-widgets/components/ConnectivityBadge/index.tsx b/packages/ringcentral-widgets/components/ConnectivityBadge/index.tsx index 3910048873..c6d4eb7145 100644 --- a/packages/ringcentral-widgets/components/ConnectivityBadge/index.tsx +++ b/packages/ringcentral-widgets/components/ConnectivityBadge/index.tsx @@ -1,13 +1,13 @@ +import clsx from 'clsx'; import type { FunctionComponent } from 'react'; import React from 'react'; -import classnames from 'classnames'; - import OvalLoading from '../../assets/images/OvalLoading.svg'; import RetryIcon from '../../assets/images/RetryIcon.svg'; import { connectivityTypes } from '../../modules/ConnectivityManager'; import Badge from '../Badge'; import Draggable from '../Draggable'; + import i18n from './i18n'; import styles from './styles.scss'; @@ -38,7 +38,8 @@ const ConnectivityBadge: FunctionComponent = ({ if (isWebphoneConnecting) { view = ( {i18n.getString(connectivityTypes.connecting, currentLocale)} @@ -48,7 +49,8 @@ const ConnectivityBadge: FunctionComponent = ({ } else { view = ( {i18n.getString(mode, currentLocale)} diff --git a/packages/ringcentral-widgets/components/ContactDetails/ContactDetails.interface.ts b/packages/ringcentral-widgets/components/ContactDetails/ContactDetails.interface.ts index 8eb6c52019..e09e13b58a 100644 --- a/packages/ringcentral-widgets/components/ContactDetails/ContactDetails.interface.ts +++ b/packages/ringcentral-widgets/components/ContactDetails/ContactDetails.interface.ts @@ -1,7 +1,6 @@ -import type { ReactNode } from 'react'; - import type { PhoneType } from '@ringcentral-integration/commons/enums/phoneTypes'; import type { ContactModel } from '@ringcentral-integration/commons/interfaces/Contact.model'; +import type { ReactNode } from 'react'; export interface onClickMailTo { onClickMailTo?(email: string, contactType: string): any; diff --git a/packages/ringcentral-widgets/components/ContactDetails/ContactDetails.tsx b/packages/ringcentral-widgets/components/ContactDetails/ContactDetails.tsx index 39808a99df..5cca69c879 100644 --- a/packages/ringcentral-widgets/components/ContactDetails/ContactDetails.tsx +++ b/packages/ringcentral-widgets/components/ContactDetails/ContactDetails.tsx @@ -1,13 +1,9 @@ +import type { ContactModel } from '@ringcentral-integration/commons/interfaces/Contact.model'; import type { FunctionComponent } from 'react'; import React from 'react'; -import type { ContactModel } from '@ringcentral-integration/commons/interfaces/Contact.model'; - import type { GetPresenceFn } from '../../react-hooks/usePresence'; -import { CompanyInfo } from './components/CompanyInfo'; -import { Emails } from './components/Emails'; -import { PhoneSection } from './components/PhoneSection'; -import { Profile } from './components/Profile'; + import type { clickToDial, clickToSMS, @@ -15,6 +11,10 @@ import type { onClickMailTo, sourceNodeRenderer, } from './ContactDetails.interface'; +import { CompanyInfo } from './components/CompanyInfo'; +import { Emails } from './components/Emails'; +import { PhoneSection } from './components/PhoneSection'; +import { Profile } from './components/Profile'; import styles from './styles.scss'; interface ContactDetailsProps diff --git a/packages/ringcentral-widgets/components/ContactDetails/components/Avatar.tsx b/packages/ringcentral-widgets/components/ContactDetails/components/Avatar.tsx index 6a947a34bd..5c5b154ddd 100644 --- a/packages/ringcentral-widgets/components/ContactDetails/components/Avatar.tsx +++ b/packages/ringcentral-widgets/components/ContactDetails/components/Avatar.tsx @@ -1,8 +1,7 @@ +import clsx from 'clsx'; import type { FunctionComponent, ReactNode } from 'react'; import React from 'react'; -import classnames from 'classnames'; - import DefaultAvatar from '../../../assets/images/DefaultAvatar.svg'; import PlaceholderImage from '../../PlaceholderImage'; import styles from '../styles.scss'; @@ -20,10 +19,7 @@ export const Avatar: FunctionComponent = ({ inactive, source, }) => { - const imageClassName = classnames( - styles.avatarImage, - inactive && styles.inactive, - ); + const imageClassName = clsx(styles.avatarImage, inactive && styles.inactive); const sourceNode = source ? (
    {source}
    ) : null; diff --git a/packages/ringcentral-widgets/components/ContactDetails/components/CompanyInfo.tsx b/packages/ringcentral-widgets/components/ContactDetails/components/CompanyInfo.tsx index 867f9726eb..d7240fb4e2 100644 --- a/packages/ringcentral-widgets/components/ContactDetails/components/CompanyInfo.tsx +++ b/packages/ringcentral-widgets/components/ContactDetails/components/CompanyInfo.tsx @@ -1,8 +1,7 @@ +import clsx from 'clsx'; import type { FunctionComponent, ReactNode } from 'react'; import React from 'react'; -import classnames from 'classnames'; - import i18n from '../i18n'; import styles from '../styles.scss'; @@ -59,9 +58,7 @@ export const CompanyInfo: FunctionComponent = ({ } if (content.length) { return ( -
    - {content} -
    +
    {content}
    ); } return null; diff --git a/packages/ringcentral-widgets/components/ContactDetails/components/Emails.tsx b/packages/ringcentral-widgets/components/ContactDetails/components/Emails.tsx index 8e2384510b..be59084f2c 100644 --- a/packages/ringcentral-widgets/components/ContactDetails/components/Emails.tsx +++ b/packages/ringcentral-widgets/components/ContactDetails/components/Emails.tsx @@ -1,9 +1,8 @@ +import clsx from 'clsx'; +import { addIndex, map } from 'ramda'; import type { FunctionComponent, ReactNode } from 'react'; import React from 'react'; -import classnames from 'classnames'; -import { addIndex, map } from 'ramda'; - import type { onClickMailTo } from '../ContactDetails.interface'; import i18n from '../i18n'; import styles from '../styles.scss'; @@ -38,7 +37,7 @@ export const Emails: FunctionComponent = ({ ); return (
    diff --git a/packages/ringcentral-widgets/components/ContactDetails/components/PhoneSection.tsx b/packages/ringcentral-widgets/components/ContactDetails/components/PhoneSection.tsx index 8faedafd9b..ec362dc55f 100644 --- a/packages/ringcentral-widgets/components/ContactDetails/components/PhoneSection.tsx +++ b/packages/ringcentral-widgets/components/ContactDetails/components/PhoneSection.tsx @@ -1,15 +1,13 @@ -import type { FunctionComponent } from 'react'; -import React from 'react'; - -import classnames from 'classnames'; -import { keys, map, reduce } from 'ramda'; - import type { PhoneType } from '@ringcentral-integration/commons/enums/phoneTypes'; import type { ContactModel } from '@ringcentral-integration/commons/interfaces/Contact.model'; import { filterByPhoneTypes, sortByPhoneTypes, } from '@ringcentral-integration/commons/lib/phoneTypeHelper'; +import clsx from 'clsx'; +import { keys, map, reduce } from 'ramda'; +import type { FunctionComponent } from 'react'; +import React from 'react'; import dynamicsFont from '../../../assets/DynamicsFont/DynamicsFont.scss'; import type { @@ -79,7 +77,7 @@ const PhoneListItem: FunctionComponent = ({ return (
  • -
    +
    = ({ ) : null} -
    - {children} -
    +
    {children}
    {footer}
    ); diff --git a/packages/ringcentral-widgets/components/Draggable/index.tsx b/packages/ringcentral-widgets/components/Draggable/index.tsx index 12baec03c5..5be1ffb2d4 100644 --- a/packages/ringcentral-widgets/components/Draggable/index.tsx +++ b/packages/ringcentral-widgets/components/Draggable/index.tsx @@ -1,7 +1,6 @@ +import clsx from 'clsx'; import React, { Component } from 'react'; -import classnames from 'classnames'; - import styles from './styles.scss'; type DraggableProps = { @@ -152,7 +151,7 @@ class Draggable extends Component { this.draggableDom = draggableDom; }} style={style} - className={classnames(styles.root, className)} + className={clsx(styles.root, className)} onClick={this._onClick} > {children} diff --git a/packages/ringcentral-widgets/components/DropdownNavigationItem/index.tsx b/packages/ringcentral-widgets/components/DropdownNavigationItem/index.tsx index 1855b4071a..200381a826 100644 --- a/packages/ringcentral-widgets/components/DropdownNavigationItem/index.tsx +++ b/packages/ringcentral-widgets/components/DropdownNavigationItem/index.tsx @@ -1,7 +1,6 @@ +import clsx from 'clsx'; import React from 'react'; -import classnames from 'classnames'; - import styles from './styles.scss'; type DropdownNavigationItemProps = { @@ -17,7 +16,7 @@ type DropdownNavigationItemProps = { dataSign?: string; }; -const DropdownNavigationItem: React.SFC = ({ +const DropdownNavigationItem: React.FC = ({ icon, activeIcon, active, @@ -43,13 +42,13 @@ const DropdownNavigationItem: React.SFC = ({ title={title || label} data-sign={dataSign} onClick={onClick} - className={classnames( + className={clsx( styles.root, active && styles.active, isReverseFillIcon && styles.reverseFillIcon, )} > -
    +
    {active ? activeIcon : icon}
    {label}
    diff --git a/packages/ringcentral-widgets/components/DropdownNavigationView/DropdownNavigationView.tsx b/packages/ringcentral-widgets/components/DropdownNavigationView/DropdownNavigationView.tsx index 05572cced7..0278876189 100644 --- a/packages/ringcentral-widgets/components/DropdownNavigationView/DropdownNavigationView.tsx +++ b/packages/ringcentral-widgets/components/DropdownNavigationView/DropdownNavigationView.tsx @@ -1,10 +1,10 @@ +import { useGlobalListener } from '@ringcentral/juno'; import type { FunctionComponent } from 'react'; import React, { useRef } from 'react'; -import { useGlobalListener } from '@ringcentral/juno'; - import DropdownNavigationItem from '../DropdownNavigationItem'; import type { TabPropTypes } from '../NavigationBar'; + import styles from './styles.scss'; export interface DropdownNavigationViewProps { @@ -79,10 +79,11 @@ const DropdownNavigation: FunctionComponent = ({ }; // TODO: that check should move to outside -export const DropdownNavigationView: FunctionComponent = - (props) => { - const { tabs } = props; - return tabs.length ? : null; - }; +export const DropdownNavigationView: FunctionComponent< + DropdownNavigationViewProps +> = (props) => { + const { tabs } = props; + return tabs.length ? : null; +}; export default DropdownNavigationView; diff --git a/packages/ringcentral-widgets/components/DropdownSelect/DropdownSelect.tsx b/packages/ringcentral-widgets/components/DropdownSelect/DropdownSelect.tsx index d585fcaef8..52c4ba10ad 100644 --- a/packages/ringcentral-widgets/components/DropdownSelect/DropdownSelect.tsx +++ b/packages/ringcentral-widgets/components/DropdownSelect/DropdownSelect.tsx @@ -1,7 +1,3 @@ -import React, { Component, createRef } from 'react'; - -import classnames from 'classnames'; - import { ellipsis, flexWidth, @@ -12,8 +8,11 @@ import { spacing, styled, } from '@ringcentral/juno'; +import clsx from 'clsx'; +import React, { Component, createRef } from 'react'; import dynamicsFont from '../../assets/DynamicsFont/DynamicsFont.scss'; + import styles from './styles.scss'; const gutter = spacing(2.5); @@ -396,7 +395,7 @@ class DropdownSelect extends Component< } return ( { this.dropdownMenu = ref; }} @@ -412,7 +411,7 @@ class DropdownSelect extends Component< data-sign="selectMenuItem" key={currentValue || idx} selected={selected} - className={classnames( + className={clsx( // @ts-expect-error TS(2538): Type 'undefined' cannot be used as an index type. styles[dropdownAlign], ellipsis && styles.ellipsis, @@ -425,7 +424,7 @@ class DropdownSelect extends Component< } }} > - {display} + {display} ); })} @@ -439,7 +438,7 @@ class DropdownSelect extends Component< return ( ) : null; - const currentIconClassName = classnames( + const currentIconClassName = clsx( styles.icon, open ? styles.iconUp : null, iconClassName, ); - const containerClassName = classnames( + const containerClassName = clsx( styles.root, className, disabled ? styles.disabled : null, open ? styles.open : null, noPadding ? styles.noPadding : null, ); - const buttonClassName = classnames( + const buttonClassName = clsx( styles.button, disabled ? styles.disabled : null, ); @@ -500,7 +499,7 @@ class DropdownSelect extends Component<
    { if (reference) reference(ref); this.wrapper = ref; @@ -508,7 +507,7 @@ class DropdownSelect extends Component< >
    @@ -517,7 +516,7 @@ class DropdownSelect extends Component< = ({
    = ({ ) : null} {option.text} diff --git a/packages/ringcentral-widgets/components/DurationCounter/index.tsx b/packages/ringcentral-widgets/components/DurationCounter/index.tsx index 22cc15f23e..1b7eff1759 100644 --- a/packages/ringcentral-widgets/components/DurationCounter/index.tsx +++ b/packages/ringcentral-widgets/components/DurationCounter/index.tsx @@ -1,6 +1,5 @@ -import React, { Component } from 'react'; - import { formatDuration } from '@ringcentral-integration/commons/lib/formatDuration'; +import React, { Component } from 'react'; type DurationCounterProps = { className?: string; @@ -39,7 +38,7 @@ class DurationCounter extends Component< // @ts-expect-error TS(4114): This member must have an 'override' modifier becau... Remove this comment to see the full error message render() { return ( - + {formatDuration(this.state.duration)} ); diff --git a/packages/ringcentral-widgets/components/EntityButton/index.tsx b/packages/ringcentral-widgets/components/EntityButton/index.tsx index e4304cb6e8..983261476d 100644 --- a/packages/ringcentral-widgets/components/EntityButton/index.tsx +++ b/packages/ringcentral-widgets/components/EntityButton/index.tsx @@ -1,10 +1,10 @@ +import clsx from 'clsx'; import React from 'react'; -import classnames from 'classnames'; - import dynamicsFont from '../../assets/DynamicsFont/DynamicsFont.scss'; import { Button } from '../Button'; import Spinner from '../Spinner'; + import styles from './styles.scss'; type EntityButtonProps = { @@ -17,7 +17,7 @@ type EntityButtonProps = { viewEntityTitle?: string; createEntityTitle?: string; }; -const EntityButton: React.SFC = ({ +const EntityButton: React.FC = ({ className, onViewEntity, onCreateEntity, @@ -38,7 +38,7 @@ const EntityButton: React.SFC = ({ const title = hasEntity ? viewEntityTitle : createEntityTitle; return ( - + + } + > + Enable Data Tracking + + + After clicking save, remember to refresh to take effect(all + tabs need to be closed) when you enable that manually, the enable + will take two hours enable, will auto close after{' '} + two hours + + + )} + + +
    ); }; diff --git a/packages/ringcentral-widgets/components/EnvironmentPanel/styles.scss b/packages/ringcentral-widgets/components/EnvironmentPanel/styles.scss index 80f5dce517..8b9373ce22 100644 --- a/packages/ringcentral-widgets/components/EnvironmentPanel/styles.scss +++ b/packages/ringcentral-widgets/components/EnvironmentPanel/styles.scss @@ -11,14 +11,30 @@ left: 0; background-color: #fff; z-index: 30; + flex-direction: column; + display: flex; +} + +.main { + flex: 1 1 auto; + overflow: auto; +} + +.saveButtonLine { + padding-bottom: 20px; + flex: none; } .saveButton { display: block; - margin-top: 20px; @include big-submit-button; &.disabled { @include big-submit-button-disabled; } } + +.comment { + padding: 0 20px 20px; + color: red; +} diff --git a/packages/ringcentral-widgets/components/Eula/Eula.tsx b/packages/ringcentral-widgets/components/Eula/Eula.tsx index 77b474a5d7..a340ab894b 100644 --- a/packages/ringcentral-widgets/components/Eula/Eula.tsx +++ b/packages/ringcentral-widgets/components/Eula/Eula.tsx @@ -1,6 +1,5 @@ -import React from 'react'; - import { RcLink } from '@ringcentral/juno'; +import React from 'react'; import type { EulaProps } from './Eula.interface'; import i18n from './i18n'; diff --git a/packages/ringcentral-widgets/components/Eula/i18n/en-US.ts b/packages/ringcentral-widgets/components/Eula/i18n/en-US.ts index ee981fe0aa..688d3a98cb 100644 --- a/packages/ringcentral-widgets/components/Eula/i18n/en-US.ts +++ b/packages/ringcentral-widgets/components/Eula/i18n/en-US.ts @@ -1,4 +1,4 @@ export default { eula: 'End User License Agreement', eulaAbbr: 'EULA', -}; +} as const; diff --git a/packages/ringcentral-widgets/components/Eula/i18n/index.ts b/packages/ringcentral-widgets/components/Eula/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/ringcentral-widgets/components/Eula/i18n/index.ts +++ b/packages/ringcentral-widgets/components/Eula/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/ringcentral-widgets/components/FeedbackPanel/i18n/en-US.ts b/packages/ringcentral-widgets/components/FeedbackPanel/i18n/en-US.ts index 7f1b0b18f9..dee1fb080b 100644 --- a/packages/ringcentral-widgets/components/FeedbackPanel/i18n/en-US.ts +++ b/packages/ringcentral-widgets/components/FeedbackPanel/i18n/en-US.ts @@ -19,4 +19,4 @@ export default { description: 'Full description', descriptionPlaceHolder: 'Please include as much information as possible', send: 'Send Your Feedback', -}; +} as const; diff --git a/packages/ringcentral-widgets/components/FeedbackPanel/i18n/es-419.ts b/packages/ringcentral-widgets/components/FeedbackPanel/i18n/es-419.ts index a0160c7077..cb1d588ec0 100644 --- a/packages/ringcentral-widgets/components/FeedbackPanel/i18n/es-419.ts +++ b/packages/ringcentral-widgets/components/FeedbackPanel/i18n/es-419.ts @@ -2,7 +2,7 @@ export default { feedbackHeader: "Enviar comentarios", back: "Atrás", revert: "Volver", - instruction: "Sus comentarios tienen mucho valor para nosotros. Si tiene problemas al utilizar la aplicación, quiere solicitar una función o informar de un problema, estaremos encantados de ayudarle.", + instruction: "Sus comentarios tienen mucho valor para nosotros. Si tiene problemas al utilizar la aplicación, quiere solicitar una función o informar de un problema, estaremos encantados de ayudarlo.", fillForm: "Rellene el siguiente formulario y haga clic ", useMailBox: " o utilice directamente su buzón de correo y envíe su solicitud a ", email: "su dirección de correo electrónico", @@ -14,7 +14,7 @@ export default { featureRequest: "Solicitud de funciones", others: "Otros", subject: "Sujeto", - subjectPlaceHolder: "Ayúdenos a saber cómo podemos ayudarle", + subjectPlaceHolder: "Ayúdenos a saber cómo podemos ayudarlo", description: "Descripción detallada", descriptionPlaceHolder: "Incluya la máxima información posible", send: "Envíenos sus comentarios" diff --git a/packages/ringcentral-widgets/components/FeedbackPanel/i18n/es-ES.ts b/packages/ringcentral-widgets/components/FeedbackPanel/i18n/es-ES.ts index 9f84677fa0..9a31c41c9d 100644 --- a/packages/ringcentral-widgets/components/FeedbackPanel/i18n/es-ES.ts +++ b/packages/ringcentral-widgets/components/FeedbackPanel/i18n/es-ES.ts @@ -2,7 +2,7 @@ export default { feedbackHeader: "Enviar comentarios", back: "Atrás", revert: "Volver", - instruction: "Sus comentarios tienen mucho valor para nosotros. Si tiene problemas al utilizar la aplicación, quiere solicitar una función o informar de un problema, estaremos encantados de ayudarle.", + instruction: "Sus comentarios tienen mucho valor para nosotros. Si tiene problemas al utilizar la app, quiere solicitar una función o informar de un problema, estaremos encantados de ayudarle.", fillForm: "Rellene el siguiente formulario y haga clic ", useMailBox: " o utilice directamente su buzón de correo y envíe su solicitud a ", email: "Su dirección de correo electrónico", diff --git a/packages/ringcentral-widgets/components/FeedbackPanel/i18n/index.ts b/packages/ringcentral-widgets/components/FeedbackPanel/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/ringcentral-widgets/components/FeedbackPanel/i18n/index.ts +++ b/packages/ringcentral-widgets/components/FeedbackPanel/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/ringcentral-widgets/components/FeedbackPanel/index.tsx b/packages/ringcentral-widgets/components/FeedbackPanel/index.tsx index 4da9f6d514..bf06c27be9 100644 --- a/packages/ringcentral-widgets/components/FeedbackPanel/index.tsx +++ b/packages/ringcentral-widgets/components/FeedbackPanel/index.tsx @@ -7,6 +7,7 @@ import Select from '../DropdownSelect'; import InputField from '../InputField'; import Panel from '../Panel'; import TextInput from '../TextInput'; + import i18n from './i18n'; import styles from './styles.scss'; diff --git a/packages/ringcentral-widgets/components/FlipPanel/i18n/en-US.ts b/packages/ringcentral-widgets/components/FlipPanel/i18n/en-US.ts index 6b7738e728..a88ba19139 100644 --- a/packages/ringcentral-widgets/components/FlipPanel/i18n/en-US.ts +++ b/packages/ringcentral-widgets/components/FlipPanel/i18n/en-US.ts @@ -2,4 +2,4 @@ export default { flipHeader: 'Flip Call to...', flip: 'Flip', complete: 'Complete Flip', -}; +} as const; diff --git a/packages/ringcentral-widgets/components/FlipPanel/i18n/es-419.ts b/packages/ringcentral-widgets/components/FlipPanel/i18n/es-419.ts index 1d29de713d..ab902921f8 100644 --- a/packages/ringcentral-widgets/components/FlipPanel/i18n/es-419.ts +++ b/packages/ringcentral-widgets/components/FlipPanel/i18n/es-419.ts @@ -1,5 +1,5 @@ export default { - flipHeader: "Voltear llamada a...", + flipHeader: "Voltear llamada a…", flip: "Transferir", complete: "Completar Flip" }; diff --git a/packages/ringcentral-widgets/components/FlipPanel/i18n/index.ts b/packages/ringcentral-widgets/components/FlipPanel/i18n/index.ts index 9a34220736..3a13838340 100644 --- a/packages/ringcentral-widgets/components/FlipPanel/i18n/index.ts +++ b/packages/ringcentral-widgets/components/FlipPanel/i18n/index.ts @@ -1,4 +1,5 @@ import I18n from '@ringcentral-integration/i18n'; +import { getTranslateFn } from '@ringcentral-integration/utils'; import type enUS from './en-US'; // @ts-expect-error @@ -6,7 +7,7 @@ import loadLocale from './loadLocale'; const i18n = new I18n(loadLocale); -export const t = i18n.getString.bind(i18n); +export const t = getTranslateFn(i18n); export type I18nKey = keyof typeof enUS; diff --git a/packages/ringcentral-widgets/components/FlipPanel/index.tsx b/packages/ringcentral-widgets/components/FlipPanel/index.tsx index a8542a0754..62ae7c12ec 100644 --- a/packages/ringcentral-widgets/components/FlipPanel/index.tsx +++ b/packages/ringcentral-widgets/components/FlipPanel/index.tsx @@ -1,13 +1,13 @@ +import clsx from 'clsx'; import React, { Component } from 'react'; -import classnames from 'classnames'; - import EndIcon from '../../assets/images/End.svg'; import FlipIcon from '../../assets/images/Flip.svg'; import BackButton from '../BackButton'; import BackHeader from '../BackHeader'; import CircleButton from '../CircleButton'; import RadioButtonGroup from '../RadioBtnGroup'; + import i18n from './i18n'; import styles from './styles.scss'; @@ -95,7 +95,7 @@ class FlipPanel extends Component { > { > = (props) => { +const Footer: React.FC = (props) => { return ( -