Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Conformance tests should unmount components #21423

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "fix: Conformance tests should unmount components",
"packageName": "@fluentui/react-conformance",
"email": "lingfangao@hotmail.com",
"dependentChangeType": "patch"
}
4 changes: 3 additions & 1 deletion packages/react-conformance/src/defaultTests.tsx
Expand Up @@ -3,7 +3,8 @@ import { defaultErrorMessages } from './defaultErrorMessages';
import { ComponentDoc } from 'react-docgen-typescript';
import { getComponent } from './utils/getComponent';
import { getCallbackArguments } from './utils/getCallbackArguments';
import { mount, ReactWrapper } from 'enzyme';
import { ReactWrapper } from 'enzyme';
import { mount } from './utils/mountWithCleanup';
import { act } from 'react-dom/test-utils';
import parseDocblock from './utils/parseDocblock';
import { validateCallbackArguments } from './utils/validateCallbackArguments';
Expand Down Expand Up @@ -254,6 +255,7 @@ export const defaultTests: TestObject = {
const defaultEl = customMount(<Component {...requiredProps} />);
const defaultComponent = getComponent(defaultEl, helperComponents, wrapperComponent);
const defaultClassNames = defaultComponent.getDOMNode().getAttribute('class')?.split(' ') || [];
defaultEl.unmount();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The odd test that calls mount more than once. This kind of test cannot be used with a 'global' mount.

I think this is 🤮 but can't think of a better, idea. Suggestions welcome

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit confused, where's the other call? Also not quite understanding what the problem is (but I also don't have a deep understanding of how mount works without attachTo).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created a mountWithCleanup module which is generally how the enzyme community seems to handle the unmounting, however that solution breaks down when mount is used more than once in a test.

Unfortunately, this is the only time in all conformance tests this is done. The alternative would be to explicitly unmount in each test.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah now I see the second mount below this one. I'm still not convinced this is a problem given that the test is comparing classNames in two different cases, but theoretically you could do the same thing by mounting the component once and updating props. Or to be certain of avoiding interference/retained state, maybe the test could mount both versions at once inside a fragment?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah that's a good shout, I'll try to update the test to use a single mount


const el = customMount(<Component {...mergedProps} />);
const component = getComponent(el, helperComponents, wrapperComponent);
Expand Down
3 changes: 3 additions & 0 deletions packages/react-conformance/src/isConformant.ts
Expand Up @@ -5,12 +5,15 @@ import { defaultTests } from './defaultTests';
import { merge } from './utils/merge';
import { createTsProgram } from './utils/createTsProgram';
import { getComponentDoc } from './utils/getComponentDoc';
import { cleanup } from './utils/mountWithCleanup';

export function isConformant<TProps = {}>(...testInfo: Partial<IsConformantOptions<TProps>>[]) {
const mergedOptions = merge<IsConformantOptions>(...testInfo);
const { componentPath, displayName, disabledTests = [], extraTests, tsconfigDir } = mergedOptions;

describe('isConformant', () => {
afterEach(cleanup);

if (!fs.existsSync(componentPath)) {
throw new Error(`Path ${componentPath} does not exist`);
}
Expand Down
25 changes: 25 additions & 0 deletions packages/react-conformance/src/utils/mountWithCleanup.ts
@@ -0,0 +1,25 @@
import { mount as enzymeMount, ReactWrapper } from 'enzyme';

let wrapper: ReactWrapper | undefined;

/**
* A wrapper around enzyme's mount that helps with unmounting and cleanup
* This is an approach that's adopted by quite a few users of enzyme.
* https://github.com/enzymejs/enzyme/issues/911
* @returns Enzyme wrapper
*/
export const mount = (...args: Parameters<typeof enzymeMount>) => {
const enzymeWrapper = enzymeMount(...args);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
wrapper = enzymeWrapper;
Comment on lines +13 to +15
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why it's not assignable?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enzyme mount is generic, which takes the props of the component passed in. SInce the wrapper is never exported or returned I decided not to recreate all those generic types

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you cast instead of ts-ignore so it's less "nuclear"?

return enzymeWrapper;
};

export const cleanup = () => {
if (wrapper) {
wrapper.unmount();
}

wrapper = undefined;
};