Skip to content
This repository has been archived by the owner on Mar 27, 2023. It is now read-only.

fix(react): fix jsdom react testing issues #6612

Merged
merged 1 commit into from
Feb 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 20 additions & 14 deletions packages/core/docs/react.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -98,20 +98,9 @@ const Component = () => {
## Testing

React Testing Library is [recommended by the React team](https://reactjs.org/docs/testing.html)
for unit testing React components. Because of the way web components are rendered, the synchronous
`get` methods of RTL might not work, use the asynchronous `find` ones instead.

```javascript
test('my test', async () => {
render(<CdsButton>My Button</CdsButton>);

// might not work
expect(screen.getByRole('button', { name: 'My Button' })).toBeInTheDocument();

// will work
expect(await screen.findByRole('button', { name: 'My Button' })).toBeInTheDocument();
});
```
for unit testing React components. By default, RTL runs in jest using jsdom, a node-based implementation
of browser standards. It does not have implementations of all the latest browser features,
so there may issues rendering and interacting with Clarity components.

### IntersectionObserver

Expand All @@ -130,3 +119,20 @@ window.IntersectionObserver = jest.fn().mockReturnValue({
<a href="https://github.com/vmware/clarity/tree/next/apps" target="_blank" rel="noopener">
<cds-button>Example Apps</cds-button>
</a>

### Finding a Clarity element

Because of the way web components are rendered, the synchronous
`get` methods of RTL might not work, use the asynchronous `find` ones instead.
ashleyryan marked this conversation as resolved.
Show resolved Hide resolved

```javascript
test('my test', async () => {
render(<CdsButton>My Button</CdsButton>);

// might not work
expect(screen.getByRole('button', { name: 'My Button' })).toBeInTheDocument();

// will work
expect(await screen.findByRole('button', { name: 'My Button' })).toBeInTheDocument();
});
```
9 changes: 9 additions & 0 deletions packages/core/src/internal/utils/environment.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { isBrowser } from './environment';

describe('Environment Helper: ', () => {
describe('isBrowser():', () => {
it('returns true when expected', () => {
expect(isBrowser()).toBe(true);
});
});
});
9 changes: 9 additions & 0 deletions packages/core/src/internal/utils/environment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { isNil } from '../utils/identity.js';

export function isBrowser(win = window) {
return !isNil(win);
}

export function isJestTest() {
return (globalThis as any)?.process?.env?.JEST_WORKER_ID !== undefined;
}
7 changes: 5 additions & 2 deletions packages/core/src/internal/utils/events.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
/*
* Copyright (c) 2016-2021 VMware, Inc. All Rights Reserved.
* Copyright (c) 2016-2022 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/

import { isJestTest } from './environment.js';

export function stopEvent(event: Event) {
event.preventDefault();
event.stopPropagation();
Expand All @@ -29,7 +31,8 @@ export const getElementUpdates = (

const updatedProp = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(element), propertyKey) as any;

if (updatedProp) {
// Jest and JSDom breaks defining a new property, so skip
if (updatedProp && !isJestTest()) {
Object.defineProperty(element, propertyKey, {
get: updatedProp.get,
set: val => {
Expand Down
10 changes: 2 additions & 8 deletions packages/core/src/internal/utils/exists.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/*
* Copyright (c) 2016-2020 VMware, Inc. All Rights Reserved.
* Copyright (c) 2016-2022 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/

import { existsIn, existsInWindow, isBrowser } from './exists.js';
import { existsIn, existsInWindow } from './exists.js';

describe('Functional Helper: ', () => {
describe('existsInWindow(): ', () => {
Expand Down Expand Up @@ -91,10 +91,4 @@ describe('Functional Helper: ', () => {
expect(existsIn(['notDefined'], myTestObject)).toEqual(false);
});
});

describe('isBrowser():', () => {
it('returns true when expected', () => {
expect(isBrowser()).toBe(true);
});
});
});
7 changes: 1 addition & 6 deletions packages/core/src/internal/utils/exists.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
/*
* Copyright (c) 2016-2021 VMware, Inc. All Rights Reserved.
* Copyright (c) 2016-2022 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/

import curryN from 'ramda/es/curryN.js';
import isNil from 'ramda/es/isNil.js';
import path from 'ramda/es/path.js';
import __ from './__.js';

Expand All @@ -26,7 +25,3 @@ export function elementExists(tagName: string, registry?: any): boolean {
}

export const existsInWindow = existsIn(__, window);

export function isBrowser(win = window) {
return !isNil(win);
}
4 changes: 2 additions & 2 deletions packages/core/src/internal/utils/global.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/*
* Copyright (c) 2016-2021 VMware, Inc. All Rights Reserved.
* Copyright (c) 2016-2022 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/

import { isBrowser } from './exists.js';
import { isBrowser } from './environment.js';
import { getAngularVersion, getReactVersion, getVueVersion, getAngularJSVersion } from './framework.js';
import { FeatureSupportMatrix, browserFeatures } from './supports.js';
import { LogService } from '../services/log.service.js';
Expand Down
5 changes: 3 additions & 2 deletions packages/core/src/internal/utils/registration.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
/*
* Copyright (c) 2016-2021 VMware, Inc. All Rights Reserved.
* Copyright (c) 2016-2022 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/

import curryN from 'ramda/es/curryN.js';
import { elementExists, existsInWindow, isBrowser } from './exists.js';
import { isBrowser } from './environment.js';
import { elementExists, existsInWindow } from './exists.js';
import { CDSState, setupCDSGlobal } from './global.js';
import { isStorybook } from './framework.js';
import { LogService } from '../services/log.service.js';
Expand Down
10 changes: 3 additions & 7 deletions packages/core/src/polyfills/aria-reflect.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2016-2021 VMware, Inc. All Rights Reserved.
* Copyright (c) 2016-2022 VMware, Inc. All Rights Reserved.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/
Expand Down Expand Up @@ -59,19 +59,15 @@ declare global {
let roleRegistered = false;
let ariaRegistered = false;

function isNode() {
return (globalThis as any)?.process?.env?.JEST_WORKER_ID !== undefined; // Jest and JSDom breaks on property reflection
}

// eslint-disable-next-line
if (!roleRegistered && !Element.prototype.hasOwnProperty('role') && !isNode()) {
if (!roleRegistered && !Element.prototype.hasOwnProperty('role')) {
reflect(Element.prototype, 'role', 'role');
roleRegistered = true;
}

// https://www.w3.org/TR/wai-aria-1.0/states_and_properties
// eslint-disable-next-line
if (!ariaRegistered && !Element.prototype.hasOwnProperty('ariaLabel') && !isNode()) {
if (!ariaRegistered && !Element.prototype.hasOwnProperty('ariaLabel')) {
ariaRegistered = true;
[
'ActiveDescendant',
Expand Down