From b08c4592c219347d066de5b91a106a454e7c993b Mon Sep 17 00:00:00 2001 From: William Grant Date: Wed, 17 Jul 2024 17:03:24 +1000 Subject: [PATCH 1/5] feat: added error boundary to component rendering in unit tests, resolve issues SVGElement in JSDOM --- package.json | 1 + ts/test/components/renderComponent.tsx | 13 +++++++++++-- ts/test/test-utils/utils/stubbing.ts | 10 ++++++++++ yarn.lock | 7 +++++++ 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 5bd7a5d26..c5fd3ff69 100644 --- a/package.json +++ b/package.json @@ -114,6 +114,7 @@ "react-contexify": "^6.0.0", "react-dom": "18.3.1", "react-draggable": "^4.4.4", + "react-error-boundary": "^4.0.13", "react-h5-audio-player": "^3.2.0", "react-intersection-observer": "^9.7.0", "react-mentions": "^4.4.9", diff --git a/ts/test/components/renderComponent.tsx b/ts/test/components/renderComponent.tsx index ad8c46ded..35ce3a12d 100644 --- a/ts/test/components/renderComponent.tsx +++ b/ts/test/components/renderComponent.tsx @@ -1,13 +1,22 @@ /* eslint-disable import/no-extraneous-dependencies */ import { render, RenderOptions } from '@testing-library/react'; -import { AnimatePresence } from 'framer-motion'; +import { AnimatePresence, MotionGlobalConfig } from 'framer-motion'; import { ReactElement, ReactNode } from 'react'; import { SessionTheme } from '../../themes/SessionTheme'; +import { ErrorBoundary } from 'react-error-boundary'; const Providers = ({ children }: { children: ReactNode }) => { + MotionGlobalConfig.skipAnimations = false; + return ( - {children} + + {`Failed to render a component!\n\t${JSON.stringify(children)}`}} + > + {children} + + ); }; diff --git a/ts/test/test-utils/utils/stubbing.ts b/ts/test/test-utils/utils/stubbing.ts index 8a5f63a7b..2d61c5eff 100644 --- a/ts/test/test-utils/utils/stubbing.ts +++ b/ts/test/test-utils/utils/stubbing.ts @@ -86,6 +86,16 @@ export function stubWindow(fn: K, value: WindowValue) }; } +/** + * Resolves "SVGElement is undefined error" in motion components by making JSDOM treat SVG elements as regular DOM elements + * @link https://github.com/jsdom/jsdom/issues/2734#issuecomment-569416871 + * */ +export function stubSVGElement() { + if (!globalAny.SVGElement) { + globalAny.SVGElement = globalAny.Element; + } +} + export const enableLogRedirect = false; export const stubWindowLog = () => { diff --git a/yarn.lock b/yarn.lock index 8f476254a..18b2c91f0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6188,6 +6188,13 @@ react-draggable@^4.4.4: clsx "^1.1.1" prop-types "^15.8.1" +react-error-boundary@^4.0.13: + version "4.0.13" + resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-4.0.13.tgz#80386b7b27b1131c5fbb7368b8c0d983354c7947" + integrity sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ== + dependencies: + "@babel/runtime" "^7.12.5" + react-h5-audio-player@^3.2.0: version "3.9.1" resolved "https://registry.yarnpkg.com/react-h5-audio-player/-/react-h5-audio-player-3.9.1.tgz#8a9721fd7a5ff6a9185ce626435207bee1774e83" From 362e360f405fb255c100954e75de34e4f57e4c44 Mon Sep 17 00:00:00 2001 From: yougotwill Date: Mon, 22 Jul 2024 12:01:45 +1000 Subject: [PATCH 2/5] fix: replace chai with react-test-renderer for unit testing components rewrite avatar placeholder test --- package.json | 5 +- ts/test/components/AvatarPlaceHolder_test.tsx | 102 +++++------------- ts/test/components/renderComponent.tsx | 43 ++++++-- ts/test/test-utils/utils/components.ts | 25 ----- yarn.lock | 85 +++++++++++---- 5 files changed, 134 insertions(+), 126 deletions(-) delete mode 100644 ts/test/test-utils/utils/components.ts diff --git a/package.json b/package.json index c5fd3ff69..9e6b5ae35 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "@reduxjs/toolkit": "1.9.7", "@signalapp/better-sqlite3": "^8.4.3", "@types/react-mentions": "^4.1.8", + "@types/react-test-renderer": "^18.3.0", "abort-controller": "3.0.0", "auto-bind": "^4.0.0", "backbone": "1.3.3", @@ -120,6 +121,7 @@ "react-mentions": "^4.4.9", "react-qrcode-logo": "^3.0.0", "react-redux": "8.1.3", + "react-test-renderer": "^18.3.1", "react-toastify": "^6.0.9", "react-use": "^17.5.0", "react-virtualized": "^9.22.4", @@ -140,6 +142,7 @@ "@commitlint/config-conventional": "^17.7.0", "@commitlint/types": "^17.4.4", "@electron/notarize": "^2.1.0", + "@testing-library/jest-dom": "^6.4.6", "@testing-library/react": "^15.0.7", "@testing-library/user-event": "^14.5.2", "@types/backbone": "1.4.2", @@ -149,7 +152,6 @@ "@types/bytebuffer": "^5.0.41", "@types/chai": "4.2.18", "@types/chai-as-promised": "^7.1.2", - "@types/chai-dom": "^1.11.3", "@types/classnames": "2.2.3", "@types/config": "0.0.34", "@types/dompurify": "^2.0.0", @@ -179,7 +181,6 @@ "chai": "^4.3.4", "chai-as-promised": "^7.1.1", "chai-bytes": "^0.1.2", - "chai-dom": "^1.12.0", "cross-env": "^6.0.3", "css-loader": "^6.7.2", "dmg-builder": "23.6.0", diff --git a/ts/test/components/AvatarPlaceHolder_test.tsx b/ts/test/components/AvatarPlaceHolder_test.tsx index 7a012fd58..388bee5a3 100644 --- a/ts/test/components/AvatarPlaceHolder_test.tsx +++ b/ts/test/components/AvatarPlaceHolder_test.tsx @@ -1,16 +1,12 @@ /* eslint-disable import/no-extraneous-dependencies */ -import { cleanup, waitFor } from '@testing-library/react'; -import chai, { expect } from 'chai'; -import chaiDom from 'chai-dom'; +import { cleanup } from '@testing-library/react'; +import { expect } from 'chai'; import Sinon from 'sinon'; import { AvatarSize } from '../../components/avatar/Avatar'; import { AvatarPlaceHolder } from '../../components/avatar/AvatarPlaceHolder/AvatarPlaceHolder'; import { MemberAvatarPlaceHolder } from '../../components/icon/MemberAvatarPlaceHolder'; -import { COLORS } from '../../themes/constants/colors'; import { TestUtils } from '../test-utils'; -import { renderComponent } from './renderComponent'; - -chai.use(chaiDom); +import { areResultsEqual, findByDataTestId, renderComponent } from './renderComponent'; describe('AvatarPlaceHolder', () => { const pubkey = TestUtils.generateFakePubKeyStr(); @@ -35,16 +31,11 @@ describe('AvatarPlaceHolder', () => { /> ); - // calculating the hash and initials needs to be done first - await waitFor(() => { - result.getByText('HW'); - }); - - const el = result.getByTestId('avatar-placeholder'); - expect(el.outerHTML, 'should not be null').to.not.equal(null); - expect(el.outerHTML, 'should not be undefined').to.not.equal(undefined); - expect(el.outerHTML, 'should not be an empty string').to.not.equal(''); - expect(el.tagName, 'should be an svg').to.equal('svg'); + const el = findByDataTestId(result, 'avatar-placeholder'); + expect(el, 'should not be null').to.not.equal(null); + expect(el, 'should not be undefined').to.not.equal(undefined); + expect(el.children, 'should not be an empty string').to.not.equal(''); + expect(el.type, 'should be an svg').to.equal('svg'); result.unmount(); }); it('should render the MemberAvatarPlaceholder if we are loading or there is no hash', async () => { @@ -56,71 +47,32 @@ describe('AvatarPlaceHolder', () => { dataTestId="avatar-placeholder" /> ); - const el = result.getByTestId('avatar-placeholder'); const result2 = renderComponent( ); - const el2 = result2.getByTestId('member-avatar-placeholder'); - // The data test ids are different so we don't use the outerHTML for comparison - expect(el.innerHTML).to.equal(el2.innerHTML); + expect(areResultsEqual(result, result2, true)).to.equal(true); result.unmount(); }); - it('should render the background color using the primary colors in the correct order', async () => { - const testPubkeys = [ - '0541214ef26572066f0535140b1d6d021218299321c6001e2cdcaaa8cd5c9382fc', // green - '0541214ef26572066f0535140b1d6d021218299321c6001e2cdcaaa8cd5c9382fa', // blue - '0541214ef26572066f0535140b1d6d021218299321c6001e2cdcaaa8cd5c9382fd', // yellow - '0541214ef26572066f0535140b1d6d021218299321c6001e2cdcaaa8cd5c9382ff', // pink - '0541214ef26572066f0535140b1d6d021218299321c6001e2cdcaaa8cd5c9382ed', // purple - '0541214ef26572066f0535140b1d6d021218299321c6001e2cdcaaa8cd5c9382f9', // orange - '0541214ef26572066f0535140b1d6d021218299321c6001e2cdcaaa8cd5c9382eb', // red - ]; - - // NOTE we can trust the order of Object.keys and Object.values to be correct since our typescript build target is 'esnext' - const primaryColorKeys = Object.keys(COLORS.PRIMARY); - const primaryColorValues = Object.values(COLORS.PRIMARY); - - async function testBackgroundColor(testPubkey: string, expectedColorValue: string) { - const result = renderComponent( - - ); - - // calculating the hash and initials needs to be done first - await waitFor(() => { - result.getByText('HW'); - }); - - const el = result.getByTestId('avatar-placeholder'); - const circle = el.querySelector('circle'); - const circleColor = circle?.getAttribute('fill'); - expect(circleColor, 'background color should not be null').to.not.equal(null); - expect(circleColor, 'background color should not be undefined').to.not.equal(undefined); - expect(circleColor, 'background color should not be an empty string').to.not.equal(''); - expect( - primaryColorValues.includes(circleColor!), - 'background color should be in COLORS.PRIMARY' - ).to.equal(true); - expect( - circleColor, - `background color should be ${primaryColorKeys[primaryColorValues.indexOf(expectedColorValue)]} (${expectedColorValue}) and not ${primaryColorKeys[primaryColorValues.indexOf(circleColor!)]} (${circleColor}) for testPubkey ${testPubkeys.indexOf(testPubkey)} (${testPubkey})` - ).to.equal(expectedColorValue); - result.unmount(); - } + it('should render the background using a color from our theme', async () => { + const testPubkey = TestUtils.generateFakePubKeyStr(); + const result = renderComponent( + // NOTE we test the pubkey to color generation and ordering with appium. Since we can't access the value of a css variable in the test + + ); - // NOTE this is the standard order of background colors for avatars on each platform - await testBackgroundColor(testPubkeys[0], COLORS.PRIMARY.GREEN); - await testBackgroundColor(testPubkeys[1], COLORS.PRIMARY.BLUE); - await testBackgroundColor(testPubkeys[2], COLORS.PRIMARY.YELLOW); - await testBackgroundColor(testPubkeys[3], COLORS.PRIMARY.PINK); - await testBackgroundColor(testPubkeys[4], COLORS.PRIMARY.PURPLE); - await testBackgroundColor(testPubkeys[5], COLORS.PRIMARY.ORANGE); - await testBackgroundColor(testPubkeys[6], COLORS.PRIMARY.RED); + const el = findByDataTestId(result, 'avatar-placeholder'); + const circle = el.findByType('circle'); + const colorVariable = circle.props.fill; + expect(colorVariable, 'should have a background color if var(--primary-color)').to.equal( + 'var(--primary-color)' + ); + result.unmount(); }); }); diff --git a/ts/test/components/renderComponent.tsx b/ts/test/components/renderComponent.tsx index 35ce3a12d..c11ce694c 100644 --- a/ts/test/components/renderComponent.tsx +++ b/ts/test/components/renderComponent.tsx @@ -1,9 +1,9 @@ -/* eslint-disable import/no-extraneous-dependencies */ -import { render, RenderOptions } from '@testing-library/react'; import { AnimatePresence, MotionGlobalConfig } from 'framer-motion'; +import { isArray, isEqual, unset } from 'lodash'; import { ReactElement, ReactNode } from 'react'; -import { SessionTheme } from '../../themes/SessionTheme'; import { ErrorBoundary } from 'react-error-boundary'; +import TestRenderer from 'react-test-renderer'; +import { SessionTheme } from '../../themes/SessionTheme'; const Providers = ({ children }: { children: ReactNode }) => { MotionGlobalConfig.skipAnimations = false; @@ -21,7 +21,38 @@ const Providers = ({ children }: { children: ReactNode }) => { ); }; -const renderComponent = (ui: ReactElement, options?: Omit) => - render(ui, { wrapper: Providers, ...options }); +function renderComponent(children: ReactElement): TestRenderer.ReactTestRenderer { + return TestRenderer.create({children}); +} + +function getComponentTree( + result: TestRenderer.ReactTestRenderer +): Array { + const trees = result.toTree(); + return !trees ? [] : isArray(trees) ? trees : [trees]; +} + +function findByDataTestId( + renderResult: TestRenderer.ReactTestRenderer, + dataTestId: string +): TestRenderer.ReactTestInstance { + return renderResult.root.findByProps({ 'data-testid': dataTestId }); +} + +function areResultsEqual( + renderResult: TestRenderer.ReactTestRenderer, + renderResult2: TestRenderer.ReactTestRenderer, + ignoreDataTestIds?: boolean +): boolean { + if (ignoreDataTestIds) { + const obj = renderResult.toJSON(); + const obj2 = renderResult2.toJSON(); + unset(obj, "props['data-testid']"); + unset(obj2, "props['data-testid']"); + return isEqual(obj, obj2); + } + + return isEqual(renderResult.toJSON(), renderResult2.toJSON()); +} -export { renderComponent }; +export { areResultsEqual, findByDataTestId, getComponentTree, renderComponent }; diff --git a/ts/test/test-utils/utils/components.ts b/ts/test/test-utils/utils/components.ts deleted file mode 100644 index d912691bf..000000000 --- a/ts/test/test-utils/utils/components.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { RenderResult, prettyDOM } from '@testing-library/react'; -import { enableLogRedirect } from './stubbing'; - -const printHTMLElement = async (element: HTMLElement, name?: string) => { - if (!window.log || !enableLogRedirect) { - throw Error( - 'window.log is not defined. Have you turned on enableLogRedirect / called stubWindowLog() ?' - ); - } - - return window.log.debug(`\nHTML Element${name ? ` (${name})` : ''}:\n${prettyDOM(element)}\n`); -}; -const printRenderResult = async (result: RenderResult, name?: string) => { - if (!window.log || !enableLogRedirect) { - throw Error( - 'window.log is not defined. Have you turned on enableLogRedirect / called stubWindowLog() ?' - ); - } - - return window.log.debug( - `\nRender Result${name ? ` (${name})` : ''}:\n${prettyDOM(result.baseElement)}\n` - ); -}; - -export { printHTMLElement, printRenderResult }; diff --git a/yarn.lock b/yarn.lock index 18b2c91f0..53144a6d3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,6 +7,11 @@ resolved "https://registry.yarnpkg.com/7zip-bin/-/7zip-bin-5.1.1.tgz#9274ec7460652f9c632c59addf24efb1684ef876" integrity sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ== +"@adobe/css-tools@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.4.0.tgz#728c484f4e10df03d5a3acd0d8adcbbebff8ad63" + integrity sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ== + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465" @@ -583,6 +588,20 @@ lz-string "^1.5.0" pretty-format "^27.0.2" +"@testing-library/jest-dom@^6.4.6": + version "6.4.6" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.4.6.tgz#ec1df8108651bed5475534955565bed88c6732ce" + integrity sha512-8qpnGVincVDLEcQXWaHOf6zmlbwTKc6Us6PPu4CRnPXCzo2OGBS5cwgMMOWdxDpEz1mkbvXHpEy99M5Yvt682w== + dependencies: + "@adobe/css-tools" "^4.4.0" + "@babel/runtime" "^7.9.2" + aria-query "^5.0.0" + chalk "^3.0.0" + css.escape "^1.5.1" + dom-accessibility-api "^0.6.3" + lodash "^4.17.21" + redent "^3.0.0" + "@testing-library/react@^15.0.7": version "15.0.7" resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-15.0.7.tgz#ff733ce0893c875cb5a47672e8e772897128f4ae" @@ -679,13 +698,6 @@ dependencies: "@types/chai" "*" -"@types/chai-dom@^1.11.3": - version "1.11.3" - resolved "https://registry.yarnpkg.com/@types/chai-dom/-/chai-dom-1.11.3.tgz#1659ace2698cdcd9ed8b2c007876f53e37d9cc89" - integrity sha512-EUEZI7uID4ewzxnU7DJXtyvykhQuwe+etJ1wwOiJyQRTH/ifMWKX+ghiXkxCUvNJ6IQDodf0JXhuP6zZcy2qXQ== - dependencies: - "@types/chai" "*" - "@types/chai@*": version "4.3.16" resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.16.tgz#b1572967f0b8b60bf3f87fe1d854a5604ea70c82" @@ -980,6 +992,13 @@ hoist-non-react-statics "^3.3.0" redux "^4.0.0" +"@types/react-test-renderer@^18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-18.3.0.tgz#839502eae70058a4ae161f63385a8e7929cef4c0" + integrity sha512-HW4MuEYxfDbOHQsVlY/XtOvNHftCVEPhJF2pQXXwcUiUF+Oyb0usgp48HSgpK5rt8m9KZb22yqOeZm+rrVG8gw== + dependencies: + "@types/react" "*" + "@types/react-virtualized@^9.21.30": version "9.21.30" resolved "https://registry.yarnpkg.com/@types/react-virtualized/-/react-virtualized-9.21.30.tgz#ba39821bcb2487512a8a2cdd9fbdb5e6fc87fedb" @@ -1606,7 +1625,7 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -aria-query@5.3.0: +aria-query@5.3.0, aria-query@^5.0.0: version "5.3.0" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== @@ -2090,11 +2109,6 @@ chai-bytes@^0.1.2: resolved "https://registry.yarnpkg.com/chai-bytes/-/chai-bytes-0.1.2.tgz#c297e81d47eb3106af0676ded5bb5e0c9f981db3" integrity sha512-0ol6oJS0y1ozj6AZK8n1pyv1/G+l44nqUJygAkK1UrYl+IOGie5vcrEdrAlwmLYGIA9NVvtHWosPYwWWIXf/XA== -chai-dom@^1.12.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/chai-dom/-/chai-dom-1.12.0.tgz#cfa4023ddfe2de93c78670eafbe2dd36902c9131" - integrity sha512-pLP8h6IBR8z1AdeQ+EMcJ7dXPdsax/1Q7gdGZjsnAmSBl3/gItQUYSCo32br1qOy4SlcBjvqId7ilAf3uJ2K1w== - chai@^4.3.4: version "4.4.1" resolved "https://registry.yarnpkg.com/chai/-/chai-4.4.1.tgz#3603fa6eba35425b0f2ac91a009fe924106e50d1" @@ -2122,6 +2136,14 @@ chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -2547,6 +2569,11 @@ css-tree@^1.1.2: mdn-data "2.0.14" source-map "^0.6.1" +css.escape@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" + integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg== + cssesc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" @@ -2825,6 +2852,11 @@ dom-accessibility-api@^0.5.9: resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== +dom-accessibility-api@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz#993e925cc1d73f2c662e7d75dd5a5445259a8fd8" + integrity sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w== + dom-helpers@^5.0.1, dom-helpers@^5.1.3: version "5.2.1" resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" @@ -6208,6 +6240,11 @@ react-intersection-observer@^9.7.0: resolved "https://registry.yarnpkg.com/react-intersection-observer/-/react-intersection-observer-9.10.3.tgz#70d21ad3c3719ea4fb4eb5a543b9755d31de3b8d" integrity sha512-9NYfKwPZRovB6QJee7fDg0zz/SyYrqXtn5xTZU0vwLtLVBtfu9aZt1pVmr825REE49VPDZ7Lm5SNHjJBOTZHpA== +"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.2.0, react-is@^18.3.1: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== + react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -6218,11 +6255,6 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-is@^18.0.0, react-is@^18.2.0: - version "18.3.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" - integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== - react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" @@ -6258,6 +6290,23 @@ react-redux@8.1.3: react-is "^18.0.0" use-sync-external-store "^1.0.0" +react-shallow-renderer@^16.15.0: + version "16.15.0" + resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz#48fb2cf9b23d23cde96708fe5273a7d3446f4457" + integrity sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA== + dependencies: + object-assign "^4.1.1" + react-is "^16.12.0 || ^17.0.0 || ^18.0.0" + +react-test-renderer@^18.3.1: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.3.1.tgz#e693608a1f96283400d4a3afead6893f958b80b4" + integrity sha512-KkAgygexHUkQqtvvx/otwxtuFu5cVjfzTCtjXLH9boS19/Nbtg84zS7wIQn39G8IlrhThBpQsMKkq5ZHZIYFXA== + dependencies: + react-is "^18.3.1" + react-shallow-renderer "^16.15.0" + scheduler "^0.23.2" + react-toastify@^6.0.9: version "6.2.0" resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-6.2.0.tgz#f2d76747c70b9de91f71f253d9feae6b53dc836c" From bde96d14e26bf8fdd01050d0f6374314a3a45c1e Mon Sep 17 00:00:00 2001 From: yougotwill Date: Mon, 22 Jul 2024 12:04:46 +1000 Subject: [PATCH 3/5] fix: remove unneeded catch --- ts/session/profile_manager/ProfileManager.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/ts/session/profile_manager/ProfileManager.ts b/ts/session/profile_manager/ProfileManager.ts index d6e145696..a92646916 100644 --- a/ts/session/profile_manager/ProfileManager.ts +++ b/ts/session/profile_manager/ProfileManager.ts @@ -106,8 +106,6 @@ export async function updateOurProfileDisplayName(newName: string, onboarding?: null ); return userInfoName; - } catch (err) { - throw err; } finally { await UserConfigWrapperActions.free(); } From 4a3adaa5e4925fbd7fe96e957930ea9d3a745c4c Mon Sep 17 00:00:00 2001 From: yougotwill Date: Mon, 22 Jul 2024 15:36:13 +1000 Subject: [PATCH 4/5] fix: aded simple test for sessioninput made note to migratie to storybook in the future --- ts/test/components/AvatarPlaceHolder_test.tsx | 1 + ts/test/components/SessionInput_test.tsx | 27 +++++++++++++++++++ ts/test/components/renderComponent.tsx | 17 ++++++++++-- 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 ts/test/components/SessionInput_test.tsx diff --git a/ts/test/components/AvatarPlaceHolder_test.tsx b/ts/test/components/AvatarPlaceHolder_test.tsx index 388bee5a3..e4e39702f 100644 --- a/ts/test/components/AvatarPlaceHolder_test.tsx +++ b/ts/test/components/AvatarPlaceHolder_test.tsx @@ -8,6 +8,7 @@ import { MemberAvatarPlaceHolder } from '../../components/icon/MemberAvatarPlace import { TestUtils } from '../test-utils'; import { areResultsEqual, findByDataTestId, renderComponent } from './renderComponent'; +// TODO[epic=SES-2418] migrate to Storybook describe('AvatarPlaceHolder', () => { const pubkey = TestUtils.generateFakePubKeyStr(); const displayName = 'Hello World'; diff --git a/ts/test/components/SessionInput_test.tsx b/ts/test/components/SessionInput_test.tsx new file mode 100644 index 000000000..c857892f7 --- /dev/null +++ b/ts/test/components/SessionInput_test.tsx @@ -0,0 +1,27 @@ +/* eslint-disable import/no-extraneous-dependencies */ +import { cleanup } from '@testing-library/react'; +import { expect } from 'chai'; +import Sinon from 'sinon'; +import { SessionInput } from '../../components/inputs'; +import { TestUtils } from '../test-utils'; +import { findAllByElementType, renderComponent } from './renderComponent'; + +// TODO[epic=SES-2418] migrate to Storybook +describe('SessionInput', () => { + beforeEach(() => { + TestUtils.stubSVGElement(); + TestUtils.stubWindowLog(); + }); + + afterEach(() => { + Sinon.restore(); + cleanup(); + }); + + it('should render an input', async () => { + const result = renderComponent(); + const inputElements = findAllByElementType(result, 'input'); + expect(inputElements.length, 'should have an input element').to.equal(1); + result.unmount(); + }); +}); diff --git a/ts/test/components/renderComponent.tsx b/ts/test/components/renderComponent.tsx index c11ce694c..0b73a0041 100644 --- a/ts/test/components/renderComponent.tsx +++ b/ts/test/components/renderComponent.tsx @@ -1,6 +1,6 @@ import { AnimatePresence, MotionGlobalConfig } from 'framer-motion'; import { isArray, isEqual, unset } from 'lodash'; -import { ReactElement, ReactNode } from 'react'; +import { ElementType, ReactElement, ReactNode } from 'react'; import { ErrorBoundary } from 'react-error-boundary'; import TestRenderer from 'react-test-renderer'; import { SessionTheme } from '../../themes/SessionTheme'; @@ -39,6 +39,13 @@ function findByDataTestId( return renderResult.root.findByProps({ 'data-testid': dataTestId }); } +function findAllByElementType( + renderResult: TestRenderer.ReactTestRenderer, + elementType: ElementType +): Array { + return renderResult.root.findAllByType(elementType); +} + function areResultsEqual( renderResult: TestRenderer.ReactTestRenderer, renderResult2: TestRenderer.ReactTestRenderer, @@ -55,4 +62,10 @@ function areResultsEqual( return isEqual(renderResult.toJSON(), renderResult2.toJSON()); } -export { areResultsEqual, findByDataTestId, getComponentTree, renderComponent }; +export { + areResultsEqual, + findAllByElementType, + findByDataTestId, + getComponentTree, + renderComponent, +}; From 3ad09b38a49473a81cd5a972909e517cbd882782 Mon Sep 17 00:00:00 2001 From: yougotwill Date: Mon, 22 Jul 2024 16:18:11 +1000 Subject: [PATCH 5/5] fix: update translation link show external link on hover --- .../settings/SessionSettingListItem.tsx | 18 ++++++++++++++---- .../settings/section/CategoryHelp.tsx | 10 +++++----- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/ts/components/settings/SessionSettingListItem.tsx b/ts/components/settings/SessionSettingListItem.tsx index c511c66f0..7cf6e1f19 100644 --- a/ts/components/settings/SessionSettingListItem.tsx +++ b/ts/components/settings/SessionSettingListItem.tsx @@ -1,5 +1,6 @@ import styled from 'styled-components'; +import { shell } from 'electron'; import { isEmpty, pick } from 'lodash'; import { ReactNode } from 'react'; import { Flex } from '../basic/Flex'; @@ -130,12 +131,21 @@ export const SessionSettingsItemWrapper = (props: { ); }; -export const SessionSettingsTitleWithLink = (props: { title: string; onClick: () => void }) => { - const { onClick, title } = props; +export const SessionSettingsTitleWithLink = (props: { title: string; link: string }) => { + const { title, link } = props; return ( - + { + void shell.openExternal(link); + }} + > - + ); }; diff --git a/ts/components/settings/section/CategoryHelp.tsx b/ts/components/settings/section/CategoryHelp.tsx index efd82673d..35f3e5e57 100644 --- a/ts/components/settings/section/CategoryHelp.tsx +++ b/ts/components/settings/section/CategoryHelp.tsx @@ -1,4 +1,4 @@ -import { ipcRenderer, shell } from 'electron'; +import { ipcRenderer } from 'electron'; import { SessionButtonShape, SessionButtonType } from '../../basic/SessionButton'; @@ -19,19 +19,19 @@ export const SettingsCategoryHelp = () => { /> void shell.openExternal('https://getsession.org/survey')} + link={'https://getsession.org/survey'} /> void shell.openExternal('https://crowdin.com/project/session-desktop/')} + link={'https://getsession.org/translate'} /> void shell.openExternal('https://getsession.org/faq')} + link={'https://getsession.org/faq'} /> void shell.openExternal('https://sessionapp.zendesk.com/hc/en-us')} + link={'https://sessionapp.zendesk.com/hc/en-us'} /> );