Skip to content

Commit

Permalink
[test] Add toWarnDev() and toErrorDev() matcher (#21581)
Browse files Browse the repository at this point in the history
  • Loading branch information
eps1lon authored Jul 2, 2020
1 parent 0d4a0db commit 45cb77f
Show file tree
Hide file tree
Showing 63 changed files with 1,319 additions and 1,378 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"test:regressions": "yarn test:regressions:build && rimraf test/regressions/screenshots/chrome/* && vrtest run --config test/vrtest.config.js --record",
"test:regressions:build": "webpack --config test/regressions/webpack.config.js",
"test:umd": "node packages/material-ui/test/umd/run.js",
"test:unit": "cross-env NODE_ENV=test mocha 'packages/**/*.test.js' 'docs/**/*.test.js' 'scripts/**/*.test.js' --exclude '**/node_modules/**'",
"test:unit": "cross-env NODE_ENV=test mocha 'packages/**/*.test.js' 'docs/**/*.test.js' 'scripts/**/*.test.js' 'test/utils/**/*.test.js' --exclude '**/node_modules/**'",
"test:watch": "yarn test:unit --watch",
"typescript": "lerna run typescript --parallel"
},
Expand Down
92 changes: 45 additions & 47 deletions packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { expect } from 'chai';
import { getClasses } from '@material-ui/core/test-utils';
import createMount from 'test/utils/createMount';
import describeConformance from '@material-ui/core/test-utils/describeConformance';
import consoleErrorMock, { consoleWarnMock } from 'test/utils/consoleErrorMock';
import { spy } from 'sinon';
import { act, createClientRender, fireEvent, screen } from 'test/utils/createClientRender';
import { createFilterOptions } from '../useAutocomplete/useAutocomplete';
Expand Down Expand Up @@ -1021,16 +1020,6 @@ describe('<Autocomplete />', () => {
});

describe('warnings', () => {
beforeEach(() => {
consoleErrorMock.spy();
consoleWarnMock.spy();
});

afterEach(() => {
consoleErrorMock.reset();
consoleWarnMock.reset();
});

it('warn if getOptionLabel do not return a string', () => {
const handleChange = spy();
render(
Expand All @@ -1045,14 +1034,18 @@ describe('<Autocomplete />', () => {
);
const textbox = screen.getByRole('textbox');

fireEvent.change(textbox, { target: { value: 'a' } });
fireEvent.keyDown(textbox, { key: 'Enter' });
expect(() => {
fireEvent.change(textbox, { target: { value: 'a' } });
fireEvent.keyDown(textbox, { key: 'Enter' });
}).toErrorDev([
'Material-UI: The `getOptionLabel` method of Autocomplete returned undefined instead of a string',
// strict mode renders twice
'Material-UI: The `getOptionLabel` method of Autocomplete returned undefined instead of a string',
'Material-UI: The `getOptionLabel` method of Autocomplete returned undefined instead of a string',
'Material-UI: The `getOptionLabel` method of Autocomplete returned undefined instead of a string',
]);
expect(handleChange.callCount).to.equal(1);
expect(handleChange.args[0][1]).to.equal('a');
expect(consoleErrorMock.callCount()).to.equal(4); // strict mode renders twice
expect(consoleErrorMock.messages()[0]).to.include(
'Material-UI: The `getOptionLabel` method of Autocomplete returned undefined instead of a string',
);
});

it('warn if getOptionSelected match multiple values for a given option', () => {
Expand All @@ -1079,11 +1072,10 @@ describe('<Autocomplete />', () => {
);
const textbox = screen.getByRole('textbox');

fireEvent.keyDown(textbox, { key: 'ArrowDown' });
fireEvent.keyDown(textbox, { key: 'Enter' });

expect(consoleErrorMock.callCount()).to.equal(1);
expect(consoleErrorMock.messages()[0]).to.include(
expect(() => {
fireEvent.keyDown(textbox, { key: 'ArrowDown' });
fireEvent.keyDown(textbox, { key: 'Enter' });
}).toErrorDev(
'The component expects a single value to match a given option but found 2 matches.',
);
});
Expand All @@ -1092,19 +1084,22 @@ describe('<Autocomplete />', () => {
const value = 'not a good value';
const options = ['first option', 'second option'];

render(
<Autocomplete
{...defaultProps}
value={value}
options={options}
renderInput={(params) => <TextField {...params} />}
/>,
);

expect(consoleWarnMock.callCount()).to.equal(4); // strict mode renders twice
expect(consoleWarnMock.messages()[0]).to.include(
expect(() => {
render(
<Autocomplete
{...defaultProps}
value={value}
options={options}
renderInput={(params) => <TextField {...params} />}
/>,
);
}).toWarnDev([
'None of the options match with `"not a good value"`',
);
// strict mode renders twice
'None of the options match with `"not a good value"`',
'None of the options match with `"not a good value"`',
'None of the options match with `"not a good value"`',
]);
});

it('warn if groups options are not sorted', () => {
Expand All @@ -1117,21 +1112,24 @@ describe('<Autocomplete />', () => {
{ group: 2, value: 'F' },
{ group: 1, value: 'C' },
];
const { getAllByRole } = render(
<Autocomplete
{...defaultProps}
options={data}
getOptionLabel={(option) => option.value}
renderInput={(params) => <TextField {...params} autoFocus />}
groupBy={(option) => option.group}
/>,
);

const options = getAllByRole('option').map((el) => el.textContent);
expect(() => {
render(
<Autocomplete
{...defaultProps}
options={data}
getOptionLabel={(option) => option.value}
renderInput={(params) => <TextField {...params} autoFocus />}
groupBy={(option) => option.group}
/>,
);
}).toWarnDev([
// strict mode renders twice
'returns duplicated headers',
'returns duplicated headers',
]);
const options = screen.getAllByRole('option').map((el) => el.textContent);
expect(options).to.have.length(7);
expect(options).to.deep.equal(['A', 'D', 'E', 'B', 'G', 'F', 'C']);
expect(consoleWarnMock.callCount()).to.equal(2); // strict mode renders twice
expect(consoleWarnMock.messages()[0]).to.include('returns duplicated headers');
});
});

Expand Down
47 changes: 13 additions & 34 deletions packages/material-ui-lab/src/TreeView/TreeView.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import * as React from 'react';
import { expect } from 'chai';
import { spy } from 'sinon';
import { createClientRender, fireEvent, screen } from 'test/utils/createClientRender';
import { ErrorBoundary } from 'test/utils/components';
import describeConformance from '@material-ui/core/test-utils/describeConformance';
import { getClasses } from '@material-ui/core/test-utils';
import createMount from 'test/utils/createMount';
import consoleErrorMock from 'test/utils/consoleErrorMock';
import TreeView from './TreeView';
import TreeItem from '../TreeItem';

Expand All @@ -28,23 +28,16 @@ describe('<TreeView />', () => {
}));

describe('warnings', () => {
beforeEach(() => {
consoleErrorMock.spy();
});

afterEach(() => {
consoleErrorMock.reset();
});

it('should warn when switching from controlled to uncontrolled of the expanded prop', () => {
const { setProps } = render(
<TreeView expanded={[]}>
<TreeItem nodeId="1" label="one" />
</TreeView>,
);

setProps({ expanded: undefined });
expect(consoleErrorMock.messages()[0]).to.include(
expect(() => {
setProps({ expanded: undefined });
}).toErrorDev(
'Material-UI: A component is changing the controlled expanded state of TreeView to be uncontrolled.',
);
});
Expand All @@ -56,35 +49,16 @@ describe('<TreeView />', () => {
</TreeView>,
);

setProps({ selected: undefined });
expect(consoleErrorMock.messages()[0]).to.include(
expect(() => {
setProps({ selected: undefined });
}).toErrorDev(
'Material-UI: A component is changing the controlled selected state of TreeView to be uncontrolled.',
);
});

// should not throw eventually or with a better error message
// FIXME: https://github.com/mui-org/material-ui/issues/20832
it('crashes when unmounting with duplicate ids', () => {
class ErrorBoundary extends React.Component {
state = { error: null };

errors = [];

static getDerivedStateFromError(error) {
return { error };
}

componentDidCatch(error) {
this.errors.push(error);
}

render() {
if (this.state.error) {
return null;
}
return this.props.children;
}
}
const CustomTreeItem = () => {
return <TreeItem nodeId="iojerogj" />;
};
Expand Down Expand Up @@ -113,7 +87,12 @@ describe('<TreeView />', () => {
</ErrorBoundary>,
);

screen.getByRole('button').click();
expect(() => {
screen.getByRole('button').click();
}).toErrorDev([
'RangeError: Maximum call stack size exceeded',
'The above error occurred in the <ForwardRef(TreeItem)> component',
]);

const {
current: { errors },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import createMount from 'test/utils/createMount';
import StylesProvider, { StylesContext } from './StylesProvider';
import makeStyles from '../makeStyles';
import createGenerateClassName from '../createGenerateClassName';
import consoleErrorMock from 'test/utils/consoleErrorMock';

function Test() {
const options = React.useContext(StylesContext);
Expand Down Expand Up @@ -136,25 +135,16 @@ describe('StylesProvider', () => {
});

describe('warnings', () => {
beforeEach(() => {
consoleErrorMock.spy();
});

afterEach(() => {
consoleErrorMock.reset();
});

it('should support invalid input', () => {
const jss = create();
mount(
<StylesProvider injectFirst jss={jss}>
<Test />
</StylesProvider>,
);
expect(consoleErrorMock.callCount()).to.equal(1);
expect(consoleErrorMock.messages()[0]).to.include(
'Material-UI: You cannot use the jss and injectFirst props at the same time',
);

expect(() => {
mount(
<StylesProvider injectFirst jss={jss}>
<Test />
</StylesProvider>,
);
}).toErrorDev('Material-UI: You cannot use the jss and injectFirst props at the same time');
});
});
});
51 changes: 24 additions & 27 deletions packages/material-ui-styles/src/ThemeProvider/ThemeProvider.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from 'react';
import { expect } from 'chai';
import consoleErrorMock from 'test/utils/consoleErrorMock';
import { createClientRender } from 'test/utils/createClientRender';
import makeStyles from '../makeStyles';
import useTheme from '../useTheme';
Expand Down Expand Up @@ -121,37 +120,35 @@ describe('ThemeProvider', () => {
});

describe('warnings', () => {
beforeEach(() => {
consoleErrorMock.spy();
});

afterEach(() => {
consoleErrorMock.reset();
});

it('should warn about missing provider', () => {
render(
<ThemeProvider theme={(theme) => theme}>
<div />
</ThemeProvider>,
);
expect(consoleErrorMock.callCount()).to.equal(2); // strict mode renders twice
expect(consoleErrorMock.messages()[0]).to.include('However, no outer theme is present.');
expect(() => {
render(
<ThemeProvider theme={(theme) => theme}>
<div />
</ThemeProvider>,
);
}).toErrorDev([
'However, no outer theme is present.',
// strict mode renders twice
'However, no outer theme is present.',
]);
});

it('should warn about wrong theme function', () => {
render(
<ThemeProvider theme={{ bar: 'bar' }}>
<ThemeProvider theme={() => {}}>
<div />
</ThemeProvider>
,
</ThemeProvider>,
);
expect(consoleErrorMock.callCount()).to.equal(2); // strict mode renders twice
expect(consoleErrorMock.messages()[0]).to.include(
expect(() => {
render(
<ThemeProvider theme={{ bar: 'bar' }}>
<ThemeProvider theme={() => {}}>
<div />
</ThemeProvider>
,
</ThemeProvider>,
);
}).toErrorDev([
'Material-UI: You should return an object from your theme function',
);
// strict mode renders twice
'Material-UI: You should return an object from your theme function',
]);
});
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { expect } from 'chai';
import consoleErrorMock from 'test/utils/consoleErrorMock';
import createGenerateClassName from './createGenerateClassName';
import nested from '../ThemeProvider/nested';

Expand Down Expand Up @@ -125,12 +124,10 @@ describe('createGenerateClassName', () => {
before(() => {
nodeEnv = env.NODE_ENV;
env.NODE_ENV = 'production';
consoleErrorMock.spy();
});

after(() => {
env.NODE_ENV = nodeEnv;
consoleErrorMock.reset();
});

it('should output a short representation', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { expect } from 'chai';
import consoleErrorMock from 'test/utils/consoleErrorMock';
import createGenerateClassNameHash from './createGenerateClassNameHash';

describe('createGenerateClassNameHash', () => {
Expand Down Expand Up @@ -232,12 +231,10 @@ describe('createGenerateClassNameHash', () => {
before(() => {
nodeEnv = env.NODE_ENV;
env.NODE_ENV = 'production';
consoleErrorMock.spy();
});

after(() => {
env.NODE_ENV = nodeEnv;
consoleErrorMock.reset();
});

it('should output a short representation', () => {
Expand Down
Loading

0 comments on commit 45cb77f

Please sign in to comment.