diff --git a/cypress/component/Emotion-useStyle.cy.tsx b/cypress/component/Emotion-useStyle.cy.tsx new file mode 100644 index 0000000000..ca62e77ff1 --- /dev/null +++ b/cypress/component/Emotion-useStyle.cy.tsx @@ -0,0 +1,157 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 - present Instructure, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import { useState } from 'react' +import { expect } from 'chai' +import { + InstUISettingsProvider, + WithStyleProps, + useStyle +} from '@instructure/emotion/src/index' + +import '../support/component' +import 'cypress-real-events' + +type Props = { + inverse?: boolean +} & WithStyleProps + +type Theme = { + key: string + colors: { + contrasts: { + grey1111: string + green4570: string + blue4570: string + } + } +} + +type ComponentTheme = { + textColor: string + textColorInverse: string + backgroundColor: string +} + +const grey1111 = 'rgb(0, 128, 0)' +const green4570 = 'rgb(10, 10, 10)' +const blue4570 = 'rgb(255, 255, 0)' +const exampleTheme: Theme = { + key: 'exampleTheme', + colors: { + contrasts: { + grey1111, + green4570, + blue4570 + } + } +} + +const generateComponentTheme = function (theme: Theme): ComponentTheme { + const { colors } = theme + return { + textColor: colors.contrasts.grey1111, + textColorInverse: colors.contrasts.green4570, + backgroundColor: colors.contrasts.blue4570 + } +} + +type StyleParams = { + inverse: boolean + clearBackground: boolean + themeOverride: Props['themeOverride'] +} + +const generateStyle = function ( + componentTheme: ComponentTheme, + params: StyleParams +) { + const { inverse, clearBackground } = params + + return { + exampleComponent: { + label: 'exampleComponent', + color: componentTheme.textColor, + background: componentTheme.backgroundColor, + insetInlineStart: '8px', + ...(inverse && { color: componentTheme.textColorInverse }), + ...(clearBackground && { background: 'transparent' }) + } + } +} + +const ThemedComponent = ({ inverse = false, themeOverride }: Props) => { + const [clearBackground, setClearBackground] = useState(false) + + const styles = useStyle({ + generateStyle, + generateComponentTheme, + componentId: 'ThemedComponent', + params: { inverse, clearBackground, themeOverride } + }) + + const handleClick = () => { + setClearBackground(true) + } + + return ( +
+

Hello World

+ +
+ ) +} + +describe('useStyle should apply bi-directional polyfill on styles object', () => { + it('in default "ltr" mode', async () => { + cy.mount( + + + + ) + + cy.get('[data-testid="useStyle-testComp"]').then(($el) => { + const computedStyle = getComputedStyle($el[0]) + + // `inset-inline-start` becomes 'left' in LTR mode + expect(computedStyle.left).to.equal('8px') + expect(computedStyle.right).to.equal('auto') + }) + }) + + it('in "rtl" mode', async () => { + cy.mount( + + + + ) + cy.get('[data-testid="useStyle-testComp"]').then(($el) => { + const computedStyle = getComputedStyle($el[0]) + + // `inset-inline-start` should be transformed to 'right' in 'rtl' mode + expect(computedStyle.left).to.equal('auto') + expect(computedStyle.right).to.equal('8px') + }) + }) +}) diff --git a/cypress/component/Emotion-withStyle.cy.tsx b/cypress/component/Emotion-withStyle.cy.tsx new file mode 100644 index 0000000000..2d3729b606 --- /dev/null +++ b/cypress/component/Emotion-withStyle.cy.tsx @@ -0,0 +1,174 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 - present Instructure, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import { Component } from 'react' +import { expect } from 'chai' +import { + InstUISettingsProvider, + WithStyleProps, + withStyle +} from '@instructure/emotion/src/index' +import PropTypes from 'prop-types' + +import '../support/component' +import 'cypress-real-events' + +type Props = { + inverse?: boolean +} & WithStyleProps + +type State = { clearBackground?: boolean } + +type Theme = { + key: string + colors: { + contrasts: { + grey1111: string + green4570: string + blue4570: string + } + } +} + +type ComponentTheme = { + textColor: string + textColorInverse: string + backgroundColor: string +} + +const grey1111 = 'rgb(0, 128, 0)' +const green4570 = 'rgb(10, 10, 10)' +const blue4570 = 'rgb(255, 255, 0)' +const exampleTheme: Theme = { + key: 'exampleTheme', + colors: { + contrasts: { + grey1111, + green4570, + blue4570 + } + } +} + +const generateComponentTheme = function (theme: Theme): ComponentTheme { + const { colors } = theme + return { + textColor: colors.contrasts.grey1111, + textColorInverse: colors.contrasts.green4570, + backgroundColor: colors.contrasts.blue4570 + } +} + +const generateStyle = function ( + componentTheme: ComponentTheme, + props: Props, + state: State +) { + const { inverse } = props + const { clearBackground } = state + + return { + exampleComponent: { + label: 'exampleComponent', + color: componentTheme.textColor, + background: componentTheme.backgroundColor, + insetInlineStart: '8px', + ...(inverse && { color: componentTheme.textColorInverse }), + ...(clearBackground && { background: 'transparent' }) + } + } +} + +@withStyle(generateStyle, generateComponentTheme) +class ThemeableComponent extends Component { + static propTypes = { + inverse: PropTypes.bool + } + + static defaultTypes = { + inverse: false + } + + state = { + clearBackground: false + } + + componentDidMount() { + this.props.makeStyles!({ clearBackground: this.state.clearBackground }) + } + + componentDidUpdate() { + this.props.makeStyles!({ clearBackground: this.state.clearBackground }) + } + + handleClick = () => { + this.setState({ + clearBackground: true + }) + } + + render() { + const { styles } = this.props + return ( +
+

Hello World

+ +
+ ) + } +} + +describe('withStyle should apply bi-directional polyfill on styles object', () => { + it('in default "ltr" mode', () => { + cy.mount( + + + + ) + + cy.get('[data-testid="withStyle-testComp"]').then(($el) => { + const computedStyle = getComputedStyle($el[0]) + + // `inset-inline-start` should be transformed to 'left' in 'ltr' mode + expect(computedStyle.left).to.equal('8px') + expect(computedStyle.right).to.equal('auto') + }) + }) + + it('in "rtl" mode', () => { + cy.mount( + + + + ) + + cy.get('[data-testid="withStyle-testComp"]').then(($el) => { + const computedStyle = getComputedStyle($el[0]) + + // `inset-inline-start` should be transformed to 'right' in 'rtl' mode + expect(computedStyle.left).to.equal('auto') + expect(computedStyle.right).to.equal('8px') + }) + }) +}) diff --git a/cypress/component/InstUISettingsProvider.cy.tsx b/cypress/component/InstUISettingsProvider.cy.tsx new file mode 100644 index 0000000000..aa5765d7cc --- /dev/null +++ b/cypress/component/InstUISettingsProvider.cy.tsx @@ -0,0 +1,59 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 - present Instructure, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import canvas from '@instructure/ui-themes/src/index' +import { InstUISettingsProvider } from '@instructure/emotion/src/index' +import '../support/component' +import 'cypress-real-events' + +describe('', () => { + it('can handle nested text direction setting changes', () => { + cy.mount( + + +
hello world
+
+
+ ) + + cy.get('[data-testid="textDirAwareElement"]').then(($el) => { + const direction = getComputedStyle($el[0]).direction + expect(direction).to.equal('rtl') + }) + + // Set prop: dir + cy.mount( + + +
hello world
+
+
+ ) + + cy.get('[data-testid="textDirAwareElement"]').then(($el) => { + const direction = getComputedStyle($el[0]).direction + expect(direction).to.equal('ltr') + }) + }) +}) diff --git a/package-lock.json b/package-lock.json index 33aeebae05..b852123e53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34841,8 +34841,11 @@ }, "devDependencies": { "@instructure/ui-babel-preset": "10.20.1", - "@instructure/ui-test-utils": "10.20.1", - "react-dom": "^18.3.1" + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^16.0.1", + "@testing-library/user-event": "^14.6.1", + "react-dom": "^18.3.1", + "vitest": "^3.2.2" }, "peerDependencies": { "react": ">=16.14 <=18" diff --git a/packages/emotion/package.json b/packages/emotion/package.json index 3e04c14093..a48510c111 100644 --- a/packages/emotion/package.json +++ b/packages/emotion/package.json @@ -38,8 +38,11 @@ }, "devDependencies": { "@instructure/ui-babel-preset": "10.20.1", - "@instructure/ui-test-utils": "10.20.1", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^16.0.1", + "@testing-library/user-event": "^14.6.1", + "vitest": "^3.2.2" }, "peerDependencies": { "react": ">=16.14 <=18" diff --git a/packages/emotion/src/InstUISettingsProvider/__old-tests__/InstUISettingProvider.test.tsx b/packages/emotion/src/InstUISettingsProvider/__old-tests__/InstUISettingProvider.test.tsx deleted file mode 100644 index 8b86571f6c..0000000000 --- a/packages/emotion/src/InstUISettingsProvider/__old-tests__/InstUISettingProvider.test.tsx +++ /dev/null @@ -1,82 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2015 - present Instructure, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -import { Component } from 'react' -import { expect, mount, spy } from '@instructure/ui-test-utils' -import canvas from '@instructure/ui-themes' -import { InstUISettingsProvider } from '../index' -import { textDirectionContextConsumer } from '@instructure/ui-i18n' -import type { TextDirectionContextConsumerProps } from '@instructure/ui-i18n' - -@textDirectionContextConsumer() -class TextDirAwareComponent extends Component { - render() { - return
hello world
- } -} - -describe('InstUISettingsProvider', async () => { - it('can handle nested text direction setting changes', async () => { - const subject = await mount( - - - - - - ) - let element = subject.getDOMNode().firstElementChild - expect(element!.getAttribute('data-dir')).to.equal('rtl') - await subject.setProps({ dir: 'ltr' }) - element = subject.getDOMNode().firstElementChild - expect(element!.getAttribute('data-dir')).to.equal('ltr') - }) - it('can handle text direction on native HTML elements', async () => { - const subject = await mount( - -
Should be RTL
-
- ) - let element = subject.getDOMNode() - - expect(element.getAttribute('dir')).to.equal('rtl') - - await subject.setProps({ dir: 'ltr' }) - element = subject.getDOMNode() - - expect(element.getAttribute('dir')).to.equal('ltr') - }) - it('warns when "as" property is used without using the "dir" property', async () => { - const consoleWarning = spy(console, 'warn') - const warningMessage = - "The 'as' property should be used in conjunction with the 'dir' property!" - - await mount( - //@ts-expect-error div is required - -
text
-
- ) - expect(consoleWarning).to.has.been.calledWith(warningMessage) - }) -}) diff --git a/packages/emotion/src/InstUISettingsProvider/__tests__/InstUISettingsProvider.test.tsx b/packages/emotion/src/InstUISettingsProvider/__tests__/InstUISettingsProvider.test.tsx index 2d1270a8b4..2c5a9b43eb 100644 --- a/packages/emotion/src/InstUISettingsProvider/__tests__/InstUISettingsProvider.test.tsx +++ b/packages/emotion/src/InstUISettingsProvider/__tests__/InstUISettingsProvider.test.tsx @@ -22,14 +22,33 @@ * SOFTWARE. */ -import { render } from '@testing-library/react' +import { render, screen } from '@testing-library/react' import { vi } from 'vitest' +import type { MockInstance } from 'vitest' import '@testing-library/jest-dom' +import { canvasHighContrast } from '@instructure/ui-themes/src/index' import { InstUISettingsProvider } from '../index' -import { canvasHighContrast } from '@instructure/ui-themes' describe('', () => { + let consoleWarningMock: ReturnType + let consoleErrorMock: ReturnType + + beforeEach(() => { + // Mocking console to prevent test output pollution and expect for messages + consoleWarningMock = vi + .spyOn(console, 'warn') + .mockImplementation(() => {}) as MockInstance + consoleErrorMock = vi + .spyOn(console, 'error') + .mockImplementation(() => {}) as MockInstance + }) + + afterEach(() => { + consoleWarningMock.mockRestore() + consoleErrorMock.mockRestore() + }) + it('passes the current theme if a function is passed to theme prop', async () => { const themeFn = vi.fn() render( @@ -40,4 +59,39 @@ describe('', () => { expect(themeFn).toHaveBeenCalledWith(canvasHighContrast) }) + + it('can handle text direction on native HTML elements', async () => { + const { rerender } = render( + +
Should be RTL
+
+ ) + + let element = screen.getByTestId('child').parentElement! + + expect(element).toHaveAttribute('dir', 'rtl') + + // Set prop: dir + rerender( + +
Should be RTL
+
+ ) + element = screen.getByTestId('child').parentElement! + + expect(element).toHaveAttribute('dir', 'ltr') + }) + + it('warns when "as" property is used without using the "dir" property', async () => { + const warningMessage = + "The 'as' property should be used in conjunction with the 'dir' property!" + + render( + //@ts-expect-error div is required + +
text
+
+ ) + expect(consoleWarningMock).toHaveBeenCalledWith(warningMessage) + }) }) diff --git a/packages/emotion/src/__old-tests__/getComponentThemeOverride.test.ts b/packages/emotion/src/__tests__/getComponentThemeOverride.test.tsx similarity index 80% rename from packages/emotion/src/__old-tests__/getComponentThemeOverride.test.ts rename to packages/emotion/src/__tests__/getComponentThemeOverride.test.tsx index 5dea293515..bdba7857dd 100644 --- a/packages/emotion/src/__old-tests__/getComponentThemeOverride.test.ts +++ b/packages/emotion/src/__tests__/getComponentThemeOverride.test.tsx @@ -22,16 +22,34 @@ * SOFTWARE. */ -import { expect } from '@instructure/ui-test-utils' +import { vi } from 'vitest' +import type { MockInstance } from 'vitest' import canvas from '@instructure/ui-themes' - import { getComponentThemeOverride } from '../getComponentThemeOverride' const componentName = 'ExampleComponent' const componentId = 'Example.Component' -describe('@getComponentThemeOverride', async () => { - describe('should return empty object', async () => { +describe('@getComponentThemeOverride', () => { + let consoleWarningMock: ReturnType + let consoleErrorMock: ReturnType + + beforeEach(() => { + // Mocking console to prevent test output pollution + consoleWarningMock = vi + .spyOn(console, 'warn') + .mockImplementation(() => {}) as MockInstance + consoleErrorMock = vi + .spyOn(console, 'error') + .mockImplementation(() => {}) as MockInstance + }) + + afterEach(() => { + consoleWarningMock.mockRestore() + consoleErrorMock.mockRestore() + }) + + describe('should return empty object', () => { it('if there is no theme set', async () => { const override = getComponentThemeOverride( {}, @@ -39,8 +57,7 @@ describe('@getComponentThemeOverride', async () => { componentId, {} ) - - expect(override).to.deep.equal({}) + expect(override).toEqual({}) }) it('if there is no override set', async () => { @@ -51,11 +68,11 @@ describe('@getComponentThemeOverride', async () => { {} ) - expect(override).to.deep.equal({}) + expect(override).toEqual({}) }) }) - describe('should return the correct override', async () => { + describe('should return the correct override', () => { it('if there is `themeOverride` set on the props', async () => { const componentOverride = { componentColor: 'rgb(150, 0, 0)' @@ -69,7 +86,7 @@ describe('@getComponentThemeOverride', async () => { } ) - expect(override).to.deep.equal(componentOverride) + expect(override).toEqual(componentOverride) }) it('if the `themeOverride` prop is a function', async () => { @@ -92,7 +109,7 @@ describe('@getComponentThemeOverride', async () => { theme ) - expect(override).to.deep.equal({ + expect(override).toEqual({ backgroundBlue: 'rgb(0, 150, 0)', backgroundDark: '#FFFFFF' }) @@ -117,12 +134,12 @@ describe('@getComponentThemeOverride', async () => { } ) - expect(override).to.deep.equal({ + expect(override).toEqual({ componentColor: 'rgb(0, 150, 0)' }) }) - describe('if there is `componentOverrides` set on the theme', async () => { + describe('if there is `componentOverrides` set on the theme', () => { it('with the displayName', async () => { const componentOverride = { componentColor: 'rgb(0, 0, 150)' @@ -139,7 +156,7 @@ describe('@getComponentThemeOverride', async () => { {} ) - expect(override).to.deep.equal(componentOverride) + expect(override).toEqual(componentOverride) }) it('with the componentId', async () => { @@ -158,7 +175,7 @@ describe('@getComponentThemeOverride', async () => { {} ) - expect(override).to.deep.equal(componentOverride) + expect(override).toEqual(componentOverride) }) it('with both the displayName and componentId (displayName takes precedence)', async () => { @@ -175,7 +192,7 @@ describe('@getComponentThemeOverride', async () => { {} ) - expect(override).to.deep.equal({ componentColor: 'rgb(150, 150, 150)' }) + expect(override).toEqual({ componentColor: 'rgb(150, 150, 150)' }) }) }) }) diff --git a/packages/emotion/src/__old-tests__/useStyle.test.tsx b/packages/emotion/src/__tests__/useStyle.test.tsx similarity index 62% rename from packages/emotion/src/__old-tests__/useStyle.test.tsx rename to packages/emotion/src/__tests__/useStyle.test.tsx index ebba41e482..7e918e39f0 100644 --- a/packages/emotion/src/__old-tests__/useStyle.test.tsx +++ b/packages/emotion/src/__tests__/useStyle.test.tsx @@ -23,8 +23,11 @@ */ import { useState } from 'react' - -import { expect, mount, stub, within } from '@instructure/ui-test-utils' +import { vi } from 'vitest' +import type { MockInstance } from 'vitest' +import { render, screen, waitFor } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import '@testing-library/jest-dom' import { InstUISettingsProvider, WithStyleProps, useStyle } from '../index' type Props = { @@ -48,7 +51,7 @@ type ComponentTheme = { backgroundColor: string } -describe('useStyle', async () => { +describe('useStyle', () => { const grey1111 = 'rgb(0, 128, 0)' const green4570 = 'rgb(10, 10, 10)' const blue4570 = 'rgb(255, 255, 0)' @@ -111,41 +114,61 @@ describe('useStyle', async () => { } return ( -
+

Hello World

) } - describe('with theme provided by InstUISettingsProvider', async () => { + let consoleWarningMock: ReturnType + let consoleErrorMock: ReturnType + + beforeEach(() => { + // Mocking console to prevent test output pollution and expect for messages + consoleWarningMock = vi + .spyOn(console, 'warn') + .mockImplementation(() => {}) as MockInstance + consoleErrorMock = vi + .spyOn(console, 'error') + .mockImplementation(() => {}) as MockInstance + }) + + afterEach(() => { + consoleWarningMock.mockRestore() + consoleErrorMock.mockRestore() + }) + + describe('with theme provided by InstUISettingsProvider', () => { it('should add css class suffixed with label', async () => { - const subject = await mount( + render( ) const emotionClassRegex = new RegExp(/^css-.+-exampleComponent$/) - expect(subject.getDOMNode().classList[0]).to.match(emotionClassRegex) + const themedComponent = screen.getByTestId('testComp')! + + expect(themedComponent.classList[0]).toMatch(emotionClassRegex) }) it('should apply correct css props', async () => { - const subject = await mount( + render( ) - const component = subject.getDOMNode() + const component = screen.getByTestId('testComp')! const computedStyle = getComputedStyle(component) - expect(computedStyle.color).to.equal('rgb(0, 128, 0)') - expect(computedStyle.backgroundColor).to.equal('rgb(255, 255, 0)') + expect(computedStyle.color).toEqual('rgb(0, 128, 0)') + expect(computedStyle.backgroundColor).toEqual('rgb(255, 255, 0)') }) - describe('should allow configuration through the themeOverride prop', async () => { + describe('should allow configuration through the themeOverride prop', () => { it('when it is an object', async () => { - const subject = await mount( + render( { /> ) - const component = subject.getDOMNode() + const component = screen.getByTestId('testComp')! const computedStyle = getComputedStyle(component) - expect(computedStyle.color).to.equal('rgb(128, 0, 128)') - expect(computedStyle.backgroundColor).to.equal('rgb(255, 255, 0)') + expect(computedStyle.color).toEqual('rgb(128, 0, 128)') + expect(computedStyle.backgroundColor).toEqual('rgb(255, 255, 0)') }) it('when it is a function', async () => { - const subject = await mount( + render( ({ @@ -171,35 +194,31 @@ describe('useStyle', async () => { /> ) - const component = subject.getDOMNode() + const component = screen.getByTestId('testComp')! const computedStyle = getComputedStyle(component) - expect(computedStyle.color).to.equal('rgb(255, 255, 0)') - expect(computedStyle.backgroundColor).to.equal('rgb(255, 255, 0)') + expect(computedStyle.color).toEqual('rgb(255, 255, 0)') + expect(computedStyle.backgroundColor).toEqual('rgb(255, 255, 0)') }) }) it('should ignore empty themeOverride props', async () => { - const subject = await mount( + render( ) - const component = subject.getDOMNode() + const component = screen.getByTestId('testComp')! const computedStyle = getComputedStyle(component) - expect(computedStyle.color).to.equal('rgb(0, 128, 0)') - expect(computedStyle.backgroundColor).to.equal('rgb(255, 255, 0)') + expect(computedStyle.color).toEqual('rgb(0, 128, 0)') + expect(computedStyle.backgroundColor).toEqual('rgb(255, 255, 0)') }) }) - describe('should update css props', async () => { + describe('should update css props', () => { it('when props are updated', async () => { - // `setProps` can be called on the outer component, - // so have to add the theme ad themeOverride here, and suppress the error - stub(console, 'warn') // suppress "no theme provided error" - - const subject = await mount( + const { rerender } = render( { }} /> ) - const component = subject.getDOMNode() + const component = screen.getByTestId('testComp')! + expect(getComputedStyle(component).color).toEqual(grey1111) - expect(getComputedStyle(component).color).to.equal(grey1111) - - await subject.setProps({ inverse: true }) + // Set Prop: inverse + rerender( + + ) - expect(getComputedStyle(component).color).to.equal(blue4570) + const updatedComponent = screen.getByTestId('testComp')! + expect(getComputedStyle(updatedComponent).color).toEqual(blue4570) }) it('when state is updated', async () => { - const subject = await mount( + render( ) - const main = within(subject.getDOMNode()) - const clearBackgroundButton = await main.find('button') - const component = main.getDOMNode() + const component = screen.getByTestId('testComp')! + const clearBackgroundButton = screen.getByText('Button') - expect(getComputedStyle(component).backgroundColor).to.equal( + expect(getComputedStyle(component).backgroundColor).toEqual( 'rgb(255, 255, 0)' ) - await clearBackgroundButton.click() - - expect(getComputedStyle(component).backgroundColor).to.equal( - 'rgba(0, 0, 0, 0)' - ) - }) - }) - - describe('should apply bi-directional polyfill on styles object', async () => { - it('in default "ltr" mode', async () => { - const subject = await mount( - - - - ) - const component = subject.getDOMNode() - const computedStyle = getComputedStyle(component) + await userEvent.click(clearBackgroundButton) - // `inset-inline-start` should be transformed to 'left' in 'ltr' mode - expect(computedStyle.left).to.equal('8px') - expect(computedStyle.right).to.equal('auto') - }) - - it('in "rtl" mode', async () => { - const subject = await mount( - - - - ) - const component = subject.getDOMNode().firstElementChild - const computedStyle = getComputedStyle(component!) - - // `inset-inline-start` should be transformed to 'right' in 'rtl' mode - expect(computedStyle.left).to.equal('auto') - expect(computedStyle.right).to.equal('8px') + await waitFor(() => { + expect(getComputedStyle(component).backgroundColor).toEqual( + 'rgba(0, 0, 0, 0)' + ) + }) }) }) }) diff --git a/packages/emotion/src/__old-tests__/useTheme.test.tsx b/packages/emotion/src/__tests__/useTheme.test.tsx similarity index 65% rename from packages/emotion/src/__old-tests__/useTheme.test.tsx rename to packages/emotion/src/__tests__/useTheme.test.tsx index 2c0f2359fa..353838c0da 100644 --- a/packages/emotion/src/__old-tests__/useTheme.test.tsx +++ b/packages/emotion/src/__tests__/useTheme.test.tsx @@ -21,14 +21,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - +import { render } from '@testing-library/react' +import { vi } from 'vitest' +import type { MockInstance } from 'vitest' import { canvas, canvasHighContrast, canvasThemeLocal } from '@instructure/ui-themes' -import { expect, mount, spy } from '@instructure/ui-test-utils' import { ThemeRegistry } from '@instructure/theme-registry' +import '@testing-library/jest-dom' import { useTheme } from '../useTheme' import { InstUISettingsProvider } from '../InstUISettingsProvider' @@ -49,64 +51,84 @@ const ExampleComponent = (props: Props) => { } describe('useTheme hook', () => { + let consoleWarningMock: ReturnType + let consoleErrorMock: ReturnType + beforeEach(() => { ThemeRegistry.clearRegistry() + + // Mocking console to prevent test output pollution and expect for messages + consoleWarningMock = vi + .spyOn(console, 'warn') + .mockImplementation(() => {}) as MockInstance + consoleErrorMock = vi + .spyOn(console, 'error') + .mockImplementation(() => {}) as MockInstance }) + afterEach(() => { //this is needed to not mess up the global Theme Registry ThemeRegistry.setRegistry(defaultRegistry) + + consoleWarningMock.mockRestore() + consoleErrorMock.mockRestore() }) + describe('with using InstUISettingsProvider', () => { it('should use default canvas theme when no "theme" is provided', async () => { - const callback = spy() - await mount( + const callback = vi.fn() + render( ) - expect(callback).to.have.been.calledWith(canvas) + expect(callback).toHaveBeenCalledWith(canvas) }) + it('should use global theme when no "theme" is provided', async () => { - const callback = spy() + const callback = vi.fn() const theme = ThemeRegistry.registerTheme(canvasHighContrast) theme.use() - await mount( + render( ) - expect(callback).to.have.been.calledWith(theme) + expect(callback).toHaveBeenCalledWith(theme) }) + it('should default to canvas theme when no global theme is available and no theme is provided', async () => { - const callback = spy() + const callback = vi.fn() - await mount( + render( ) - expect(callback).to.have.been.calledWith(canvas) + expect(callback).toHaveBeenCalledWith(canvas) }) + it('should use provided theme when theme is provided', async () => { - const callback = spy() + const callback = vi.fn() const theme = ThemeRegistry.registerTheme(canvas) theme.use() - await mount( + render( ) - expect(callback).to.have.been.calledWith(canvasHighContrast) + expect(callback).toHaveBeenCalledWith(canvasHighContrast) }) + it('should override the default theme when no other theme is available', async () => { - const callback = spy() - await mount( + const callback = vi.fn() + render( { ) - expect(callback).to.have.been.calledWithMatch({ - ...canvas, - colors: { - primitives: { - white: 'red' - } - } - }) + expect(callback).toHaveBeenCalledWith( + expect.objectContaining({ + colors: expect.objectContaining({ + primitives: expect.objectContaining({ + white: 'red' + }) + }) + }) + ) }) + it('should override the global theme if that is the only theme available', async () => { - const callback = spy() + const callback = vi.fn() const theme = ThemeRegistry.registerTheme(canvasHighContrast) theme.use() - await mount( + render( { ) - expect(callback).to.have.been.calledWithMatch({ - ...theme, - colors: { - primitives: { - white: 'red' - } - } - }) + expect(callback).toHaveBeenCalledWith( + expect.objectContaining({ + ...theme, + colors: expect.objectContaining({ + primitives: expect.objectContaining({ + white: 'red' + }) + }) + }) + ) }) + it('should override the global theme with the "themeOverrides" option if that is the only theme available', async () => { - const callback = spy() + const callback = vi.fn() const theme = ThemeRegistry.registerTheme(canvasHighContrast) theme.use() - await mount( + render( { ) - expect(callback).to.have.been.calledWithMatch({ - ...theme, - colors: { - primitives: { - white: 'red' - } - } - }) + expect(callback).toHaveBeenCalledWith( + expect.objectContaining({ + ...theme, + colors: expect.objectContaining({ + primitives: expect.objectContaining({ + white: 'red' + }) + }) + }) + ) }) + it('should override the theme from context when provided', async () => { - const callback = spy() + const callback = vi.fn() const theme = ThemeRegistry.registerTheme(canvasHighContrast) theme.use() - await mount( + render( { ) - expect(callback).to.have.been.calledWithMatch({ - ...canvasHighContrast, - colors: { - primitives: { - white: 'red' - } - } - }) + expect(callback).toHaveBeenCalledWith( + expect.objectContaining({ + ...canvasHighContrast, + colors: expect.objectContaining({ + primitives: expect.objectContaining({ + white: 'red' + }) + }) + }) + ) }) + it('should use local themes correctly', async () => { - const callback = spy() + const callback = vi.fn() const theme = ThemeRegistry.registerTheme(canvas) theme.use() - await mount( + render( { ) - expect(callback).to.have.been.calledWithMatch({ - ...canvasThemeLocal, - colors: { - primitives: { - white: 'red' - } - } - }) + expect(callback).toHaveBeenCalledWith( + expect.objectContaining({ + ...canvasThemeLocal, + colors: expect.objectContaining({ + primitives: expect.objectContaining({ + white: 'red' + }) + }) + }) + ) }) }) + describe('without using InstUISettingsProvider', () => { it('should use theme from global ThemeRegistry', async () => { - const callback = spy() + const callback = vi.fn() const theme = ThemeRegistry.registerTheme(canvasHighContrast) theme.use() - await mount() + render() - expect(callback).to.have.been.calledWith(theme) + expect(callback).toHaveBeenCalledWith(theme) }) + it('should use default "canvas" theme when no theme is used from ThemeRegistry', async () => { - const callback = spy() - await mount() + const callback = vi.fn() + render() - expect(callback).to.have.been.calledWith(canvas) + expect(callback).toHaveBeenCalledWith(canvas) }) }) @@ -273,7 +310,7 @@ describe('useTheme hook', () => { it('overrides should work correctly', async () => { const [cb1, cb2, cb3, cb4, cb5, cb6] = Array(6) .fill(0) - .map(() => spy()) + .map(() => vi.fn()) const theme = ThemeRegistry.registerTheme(canvasHighContrast) theme.use({ @@ -288,7 +325,7 @@ describe('useTheme hook', () => { } }) - await mount( + render( <> {/* no provider -> canvasHighContrast theme */} @@ -345,52 +382,61 @@ describe('useTheme hook', () => { ) - expect(cb1).to.have.been.calledWithMatch({ - ...theme, - colors: { - primitives: { - red12: 'red', - green12: 'yellow', - blue12: 'magenta' - } - } - }) - expect(cb2).to.have.been.calledWith(canvasHighContrast) - expect(cb3).to.have.been.calledWith(canvas) - expect(cb4).to.have.been.calledWithMatch({ - ...canvas, - colors: { - primitives: { - red12: 'orange', - green12: 'olive' - } - } - }) - expect(cb5).to.have.been.calledWithMatch({ - ...theme, - colors: { - primitives: { - red12: 'red', - green12: 'yellow', - blue12: 'magenta' - } - } - }) - expect(cb6).to.have.been.calledWithMatch({ - ...theme, - colors: { - primitives: { - red12: 'brown', - green12: 'pink', - blue12: 'magenta' - } - } - }) + expect(cb1).toHaveBeenCalledWith( + expect.objectContaining({ + ...theme, + colors: expect.objectContaining({ + primitives: expect.objectContaining({ + red12: 'red', + green12: 'yellow', + blue12: 'magenta' + }) + }) + }) + ) + expect(cb2).toHaveBeenCalledWith(canvasHighContrast) + expect(cb3).toHaveBeenCalledWith(canvas) + expect(cb4).toHaveBeenCalledWith( + expect.objectContaining({ + ...canvas, + colors: expect.objectContaining({ + primitives: expect.objectContaining({ + red12: 'orange', + green12: 'olive' + }) + }) + }) + ) + expect(cb5).toHaveBeenCalledWith( + expect.objectContaining({ + ...theme, + colors: expect.objectContaining({ + primitives: expect.objectContaining({ + red12: 'red', + green12: 'yellow', + blue12: 'magenta' + }) + }) + }) + ) + expect(cb6).toHaveBeenCalledWith( + expect.objectContaining({ + ...theme, + colors: expect.objectContaining({ + primitives: expect.objectContaining({ + red12: 'brown', + green12: 'pink', + blue12: 'magenta' + }) + }) + }) + ) }) + it('local themes should work correctly', async () => { const [cb1, cb2, cb3, cb4] = Array(6) .fill(0) - .map(() => spy()) + .map(() => vi.fn()) const theme = ThemeRegistry.registerTheme(canvasHighContrast) theme.use({ @@ -405,7 +451,7 @@ describe('useTheme hook', () => { } }) - await mount( + render( <> {/* no provider -> canvasHighContrast theme */} @@ -439,72 +485,79 @@ describe('useTheme hook', () => { ) - expect(cb1).to.have.been.calledWithMatch({ - ...theme, - colors: { - primitives: { - red12: 'red', - green12: 'yellow', - blue12: 'magenta' - } - } - }) - expect(cb2).to.have.been.calledWith(canvas) - expect(cb3).to.have.been.calledWith(canvasThemeLocal) - expect(cb4).to.have.been.calledWithMatch({ - ...canvasThemeLocal, - colors: { - primitives: { - red12: 'orange', - green12: 'olive' - } - } - }) + expect(cb1).toHaveBeenCalledWith( + expect.objectContaining({ + ...theme, + colors: expect.objectContaining({ + primitives: expect.objectContaining({ + red12: 'red', + green12: 'yellow', + blue12: 'magenta' + }) + }) + }) + ) + + expect(cb2).toHaveBeenCalledWith(canvas) + expect(cb3).toHaveBeenCalledWith(canvasThemeLocal) + expect(cb4).toHaveBeenCalledWith( + expect.objectContaining({ + ...canvasThemeLocal, + colors: expect.objectContaining({ + primitives: expect.objectContaining({ + red12: 'orange', + green12: 'olive' + }) + }) + }) + ) }) }) + it('should work correctly when multiple React trees is used', async () => { - const callback1 = spy() - const callback2 = spy() + const callback1 = vi.fn() + const callback2 = vi.fn() const theme = ThemeRegistry.registerTheme(canvasHighContrast) theme.use() //first react tree - await mount() + render() //second react tree - await mount() + render() - expect(callback1).to.have.been.calledWith(theme) - expect(callback2).to.have.been.calledWith(theme) + expect(callback1).toHaveBeenCalledWith(theme) + expect(callback2).toHaveBeenCalledWith(theme) }) + it('should work correctly when multiple React trees is used together with InstUISettingsProvider', async () => { - const callback1 = spy() - const callback2 = spy() - const callback3 = spy() + const callback1 = vi.fn() + const callback2 = vi.fn() + const callback3 = vi.fn() const theme = ThemeRegistry.registerTheme(canvasHighContrast) theme.use() //first react tree - await mount() + render() //second react tree - await mount( + render( ) //third react tree - await mount( + render( ) - expect(callback1).to.have.been.calledWith(theme) - expect(callback2).to.have.been.calledWith(theme) - expect(callback3).to.have.been.calledWith(canvasHighContrast) + expect(callback1).toHaveBeenCalledWith(theme) + expect(callback2).toHaveBeenCalledWith(theme) + expect(callback3).toHaveBeenCalledWith(canvasHighContrast) }) }) diff --git a/packages/emotion/src/__old-tests__/withStyle.test.tsx b/packages/emotion/src/__tests__/withStyle.test.tsx similarity index 62% rename from packages/emotion/src/__old-tests__/withStyle.test.tsx rename to packages/emotion/src/__tests__/withStyle.test.tsx index 3f796381be..d2d991259d 100644 --- a/packages/emotion/src/__old-tests__/withStyle.test.tsx +++ b/packages/emotion/src/__tests__/withStyle.test.tsx @@ -25,9 +25,13 @@ import { Component } from 'react' import ReactDOM from 'react-dom' import ReactTestUtils from 'react-dom/test-utils' +import { render, screen, waitFor } from '@testing-library/react' +import { vi } from 'vitest' +import type { MockInstance } from 'vitest' +import userEvent from '@testing-library/user-event' +import '@testing-library/jest-dom' import PropTypes from 'prop-types' -import { expect, match, mount, stub, within } from '@instructure/ui-test-utils' import { withStyle, InstUISettingsProvider, WithStyleProps } from '../index' type Props = { @@ -53,7 +57,7 @@ type ComponentTheme = { backgroundColor: string } -describe('@withStyle', async () => { +describe('@withStyle', () => { const grey1111 = 'rgb(0, 128, 0)' const green4570 = 'rgb(10, 10, 10)' const blue4570 = 'rgb(255, 255, 0)' @@ -128,7 +132,7 @@ describe('@withStyle', async () => { render() { const { styles } = this.props return ( -
+

Hello World

@@ -146,46 +150,66 @@ describe('@withStyle', async () => { } } + let consoleWarningMock: ReturnType + let consoleErrorMock: ReturnType + + beforeEach(() => { + // Mocking console to prevent test output pollution + consoleWarningMock = vi + .spyOn(console, 'warn') + .mockImplementation(() => {}) as MockInstance + consoleErrorMock = vi + .spyOn(console, 'error') + .mockImplementation(() => {}) as MockInstance + }) + + afterEach(() => { + consoleWarningMock.mockRestore() + consoleErrorMock.mockRestore() + }) + it('can be found and tested with ReactTestUtils', async () => { const rootNode = document.createElement('div') document.body.appendChild(rootNode) // eslint-disable-next-line react/no-render-return-value const rendered = ReactDOM.render(, rootNode) - ReactTestUtils.findRenderedComponentWithType( + const foundComponent = ReactTestUtils.findRenderedComponentWithType( rendered as any, (ThemeableComponent as any).originalType ) + expect(foundComponent).toBeTruthy() }) - describe('with theme provided by InstUISettingsProvider', async () => { + describe('with theme provided by InstUISettingsProvider', () => { it('should add css class suffixed with label', async () => { - const subject = await mount( + render( ) const emotionClassRegex = new RegExp(/^css-.+-exampleComponent$/) + const themeableComponent = screen.getAllByTestId('testComp')[1]! - expect(subject.getDOMNode().classList[0]).to.match(emotionClassRegex) + expect(themeableComponent.classList[0]).toMatch(emotionClassRegex) }) it('should apply correct css props', async () => { - const subject = await mount( + render( ) - const component = subject.getDOMNode() + const component = screen.getAllByTestId('testComp')[1]! const computedStyle = getComputedStyle(component) - expect(computedStyle.color).to.equal('rgb(0, 128, 0)') - expect(computedStyle.backgroundColor).to.equal('rgb(255, 255, 0)') + expect(computedStyle.color).toEqual('rgb(0, 128, 0)') + expect(computedStyle.backgroundColor).toEqual('rgb(255, 255, 0)') }) - describe('should allow configuration through the themeOverride prop', async () => { + describe('should allow configuration through the themeOverride prop', () => { it('when it is an object', async () => { - const subject = await mount( + render( { /> ) - const component = subject.getDOMNode() + const component = screen.getAllByTestId('testComp')[1]! const computedStyle = getComputedStyle(component) - expect(computedStyle.color).to.equal('rgb(128, 0, 128)') - expect(computedStyle.backgroundColor).to.equal('rgb(255, 255, 0)') + expect(computedStyle.color).toEqual('rgb(128, 0, 128)') + expect(computedStyle.backgroundColor).toEqual('rgb(255, 255, 0)') }) it('when it is a function', async () => { - const subject = await mount( + render( ({ @@ -211,35 +235,31 @@ describe('@withStyle', async () => { /> ) - const component = subject.getDOMNode() + const component = screen.getAllByTestId('testComp')[1]! const computedStyle = getComputedStyle(component) - expect(computedStyle.color).to.equal('rgb(255, 255, 0)') - expect(computedStyle.backgroundColor).to.equal('rgb(255, 255, 0)') + expect(computedStyle.color).toEqual('rgb(255, 255, 0)') + expect(computedStyle.backgroundColor).toEqual('rgb(255, 255, 0)') }) }) it('should ignore empty themeOverride props', async () => { - const subject = await mount( + render( ) - const component = subject.getDOMNode() + const component = screen.getAllByTestId('testComp')[1]! const computedStyle = getComputedStyle(component) - expect(computedStyle.color).to.equal('rgb(0, 128, 0)') - expect(computedStyle.backgroundColor).to.equal('rgb(255, 255, 0)') + expect(computedStyle.color).toEqual('rgb(0, 128, 0)') + expect(computedStyle.backgroundColor).toEqual('rgb(255, 255, 0)') }) }) - describe('should update css props', async () => { + describe('should update css props', () => { it('when props are updated', async () => { - // `setProps` can be called on the outer component, - // so have to add the theme ad themeOverride here, and suppress the error - stub(console, 'warn') // suppress "no theme provided error" - - const subject = await mount( + const { rerender } = render( { }} /> ) - const component = subject.getDOMNode() + const component = screen.getAllByTestId('testComp')[1]! + expect(getComputedStyle(component).color).toEqual(grey1111) - expect(getComputedStyle(component).color).to.equal(grey1111) - - await subject.setProps({ inverse: true }) - - expect(getComputedStyle(component).color).to.equal(blue4570) + // Set prop: inverse + rerender( + + ) + const updatedComponent = screen.getAllByTestId('testComp')[1]! + expect(getComputedStyle(updatedComponent).color).toEqual(blue4570) }) it('when state is updated', async () => { - const subject = await mount( + render( ) - const main = within(subject.getDOMNode()) - const clearBackgroundButton = await main.find('button') - const component = main.getDOMNode() + const component = screen.getAllByTestId('testComp')[1]! + const clearBackgroundButton = screen.getAllByText('Button')[1] - expect(getComputedStyle(component).backgroundColor).to.equal( + expect(getComputedStyle(component).backgroundColor).toEqual( 'rgb(255, 255, 0)' ) - await clearBackgroundButton.click() + await userEvent.click(clearBackgroundButton) - expect(getComputedStyle(component).backgroundColor).to.equal( - 'rgba(0, 0, 0, 0)' - ) - }) - }) - - describe('should apply bi-directional polyfill on styles object', async () => { - it('in default "ltr" mode', async () => { - const subject = await mount( - - - - ) - const component = subject.getDOMNode() - const computedStyle = getComputedStyle(component) - - // `inset-inline-start` should be transformed to 'left' in 'ltr' mode - expect(computedStyle.left).to.equal('8px') - expect(computedStyle.right).to.equal('auto') - }) - - it('in "rtl" mode', async () => { - const subject = await mount( - - - - ) - const component = subject.getDOMNode().firstElementChild - const computedStyle = getComputedStyle(component!) - - // `inset-inline-start` should be transformed to 'right' in 'rtl' mode - expect(computedStyle.left).to.equal('auto') - expect(computedStyle.right).to.equal('8px') + await waitFor(() => { + expect(getComputedStyle(component).backgroundColor).toEqual( + 'rgba(0, 0, 0, 0)' + ) + }) }) }) - describe('should not allow manually passing prop', async () => { + describe('should not allow manually passing prop', () => { it('styles', async () => { - stub(console, 'warn') // suppress warning - - const subject = await mount( + render( ) - const component = subject.getDOMNode() + const component = screen.getAllByTestId('testComp')[1]! const computedStyle = getComputedStyle(component) - expect(computedStyle.color).to.equal(grey1111) + expect(computedStyle.color).toEqual(grey1111) }) it('makeStyles', async () => { - stub(console, 'warn') // suppress warning - const consoleLog = stub(console, 'log') + const consoleLog = vi.spyOn(console, 'log').mockImplementation(() => {}) - const subject = await mount( + render( { @@ -340,35 +337,35 @@ describe('@withStyle', async () => { /> ) - const component = subject.getDOMNode() + const component = screen.getAllByTestId('testComp')[1]! const computedStyle = getComputedStyle(component) - expect(computedStyle.color).to.equal(grey1111) - expect(consoleLog).not.to.have.been.called() + expect(computedStyle.color).toEqual(grey1111) + expect(consoleLog).not.toHaveBeenCalled() + + vi.restoreAllMocks() }) }) - describe('should throw warning when manually passing prop', async () => { + describe('should throw warning when manually passing prop', () => { it('styles', async () => { - const consoleWarning = stub(console, 'warn') - - await mount( + render( ) - expect(consoleWarning).to.have.been.calledWithMatch( - `Warning: Manually passing the "styles" property is not allowed on the ThemeableComponent component. Using the default styles calculated by the @withStyle decorator instead.`, - match.object, - match.string + const expectedWarningMessage = `Warning: Manually passing the "styles" property is not allowed on the ThemeableComponent component. Using the default styles calculated by the @withStyle decorator instead.` + + expect(consoleWarningMock).toHaveBeenCalledWith( + expect.stringContaining(expectedWarningMessage), + expect.any(Object), + expect.any(String) ) }) it('makeStyles', async () => { - const consoleWarning = stub(console, 'warn') - - await mount( + render( { @@ -380,9 +377,11 @@ describe('@withStyle', async () => { ) - expect(consoleWarning).to.have.been.calledWithMatch( - `Manually passing the "makeStyles" property is not allowed on the ThemeableComponent component. Styles are calculated by the @withStyle decorator.`, - match.string + const expectedWarningMessage = `Manually passing the "makeStyles" property is not allowed on the ThemeableComponent component. Styles are calculated by the @withStyle decorator.` + + expect(consoleWarningMock).toHaveBeenCalledWith( + expect.stringContaining(expectedWarningMessage), + expect.any(String) ) }) }) diff --git a/packages/emotion/src/styleUtils/__old-tests__/getShorthandPropValue.test.ts b/packages/emotion/src/styleUtils/__tests__/getShorthandPropValue.test.tsx similarity index 80% rename from packages/emotion/src/styleUtils/__old-tests__/getShorthandPropValue.test.ts rename to packages/emotion/src/styleUtils/__tests__/getShorthandPropValue.test.tsx index eae21f3d9f..dc28d9d9f8 100644 --- a/packages/emotion/src/styleUtils/__old-tests__/getShorthandPropValue.test.ts +++ b/packages/emotion/src/styleUtils/__tests__/getShorthandPropValue.test.tsx @@ -22,7 +22,7 @@ * SOFTWARE. */ -import { expect } from '@instructure/ui-test-utils' +import { expect } from 'vitest' import { getShorthandPropValue } from '../getShorthandPropValue' const theme = { @@ -50,9 +50,9 @@ describe('getShorthandPropValue', () => { const marginResult = getShorthandPropValue(name, theme, value, 'margin') const paddingResult = getShorthandPropValue(name, theme, value, 'padding') - expect(borderResult).to.equal('0.1rem') - expect(marginResult).to.equal('1.1rem') - expect(paddingResult).to.equal('2.1rem') + expect(borderResult).toEqual('0.1rem') + expect(marginResult).toEqual('1.1rem') + expect(paddingResult).toEqual('2.1rem') }) it('converts 2 value syntax', () => { @@ -61,9 +61,9 @@ describe('getShorthandPropValue', () => { const marginResult = getShorthandPropValue(name, theme, values, 'margin') const paddingResult = getShorthandPropValue(name, theme, values, 'padding') - expect(borderResult).to.equal('0.3rem 0.4rem') - expect(marginResult).to.equal('1.3rem 1.4rem') - expect(paddingResult).to.equal('2.3rem 2.4rem') + expect(borderResult).toEqual('0.3rem 0.4rem') + expect(marginResult).toEqual('1.3rem 1.4rem') + expect(paddingResult).toEqual('2.3rem 2.4rem') }) it('converts 3 value syntax', () => { @@ -72,9 +72,9 @@ describe('getShorthandPropValue', () => { const marginResult = getShorthandPropValue(name, theme, values, 'margin') const paddingResult = getShorthandPropValue(name, theme, values, 'padding') - expect(borderResult).to.equal('0.2rem 0.1rem 0.4rem') - expect(marginResult).to.equal('1.2rem 1.1rem 1.4rem') - expect(paddingResult).to.equal('2.2rem 2.1rem 2.4rem') + expect(borderResult).toEqual('0.2rem 0.1rem 0.4rem') + expect(marginResult).toEqual('1.2rem 1.1rem 1.4rem') + expect(paddingResult).toEqual('2.2rem 2.1rem 2.4rem') }) it('converts 4 value syntax', () => { @@ -83,14 +83,14 @@ describe('getShorthandPropValue', () => { const marginResult = getShorthandPropValue(name, theme, values, 'margin') const paddingResult = getShorthandPropValue(name, theme, values, 'padding') - expect(borderResult).to.equal('0.4rem 0.2rem 0.1rem 0.3rem') - expect(marginResult).to.equal('1.4rem 1.2rem 1.1rem 1.3rem') - expect(paddingResult).to.equal('2.4rem 2.2rem 2.1rem 2.3rem') + expect(borderResult).toEqual('0.4rem 0.2rem 0.1rem 0.3rem') + expect(marginResult).toEqual('1.4rem 1.2rem 1.1rem 1.3rem') + expect(paddingResult).toEqual('2.4rem 2.2rem 2.1rem 2.3rem') }) it('returns 0 for none or 0, and returns auto if auto is supplied', () => { const values = '0 none auto large' const result = getShorthandPropValue(name, theme, values, 'border') - expect(result).to.equal('0 0 auto 0.3rem') + expect(result).toEqual('0 0 auto 0.3rem') }) }) diff --git a/packages/emotion/src/styleUtils/__old-tests__/makeThemeVars.test.ts b/packages/emotion/src/styleUtils/__tests__/makeThemeVars.test.tsx similarity index 82% rename from packages/emotion/src/styleUtils/__old-tests__/makeThemeVars.test.ts rename to packages/emotion/src/styleUtils/__tests__/makeThemeVars.test.tsx index 49dc506e04..16bfebb23b 100644 --- a/packages/emotion/src/styleUtils/__old-tests__/makeThemeVars.test.ts +++ b/packages/emotion/src/styleUtils/__tests__/makeThemeVars.test.tsx @@ -22,7 +22,7 @@ * SOFTWARE. */ -import { expect } from '@instructure/ui-test-utils' +import { expect } from 'vitest' import { makeThemeVars } from '../makeThemeVars' describe('makeThemeVars', () => { @@ -35,14 +35,14 @@ describe('makeThemeVars', () => { const result = makeThemeVars('margin', vars) // @ts-expect-error we expect it to throw error - expect(result['xSmall']).to.not.exist() + expect(result['xSmall']).toBeUndefined() // @ts-expect-error we expect it to throw error - expect(result['medium']).to.not.exist() + expect(result['medium']).toBeUndefined() // @ts-expect-error we expect it to throw error - expect(result['xxLarge']).to.not.exist() + expect(result['xxLarge']).toBeUndefined() - expect(result['marginXSmall']).to.equal('foo') - expect(result['marginMedium']).to.equal('bar') - expect(result['marginXxLarge']).to.equal('baz') + expect(result['marginXSmall']).toEqual('foo') + expect(result['marginMedium']).toEqual('bar') + expect(result['marginXxLarge']).toEqual('baz') }) }) diff --git a/packages/emotion/src/styleUtils/__old-tests__/mirrorShorthand.test.ts b/packages/emotion/src/styleUtils/__tests__/mirrorShorthand.test.tsx similarity index 85% rename from packages/emotion/src/styleUtils/__old-tests__/mirrorShorthand.test.ts rename to packages/emotion/src/styleUtils/__tests__/mirrorShorthand.test.tsx index 8bfb1c794b..437a49f14e 100644 --- a/packages/emotion/src/styleUtils/__old-tests__/mirrorShorthand.test.ts +++ b/packages/emotion/src/styleUtils/__tests__/mirrorShorthand.test.tsx @@ -22,7 +22,7 @@ * SOFTWARE. */ -import { expect } from '@instructure/ui-test-utils' +import { expect } from 'vitest' import { mirrorShorthandCorners, mirrorShorthandEdges @@ -32,24 +32,24 @@ describe('convertRtlShorthandEdges', () => { it('should not modify 1 value syntax', () => { const value = 'x-small' const result = mirrorShorthandEdges(value) - expect(result).to.equal(value) + expect(result).toEqual(value) }) it('should not modify 2 value syntax', () => { const value = 'x-small xx-large' const result = mirrorShorthandEdges(value) - expect(result).to.equal(value) + expect(result).toEqual(value) }) it('should not modify 3 value syntax', () => { const value = 'x-small medium x-large' const result = mirrorShorthandEdges(value) - expect(result).to.equal(value) + expect(result).toEqual(value) }) it('should swap the second and fourth values with 4 value syntax', () => { const result = mirrorShorthandEdges('auto x-small none x-large') - expect(result).to.equal('auto x-large none x-small') + expect(result).toEqual('auto x-large none x-small') }) }) @@ -57,21 +57,21 @@ describe('convertRtlShorthandCorners', () => { it('should not modify 1 value syntax', () => { const value = 'x-small' const result = mirrorShorthandCorners(value) - expect(result).to.equal(value) + expect(result).toEqual(value) }) it('should swap the first and second values with 2 value syntax', () => { const result = mirrorShorthandCorners('x-small xx-large') - expect(result).to.equal('xx-large x-small') + expect(result).toEqual('xx-large x-small') }) it('should convert 3 value syntax to 4 value syntax and switch the values appropriately', () => { const result = mirrorShorthandCorners('x-small medium x-large') - expect(result).to.equal('medium x-small medium x-large') + expect(result).toEqual('medium x-small medium x-large') }) it('should appropriately switch shorthand values to rtl with 4 value syntax', () => { const result = mirrorShorthandCorners('auto x-small none x-large') - expect(result).to.equal('x-small auto x-large none') + expect(result).toEqual('x-small auto x-large none') }) }) diff --git a/packages/emotion/tsconfig.build.json b/packages/emotion/tsconfig.build.json index 5e65e8b8fa..9a6e822bdf 100644 --- a/packages/emotion/tsconfig.build.json +++ b/packages/emotion/tsconfig.build.json @@ -28,9 +28,6 @@ { "path": "../ui-babel-preset/tsconfig.build.json" }, - { - "path": "../ui-test-utils/tsconfig.build.json" - }, { "path": "../ui-react-utils/tsconfig.build.json" },