Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "fix: Conformance tests should unmount components",
"packageName": "@fluentui/react-conformance",
"email": "[email protected]",
"dependentChangeType": "patch"
}
4 changes: 3 additions & 1 deletion packages/react-conformance/src/defaultTests.tsx
Original file line number Diff line number Diff line change
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
Contributor 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
Contributor 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
Contributor 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
Original file line number Diff line number Diff line change
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
29 changes: 29 additions & 0 deletions packages/react-conformance/src/utils/mountWithCleanup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
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
Contributor 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) {
try {
wrapper.unmount();
} catch {
console.error('failed to unmount');
}
}

wrapper = undefined;
};