fix: replace chai with react-test-renderer for unit testing components

rewrite avatar placeholder test
pull/3083/head
yougotwill 9 months ago
parent b08c4592c2
commit 362e360f40

@ -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",

@ -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(
<MemberAvatarPlaceHolder dataTestId="member-avatar-placeholder" />
);
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(
<AvatarPlaceHolder
diameter={AvatarSize.XL}
name={displayName}
pubkey={testPubkey}
dataTestId="avatar-placeholder"
/>
);
// 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
<AvatarPlaceHolder
diameter={AvatarSize.XL}
name={displayName}
pubkey={testPubkey}
dataTestId="avatar-placeholder"
/>
);
// 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();
});
});

@ -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<RenderOptions, 'wrapper'>) =>
render(ui, { wrapper: Providers, ...options });
function renderComponent(children: ReactElement): TestRenderer.ReactTestRenderer {
return TestRenderer.create(<Providers>{children}</Providers>);
}
function getComponentTree(
result: TestRenderer.ReactTestRenderer
): Array<TestRenderer.ReactTestRendererTree> {
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 };

@ -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 };

@ -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"

Loading…
Cancel
Save