123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 |
- import {ConfigFixture} from 'sentry-fixture/config';
- import {UserFixture} from 'sentry-fixture/user';
- import {initializeOrg} from 'sentry-test/initializeOrg';
- import {act, render, screen, waitFor} from 'sentry-test/reactTestingLibrary';
- import ConfigStore from 'sentry/stores/configStore';
- import ProjectsStore from 'sentry/stores/projectsStore';
- import EventView from 'sentry/utils/discover/eventView';
- import {getFieldRenderer} from 'sentry/utils/discover/fieldRenderers';
- import {SPAN_OP_RELATIVE_BREAKDOWN_FIELD} from 'sentry/utils/discover/fields';
- describe('getFieldRenderer', function () {
- let location, context, project, organization, data, user;
- beforeEach(function () {
- context = initializeOrg();
- organization = context.organization;
- project = context.project;
- act(() => ProjectsStore.loadInitialData([project]));
- user = 'email:text@example.com';
- location = {
- pathname: '/events',
- query: {},
- };
- data = {
- id: '1',
- team_key_transaction: 1,
- title: 'ValueError: something bad',
- transaction: 'api.do_things',
- boolValue: 1,
- numeric: 1.23,
- createdAt: new Date(2019, 9, 3, 12, 13, 14),
- url: '/example',
- project: project.slug,
- release: 'F2520C43515BD1F0E8A6BD46233324641A370BF6',
- issue: 'SENTRY-T6P',
- user,
- 'span_ops_breakdown.relative': '',
- 'spans.browser': 10,
- 'spans.db': 30,
- 'spans.http': 15,
- 'spans.resource': 20,
- 'spans.total.time': 75,
- 'transaction.duration': 75,
- 'timestamp.to_day': '2021-09-05T00:00:00+00:00',
- 'issue.id': '123214',
- 'http_response_rate(3)': 0.012,
- 'http_response_rate(5)': 0.000021,
- lifetimeCount: 10000,
- filteredCount: 3000,
- count: 6000,
- selectionDateString: 'last 7 days',
- };
- MockApiClient.addMockResponse({
- url: `/organizations/${organization.slug}/projects/${project.slug}/`,
- body: project,
- });
- MockApiClient.addMockResponse({
- url: `/organizations/${organization.slug}/key-transactions/`,
- method: 'POST',
- });
- MockApiClient.addMockResponse({
- url: `/organizations/${organization.slug}/key-transactions/`,
- method: 'DELETE',
- });
- MockApiClient.addMockResponse({
- url: `/organizations/${organization.slug}/projects/`,
- body: [project],
- });
- });
- it('can render string fields', function () {
- const renderer = getFieldRenderer('url', {url: 'string'});
- render(renderer(data, {location, organization}) as React.ReactElement<any, any>);
- expect(screen.getByText(data.url)).toBeInTheDocument();
- });
- it('can render empty string fields', function () {
- const renderer = getFieldRenderer('url', {url: 'string'});
- data.url = '';
- render(renderer(data, {location, organization}) as React.ReactElement<any, any>);
- expect(screen.getByText('(empty string)')).toBeInTheDocument();
- });
- it('can render boolean fields', function () {
- const renderer = getFieldRenderer('boolValue', {boolValue: 'boolean'});
- render(renderer(data, {location, organization}) as React.ReactElement<any, any>);
- expect(screen.getByText('true')).toBeInTheDocument();
- });
- it('can render integer fields', function () {
- const renderer = getFieldRenderer('numeric', {numeric: 'integer'});
- render(renderer(data, {location, organization}) as React.ReactElement<any, any>);
- expect(screen.getByText(data.numeric)).toBeInTheDocument();
- });
- describe('percentage', function () {
- it('can render percentage fields', function () {
- const renderer = getFieldRenderer(
- 'http_response_rate(3)',
- {
- 'http_response_rate(3)': 'percentage',
- },
- false
- );
- render(renderer(data, {location, organization}) as React.ReactElement<any, any>);
- expect(screen.getByText('1.2%')).toBeInTheDocument();
- });
- it('can render very small percentages', function () {
- const renderer = getFieldRenderer(
- 'http_response_rate(5)',
- {
- 'http_response_rate(5)': 'percentage',
- },
- false
- );
- render(renderer(data, {location, organization}) as React.ReactElement<any, any>);
- expect(screen.getByText('<0.01%')).toBeInTheDocument();
- });
- });
- describe('date', function () {
- beforeEach(function () {
- ConfigStore.loadInitialData(
- ConfigFixture({
- user: UserFixture({
- options: {
- ...UserFixture().options,
- timezone: 'America/Los_Angeles',
- },
- }),
- })
- );
- });
- it('can render date fields', async function () {
- const renderer = getFieldRenderer('createdAt', {createdAt: 'date'});
- render(renderer(data, {location, organization}) as React.ReactElement<any, any>);
- await waitFor(() =>
- expect(screen.getByText('Oct 3, 2019 9:13:14 AM PDT')).toBeInTheDocument()
- );
- });
- it('can render date fields using utc when query string has utc set to true', async function () {
- const renderer = getFieldRenderer('createdAt', {createdAt: 'date'});
- render(
- renderer(data, {
- location: {...location, query: {utc: 'true'}},
- organization,
- }) as React.ReactElement<any, any>
- );
- await waitFor(() =>
- expect(screen.getByText('Oct 3, 2019 4:13:14 PM UTC')).toBeInTheDocument()
- );
- });
- });
- it('can render null date fields', function () {
- const renderer = getFieldRenderer('nope', {nope: 'date'});
- render(renderer(data, {location, organization}) as React.ReactElement<any, any>);
- expect(screen.getByText('(no value)')).toBeInTheDocument();
- });
- it('can render timestamp.to_day', function () {
- // Set timezone
- ConfigStore.loadInitialData(
- ConfigFixture({
- user: UserFixture({
- options: {
- ...UserFixture().options,
- timezone: 'America/Los_Angeles',
- },
- }),
- })
- );
- const renderer = getFieldRenderer('timestamp.to_day', {'timestamp.to_day': 'date'});
- render(renderer(data, {location, organization}) as React.ReactElement<any, any>);
- expect(screen.getByText('Sep 5, 2021')).toBeInTheDocument();
- });
- it('can render error.handled values', function () {
- const renderer = getFieldRenderer('error.handled', {'error.handled': 'boolean'});
- function validate(value, expectText) {
- const {unmount} = render(
- renderer(
- {'error.handled': value},
- {location, organization}
- ) as React.ReactElement<any, any>
- );
- expect(screen.getByText(expectText)).toBeInTheDocument();
- unmount();
- }
- // Should render the same as the filter.
- // ie. all 1 or null
- validate([0, 1], 'false');
- validate([1, 0], 'false');
- validate([null, 0], 'false');
- validate([0, null], 'false');
- validate([null, 1], 'true');
- validate([1, null], 'true');
- // null = true for error.handled data.
- validate([null], 'true');
- // Default events won't have error.handled and will return an empty list.
- validate([], '(no value)');
- // Transactions will have null for error.handled as the 'tag' won't be set.
- validate(null, '(no value)');
- });
- it('can render user fields with aliased user', function () {
- const renderer = getFieldRenderer('user', {user: 'string'});
- render(renderer(data, {location, organization}) as React.ReactElement<any, any>);
- expect(screen.getByTestId('letter_avatar-avatar')).toBeInTheDocument();
- expect(screen.getByText('text@example.com')).toBeInTheDocument();
- });
- it('can render null user fields', function () {
- const renderer = getFieldRenderer('user', {user: 'string'});
- delete data.user;
- render(renderer(data, {location, organization}) as React.ReactElement<any, any>);
- expect(screen.queryByTestId('letter_avatar-avatar')).not.toBeInTheDocument();
- expect(screen.getByText('(no value)')).toBeInTheDocument();
- });
- it('can render null release fields', function () {
- const renderer = getFieldRenderer('release', {release: 'string'});
- delete data.release;
- render(renderer(data, {location, organization}) as React.ReactElement<any, any>);
- expect(screen.getByText('(no value)')).toBeInTheDocument();
- });
- it('renders release version with hyperlink', function () {
- const renderer = getFieldRenderer('release', {release: 'string'});
- render(renderer(data, {location, organization}) as React.ReactElement<any, any>, {
- router: context.router,
- });
- expect(screen.queryByRole('link')).toHaveAttribute(
- 'href',
- `/organizations/org-slug/releases/F2520C43515BD1F0E8A6BD46233324641A370BF6/`
- );
- expect(screen.getByText('F2520C43515B')).toBeInTheDocument();
- });
- it('renders issue hyperlink', function () {
- const renderer = getFieldRenderer('issue', {issue: 'string'});
- render(renderer(data, {location, organization}) as React.ReactElement<any, any>, {
- router: context.router,
- });
- expect(screen.queryByRole('link')).toHaveAttribute(
- 'href',
- `/organizations/org-slug/issues/123214/`
- );
- expect(screen.getByText('SENTRY-T6P')).toBeInTheDocument();
- });
- it('can render project as an avatar', function () {
- const renderer = getFieldRenderer('project', {project: 'string'});
- render(renderer(data, {location, organization}) as React.ReactElement<any, any>, {
- router: context.router,
- });
- expect(screen.queryByTestId('letter_avatar-avatar')).not.toBeInTheDocument();
- expect(screen.getByText(project.slug)).toBeInTheDocument();
- });
- it('can render project id as an avatar', function () {
- const renderer = getFieldRenderer('project', {project: 'number'});
- data = {...data, project: parseInt(project.id, 10)};
- render(renderer(data, {location, organization}) as React.ReactElement<any, any>, {
- router: context.router,
- });
- expect(screen.queryByTestId('letter_avatar-avatar')).not.toBeInTheDocument();
- expect(screen.getByText(project.slug)).toBeInTheDocument();
- });
- it('can render team key transaction as a star with the dropdown', async function () {
- const renderer = getFieldRenderer('team_key_transaction', {
- team_key_transaction: 'boolean',
- });
- render(renderer(data, {location, organization}) as React.ReactElement<any, any>, {
- router: context.router,
- });
- const star = screen.getByRole('button', {name: 'Toggle star for team'});
- // Enabled, can't open the menu in the test without setting up the
- // TeamKeyTransactionManager
- await waitFor(() => expect(star).toBeEnabled());
- });
- it('can render team key transaction as a star without the dropdown', function () {
- const renderer = getFieldRenderer('team_key_transaction', {
- team_key_transaction: 'boolean',
- });
- delete data.project;
- render(renderer(data, {location, organization}) as React.ReactElement<any, any>, {
- router: context.router,
- });
- const star = screen.getByRole('button', {name: 'Toggle star for team'});
- // Not enabled without a project
- expect(star).toBeDisabled();
- });
- describe('ops breakdown', () => {
- const getWidths = () =>
- Array.from(screen.getByTestId('relative-ops-breakdown').children).map(
- node => (node as HTMLElement).style.width
- );
- it('can render operation breakdowns', function () {
- const renderer = getFieldRenderer(SPAN_OP_RELATIVE_BREAKDOWN_FIELD, {
- [SPAN_OP_RELATIVE_BREAKDOWN_FIELD]: 'string',
- });
- render(renderer(data, {location, organization}) as React.ReactElement<any, any>, {
- router: context.router,
- });
- expect(getWidths()).toEqual(['13.333%', '40.000%', '20.000%', '26.667%', '0.000%']);
- });
- it('renders operation breakdowns in sorted order when a sort field is provided', function () {
- const renderer = getFieldRenderer(SPAN_OP_RELATIVE_BREAKDOWN_FIELD, {
- [SPAN_OP_RELATIVE_BREAKDOWN_FIELD]: 'string',
- });
- render(
- renderer(data, {
- location,
- organization,
- eventView: new EventView({
- sorts: [{field: 'spans.db', kind: 'desc'}],
- createdBy: UserFixture(),
- display: undefined,
- end: undefined,
- start: undefined,
- id: undefined,
- name: undefined,
- project: [],
- query: '',
- statsPeriod: undefined,
- environment: [],
- fields: [{field: 'spans.db'}],
- team: [],
- topEvents: undefined,
- }),
- }) as React.ReactElement<any, any>,
- {router: context.router}
- );
- expect(getWidths()).toEqual(['40.000%', '13.333%', '20.000%', '26.667%', '0.000%']);
- });
- });
- });
|