Browse Source

ref(tests): Introduce React-Testing-Library (#26712)

Priscila Oliveira 3 years ago
parent
commit
e7528c51ba

+ 4 - 1
jest.config.js

@@ -54,7 +54,10 @@ module.exports = {
     '<rootDir>/tests/js/setup.js',
     'jest-canvas-mock',
   ],
-  setupFilesAfterEnv: ['<rootDir>/tests/js/setupFramework.ts'],
+  setupFilesAfterEnv: [
+    '<rootDir>/tests/js/setupFramework.ts',
+    '@testing-library/jest-dom/extend-expect',
+  ],
   testMatch: testMatch || ['<rootDir>/tests/js/**/*(*.)@(spec|test).(js|ts)?(x)'],
   testPathIgnorePatterns: ['<rootDir>/tests/sentry/lang/javascript/'],
 

+ 2 - 0
package.json

@@ -28,6 +28,8 @@
     "@sentry/rrweb": "^0.1.1",
     "@sentry/tracing": "6.7.2",
     "@sentry/utils": "6.7.2",
+    "@testing-library/jest-dom": "^5.14.1",
+    "@testing-library/react": "^12.0.0",
     "@types/color": "^3.0.0",
     "@types/create-react-class": "^15.6.2",
     "@types/diff": "5.0.0",

+ 1 - 1
static/app/components/avatar/avatarList.tsx

@@ -48,7 +48,7 @@ export default class AvatarList extends Component<Props> {
       <AvatarListWrapper className={className}>
         {!!numCollapsedUsers && (
           <Tooltip title={`${numCollapsedUsers} other ${typeMembers}`}>
-            <CollapsedUsers size={avatarSize}>
+            <CollapsedUsers size={avatarSize} data-test-id="avatarList-collapsedusers">
               {numCollapsedUsers < 99 && <Plus>+</Plus>}
               {numCollapsedUsers}
             </CollapsedUsers>

+ 2 - 2
static/app/types/index.tsx

@@ -453,9 +453,9 @@ export type AvatarUser = {
   name: string;
   username: string;
   email: string;
+  ip_address: string;
   avatarUrl?: string;
   avatar?: Avatar;
-  ip_address: string;
   // Compatibility shim with EventUser serializer
   ipAddress?: string;
   options?: {
@@ -1749,7 +1749,7 @@ export type Tag = {
   maxSuggestedValues?: number;
 };
 
-export type TagCollection = {[key: string]: Tag};
+export type TagCollection = Record<string, Tag>;
 
 export type TagValue = {
   count: number;

+ 9 - 5
static/app/views/issueList/tagFilter.tsx

@@ -62,9 +62,11 @@ class IssueListTagFilter extends React.Component<Props, State> {
   handleLoadOptions = () => {
     const {tag, tagValueLoader} = this.props;
     const {textValue} = this.state;
+
     if (tag.isInput || tag.predefined) {
       return;
     }
+
     if (!this.api) {
       return;
     }
@@ -151,6 +153,8 @@ class IssueListTagFilter extends React.Component<Props, State> {
 
   render() {
     const {tag} = this.props;
+    const {options, isLoading} = this.state;
+
     return (
       <StreamTagFilter>
         <StyledHeader>{tag.key}</StyledHeader>
@@ -167,15 +171,15 @@ class IssueListTagFilter extends React.Component<Props, State> {
         {!tag.isInput && (
           <SelectControl
             clearable
+            aria-label={tag.key}
             placeholder="--"
+            loadingMessage={() => t('Loading\u2026')}
             value={this.state.value}
             onChange={this.handleChangeSelect}
-            isLoading={this.state.isLoading}
+            isLoading={isLoading}
             onInputChange={this.handleChangeSelectInput}
             onFocus={this.handleOpenMenu}
-            noResultsText={
-              this.state.isLoading ? t('Loading\u2026') : t('No results found')
-            }
+            noResultsText={isLoading ? t('Loading\u2026') : t('No results found')}
             options={
               tag.predefined
                 ? tag.values &&
@@ -183,7 +187,7 @@ class IssueListTagFilter extends React.Component<Props, State> {
                     value,
                     label: value,
                   }))
-                : this.state.options
+                : options
             }
           />
         )}

+ 20 - 0
tests/js/sentry-test/reactTestingLibrary.tsx

@@ -0,0 +1,20 @@
+import {cache} from '@emotion/css';
+import {CacheProvider, ThemeProvider} from '@emotion/react';
+import {render, RenderOptions} from '@testing-library/react';
+
+import {lightTheme} from 'app/utils/theme';
+
+const AllTheProviders = ({children}: {children?: React.ReactNode}) => {
+  return (
+    <CacheProvider value={cache}>
+      <ThemeProvider theme={lightTheme}>{children}</ThemeProvider>
+    </CacheProvider>
+  );
+};
+
+const mountWithTheme = (ui: React.ReactElement, options?: RenderOptions) => {
+  return render(ui, {wrapper: AllTheProviders, ...options});
+};
+
+export * from '@testing-library/react';
+export {mountWithTheme};

+ 8 - 0
tests/js/setup.js

@@ -1,3 +1,4 @@
+import {configure} from '@testing-library/react';
 import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
 import Enzyme from 'enzyme'; // eslint-disable-line no-restricted-imports
 import MockDate from 'mockdate';
@@ -23,6 +24,13 @@ if (!SVGElement.prototype.getTotalLength) {
   SVGElement.prototype.getTotalLength = () => 1;
 }
 
+/**
+ * React Testing Library configuration to override the default test id attribute
+ *
+ * See: https://testing-library.com/docs/queries/bytestid/#overriding-data-testid
+ */
+configure({testIdAttribute: 'data-test-id'});
+
 /**
  * Enzyme configuration
  *

+ 0 - 30
tests/js/spec/components/avatar/avatarList.spec.jsx

@@ -1,30 +0,0 @@
-import {mountWithTheme} from 'sentry-test/enzyme';
-
-import AvatarList from 'app/components/avatar/avatarList';
-
-describe('AvatarList', function () {
-  it('renders with user avatars', function () {
-    const users = [TestStubs.User({id: '1'}), TestStubs.User({id: '2'})];
-
-    const wrapper = mountWithTheme(<AvatarList users={users} />);
-    expect(wrapper.find('UserAvatar')).toHaveLength(2);
-    expect(wrapper.find('CollapsedUsers')).toHaveLength(0);
-    expect(wrapper).toSnapshot();
-  });
-
-  it('renders with collapsed avatar count if > 5 users', function () {
-    const users = [
-      TestStubs.User({id: '1'}),
-      TestStubs.User({id: '2'}),
-      TestStubs.User({id: '3'}),
-      TestStubs.User({id: '4'}),
-      TestStubs.User({id: '5'}),
-      TestStubs.User({id: '6'}),
-    ];
-
-    const wrapper = mountWithTheme(<AvatarList users={users} />);
-    expect(wrapper.find('UserAvatar')).toHaveLength(5);
-    expect(wrapper.find('CollapsedUsers')).toHaveLength(1);
-    expect(wrapper).toSnapshot();
-  });
-});

+ 47 - 0
tests/js/spec/components/avatar/avatarList.spec.tsx

@@ -0,0 +1,47 @@
+import {mountWithTheme} from 'sentry-test/reactTestingLibrary';
+
+import AvatarList from 'app/components/avatar/avatarList';
+
+function renderComponent(avatarUsersSixUsers: AvatarList['props']['users']) {
+  return mountWithTheme(<AvatarList users={avatarUsersSixUsers} />);
+}
+
+describe('AvatarList', () => {
+  // @ts-expect-error
+  const user = TestStubs.User();
+
+  it('renders with user letter avatars', () => {
+    const users = [
+      {...user, id: '1', name: 'AB'},
+      {...user, id: '2', name: 'BC'},
+    ];
+
+    const {container, queryByTestId, getByText} = renderComponent(users);
+    expect(getByText('A')).toBeTruthy();
+    expect(getByText('B')).toBeTruthy();
+    expect(queryByTestId('avatarList-collapsedusers')).toBeNull();
+
+    expect(container).toSnapshot();
+  });
+
+  it('renders with collapsed avatar count if > 5 users', () => {
+    const users = [
+      {...user, id: '1', name: 'AB'},
+      {...user, id: '2', name: 'BC'},
+      {...user, id: '3', name: 'CD'},
+      {...user, id: '4', name: 'DE'},
+      {...user, id: '5', name: 'EF'},
+      {...user, id: '6', name: 'FG'},
+    ];
+
+    const {container, getByTestId, queryByText, queryAllByText} = renderComponent(users);
+    expect(queryAllByText(users[0].name.charAt(0))).toBeTruthy();
+    expect(queryAllByText(users[1].name.charAt(0))).toBeTruthy();
+    expect(queryAllByText(users[2].name.charAt(0))).toBeTruthy();
+    expect(queryAllByText(users[3].name.charAt(0))).toBeTruthy();
+    expect(queryAllByText(users[4].name.charAt(0))).toBeTruthy();
+    expect(queryByText(users[5].name.charAt(0))).toBeNull();
+    expect(getByTestId('avatarList-collapsedusers')).toBeTruthy();
+    expect(container).toSnapshot();
+  });
+});

+ 0 - 73
tests/js/spec/views/issueList/tagFilter.spec.jsx

@@ -1,73 +0,0 @@
-import {mountWithTheme} from 'sentry-test/enzyme';
-import {openMenu, selectByLabel} from 'sentry-test/select-new';
-
-import IssueListTagFilter from 'app/views/issueList/tagFilter';
-
-describe('IssueListTagFilter', function () {
-  let tagValueLoader;
-  let project;
-
-  beforeEach(function () {
-    MockApiClient.clearMockResponses();
-    project = TestStubs.ProjectDetails();
-
-    tagValueLoader = () =>
-      new Promise(resolve =>
-        resolve([
-          {
-            count: 0,
-            firstSeen: '2018-05-30T11:33:46.535Z',
-            key: 'browser',
-            lastSeen: '2018-05-30T11:33:46.535Z',
-            name: 'foo',
-            value: 'foo',
-          },
-        ])
-      );
-  });
-
-  it('calls API and renders options when opened', async function () {
-    const selectMock = jest.fn();
-    const tag = {key: 'browser', name: 'Browser'};
-    const wrapper = mountWithTheme(
-      <IssueListTagFilter
-        tag={tag}
-        projectId={project.slug}
-        value=""
-        onSelect={selectMock}
-        tagValueLoader={tagValueLoader}
-      />,
-      TestStubs.routerContext()
-    );
-
-    openMenu(wrapper, {control: true});
-
-    await tick();
-    wrapper.update();
-
-    selectByLabel(wrapper, 'foo', {control: true});
-    expect(selectMock).toHaveBeenCalledWith(tag, 'foo');
-  });
-
-  it('calls API and renders options when opened without project', async function () {
-    const selectMock = jest.fn();
-    const tag = {key: 'browser', name: 'Browser'};
-    const wrapper = mountWithTheme(
-      <IssueListTagFilter
-        tag={tag}
-        value=""
-        onSelect={selectMock}
-        tagValueLoader={tagValueLoader}
-      />,
-      TestStubs.routerContext()
-    );
-
-    openMenu(wrapper, {control: true});
-    await tick();
-    wrapper.update();
-
-    selectByLabel(wrapper, 'foo', {control: true});
-
-    expect(selectMock).toHaveBeenCalledWith(tag, 'foo');
-  });
-});

Some files were not shown because too many files changed in this diff