diff --git a/packages/react-conformance/src/defaultErrorMessages.tsx b/packages/react-conformance/src/defaultErrorMessages.tsx index cd851e8c5bffd5..aa5334002706c0 100644 --- a/packages/react-conformance/src/defaultErrorMessages.tsx +++ b/packages/react-conformance/src/defaultErrorMessages.tsx @@ -4,6 +4,7 @@ import { EOL } from 'os'; import * as path from 'path'; import { errorMessageColors, formatArray, getErrorMessage, formatErrors, getPackagePath } from './utils/index'; +import { prettyDOM } from '@testing-library/dom'; /* eslint-disable @typescript-eslint/naming-convention */ @@ -223,7 +224,7 @@ export const defaultErrorMessages = { overview: `has a custom 'size' prop but also applies 'size' as a native prop.`, details: [ `After passing 'size="${sizeValue}"' to ${testErrorName(displayName)} it was applied to this element:`, - appliedToElement.outerHTML, + prettyDOM(appliedToElement) as string, ], suggestions: [ `Ensure 'size' is omitted when calling native props helpers.`, @@ -238,15 +239,14 @@ export const defaultErrorMessages = { error: Error, testClassName: string, classNames: string[] | undefined, - componentHTML: string, + rootEl: HTMLElement, ) => { const { displayName } = testInfo; const { testErrorInfo, resolveInfo, failedError, testErrorName } = errorMessageColors; - // Show part of the HTMl in the debug message if possible - const debugHTML = componentHTML.includes(testClassName) - ? componentHTML.substr(0, componentHTML.indexOf(testClassName) + 50) + '...' - : ''; + // Show part of the HTML in the debug message if possible + const elementWithClass = rootEl.getElementsByClassName(testClassName)[0]; + const debugHTML = elementWithClass ? prettyDOM(elementWithClass, 50) + '...' : ''; // Message Description: Handles scenario where className prop doesn't exist or isn't applied on the component // @@ -524,7 +524,6 @@ export const defaultErrorMessages = { 'component-has-static-classnames-object-exported': ( testInfo: IsConformantOptions, error: Error, - componentClassName: string, exportName: string, ) => { const { componentPath, displayName } = testInfo; @@ -548,7 +547,6 @@ export const defaultErrorMessages = { 'component-has-static-classnames-in-correct-format': ( testInfo: IsConformantOptions, error: Error, - componentClassName: string, exportName: string, ) => { const { componentPath, displayName } = testInfo; @@ -574,15 +572,29 @@ export const defaultErrorMessages = { error: Error, componentName: string, missingClassNames: string, + rootEl: HTMLElement, ) => { const { displayName } = testInfo; - const { testErrorInfo, failedError } = errorMessageColors; + const { testErrorInfo, failedError, resolveInfo } = errorMessageColors; return getErrorMessage({ displayName, overview: `missing one or more static classNames on component (${testErrorInfo(componentName)}).`, - details: [`Missing the following classes after render:`, ` ${failedError(missingClassNames)}`], - suggestions: [`Ensure that each slot of the component has its corresponding static className.`], + details: [ + `Missing the following classes after render:`, + ` ${failedError(missingClassNames)}`, + 'Actual HTML:', + prettyDOM(rootEl) as string, + ], + suggestions: [ + `Ensure that each slot of the component has its corresponding static className.`, + `If the component is rendered in a portal, add ${resolveInfo( + `getTargetElement`, + )} to isConformant in your test file.`, + `If the component requires certain props to render all slots, add ${resolveInfo( + 'staticClassNames.props', + )} to isConformant in your test file. (You can add multiple prop combinations.)`, + ], error, }); }, diff --git a/packages/react-conformance/src/defaultTests.tsx b/packages/react-conformance/src/defaultTests.tsx index 6a2dd7d98b3d01..e260634e92d8aa 100644 --- a/packages/react-conformance/src/defaultTests.tsx +++ b/packages/react-conformance/src/defaultTests.tsx @@ -1,13 +1,13 @@ +import * as React from 'react'; +import * as _ from 'lodash'; +import * as path from 'path'; +import { render } from '@testing-library/react'; + import { TestObject, IsConformantOptions } from './types'; import { defaultErrorMessages } from './defaultErrorMessages'; import { ComponentDoc } from 'react-docgen-typescript'; import { getPackagePath, getCallbackArguments, validateCallbackArguments } from './utils/index'; import { act } from 'react-dom/test-utils'; -import { render } from '@testing-library/react'; - -import * as React from 'react'; -import * as _ from 'lodash'; -import * as path from 'path'; const CALLBACK_REGEX = /^on(?!Render[A-Z])[A-Z]/; const DEFAULT_CLASSNAME_PREFIX = 'fui-'; @@ -214,13 +214,7 @@ export const defaultTests: TestObject = { handledClassName = true; } catch (e) { throw new Error( - defaultErrorMessages['component-handles-classname']( - testInfo, - e, - testClassName, - classNames, - domNode.outerHTML, - ), + defaultErrorMessages['component-handles-classname'](testInfo, e, testClassName, classNames, domNode), ); } }); @@ -307,14 +301,7 @@ export const defaultTests: TestObject = { }, 'component-has-static-classnames-object': (componentInfo: ComponentDoc, testInfo: IsConformantOptions) => { - const { - componentPath, - Component, - wrapperComponent, - helperComponents = [], - requiredProps, - customMount = mount, - } = testInfo; + const { componentPath, Component, requiredProps, renderOptions } = testInfo; const componentName = componentInfo.displayName; const componentClassName = `fui-${componentName}`; @@ -334,12 +321,7 @@ export const defaultTests: TestObject = { handledClassNamesObjectExport = true; } catch (e) { throw new Error( - defaultErrorMessages['component-has-static-classnames-object-exported']( - testInfo, - e, - componentClassName, - exportName, - ), + defaultErrorMessages['component-has-static-classnames-object-exported'](testInfo, e, exportName), ); } }); @@ -364,12 +346,7 @@ export const defaultTests: TestObject = { expect(classNamesFromFile).toEqual(expectedClassNames); } catch (e) { throw new Error( - defaultErrorMessages['component-has-static-classnames-in-correct-format']( - testInfo, - e, - componentClassName, - exportName, - ), + defaultErrorMessages['component-has-static-classnames-in-correct-format'](testInfo, e, exportName), ); } }); @@ -387,27 +364,27 @@ export const defaultTests: TestObject = { ...requiredProps, ...staticClassNames.props, }; - const defaultEl = customMount(); - const defaultComponent = getComponent(defaultEl, helperComponents, wrapperComponent); + const result = render(, renderOptions); + const rootEl = getTargetElement(testInfo, result, 'className'); const indexFile = require(indexPath); const classNamesFromFile = indexFile[exportName]; const expectedClassNames: { [key: string]: string } = staticClassNames.expectedClassNames ?? classNamesFromFile; - const missingClassNames = Object.values(expectedClassNames).reduce((acc, className) => { - if (defaultComponent.find(`.${className}`).length < 1) { - (acc as string[]).push(className); - } - return acc; - }, []); + const missingClassNames = Object.values(expectedClassNames).filter( + className => !rootEl.classList.contains(className) && !rootEl.querySelector(`.${className}`), + ); - if (missingClassNames.length > 0) { + try { + expect(missingClassNames).toHaveLength(0); + } catch (e) { throw new Error( defaultErrorMessages['component-has-static-classnames']( testInfo, - new Error(), + e, componentName, missingClassNames.join(', '), + rootEl, ), ); } diff --git a/packages/react-conformance/tsconfig.json b/packages/react-conformance/tsconfig.json index d61246ed54ac9f..a71ee73bcc3961 100644 --- a/packages/react-conformance/tsconfig.json +++ b/packages/react-conformance/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "baseUrl": ".", "outDir": "lib", - "target": "es2017", + "target": "es6", "module": "commonjs", "jsx": "react", "declaration": true, diff --git a/packages/react-popover/src/components/Popover/Popover.test.tsx b/packages/react-popover/src/components/Popover/Popover.test.tsx index cee7495f54c3d2..ef2231346dc5bf 100644 --- a/packages/react-popover/src/components/Popover/Popover.test.tsx +++ b/packages/react-popover/src/components/Popover/Popover.test.tsx @@ -8,6 +8,7 @@ describe('Popover', () => { isConformant({ Component: Popover, displayName: 'Popover', + requiredProps: { children:
hello
}, disabledTests: [ // Popover does not render DOM elements 'component-handles-ref',