import {
ReplayRequestFrameFixture,
ReplayResourceFrameFixture,
} from 'sentry-fixture/replay/replaySpanFrameData';
import {ReplayRecordFixture} from 'sentry-fixture/replayRecord';
import {render, screen} from 'sentry-test/reactTestingLibrary';
import hydrateSpans from 'sentry/utils/replays/hydrateSpans';
import useProjectSdkNeedsUpdate from 'sentry/utils/useProjectSdkNeedsUpdate';
import NetworkDetailsContent from 'sentry/views/replays/detail/network/details/content';
import type {TabKey} from 'sentry/views/replays/detail/network/details/tabs';
jest.mock('sentry/utils/useProjectSdkNeedsUpdate');
function mockNeedsUpdate(needsUpdate: boolean) {
jest
.mocked(useProjectSdkNeedsUpdate)
.mockReturnValue({isError: false, isFetching: false, needsUpdate});
}
const [
img,
fetchNoDataObj,
fetchUrlSkipped,
fetchBodySkipped,
fetchWithHeaders,
fetchWithRespBody,
] = hydrateSpans(ReplayRecordFixture(), [
ReplayResourceFrameFixture({
op: 'resource.img',
startTimestamp: new Date(),
endTimestamp: new Date(),
description: '/static/img/logo.png',
}),
ReplayRequestFrameFixture({
op: 'resource.fetch',
startTimestamp: new Date(),
endTimestamp: new Date(),
description: '/api/0/issues/1234',
}),
ReplayRequestFrameFixture({
op: 'resource.fetch',
startTimestamp: new Date(),
endTimestamp: new Date(),
description: '/api/0/issues/1234',
data: {
method: 'GET',
statusCode: 200,
request: {_meta: {warnings: ['URL_SKIPPED']}, headers: {}},
response: {_meta: {warnings: ['URL_SKIPPED']}, headers: {}},
},
}),
ReplayRequestFrameFixture({
op: 'resource.fetch',
startTimestamp: new Date(),
endTimestamp: new Date(),
description: '/api/0/issues/1234',
data: {
method: 'GET',
statusCode: 200,
request: {
// @ts-expect-error
_meta: {warnings: ['BODY_SKIPPED']},
headers: {accept: 'application/json'},
},
response: {
// @ts-expect-error
_meta: {warnings: ['BODY_SKIPPED']},
headers: {'content-type': 'application/json'},
},
},
}),
ReplayRequestFrameFixture({
op: 'resource.fetch',
startTimestamp: new Date(),
endTimestamp: new Date(),
description: '/api/0/issues/1234',
data: {
method: 'GET',
statusCode: 200,
request: {
_meta: {},
headers: {accept: 'application/json'},
},
response: {
_meta: {},
headers: {'content-type': 'application/json'},
},
},
}),
ReplayRequestFrameFixture({
op: 'resource.fetch',
startTimestamp: new Date(),
endTimestamp: new Date(),
description: '/api/0/issues/1234',
data: {
method: 'GET',
statusCode: 200,
request: {
_meta: {},
headers: {accept: 'application/json'},
},
response: {
_meta: {},
headers: {'content-type': 'application/json'},
body: {success: true},
},
},
}),
]);
const mockItems = {
img,
fetchNoDataObj,
fetchUrlSkipped,
fetchBodySkipped,
fetchWithHeaders,
fetchWithRespBody,
};
function basicSectionProps() {
return {
projectId: '',
startTimestampMs: new Date('2023-12-24').getTime(),
};
}
function queryScreenState() {
return {
dataSectionHeaders: screen
.queryAllByLabelText('toggle section')
.map(elem => elem.textContent),
isShowingSetup: Boolean(screen.queryByTestId('network-setup-steps')),
isShowingUnsupported: Boolean(screen.queryByTestId('network-op-unsupported')),
};
}
describe('NetworkDetailsContent', () => {
mockNeedsUpdate(false);
describe('Details Tab', () => {
const visibleTab = 'details' as TabKey;
describe('Unsupported Operation', () => {
it.each([
{isSetup: false, itemName: 'img'},
{isSetup: true, itemName: 'img'},
])(
'should render the `general` & `unsupported` sections when the span is not FETCH or XHR and isSetup=$isSetup. [$itemName]',
({isSetup}) => {
render(
);
expect(queryScreenState()).toStrictEqual({
dataSectionHeaders: ['General'],
isShowingSetup: false,
isShowingUnsupported: true,
});
}
);
});
describe('Supported Operation', () => {
it.each([
{isSetup: false, itemName: 'fetchNoDataObj'},
{isSetup: false, itemName: 'fetchUrlSkipped'},
{isSetup: false, itemName: 'fetchBodySkipped'},
{isSetup: false, itemName: 'fetchWithHeaders'},
{isSetup: false, itemName: 'fetchWithRespBody'},
])(
'should render the `general` & `setup` sections when isSetup=false, no matter the item. [$itemName]',
({isSetup, itemName}) => {
render(
);
expect(queryScreenState()).toStrictEqual({
dataSectionHeaders: ['General'],
isShowingSetup: true,
isShowingUnsupported: false,
});
}
);
it.each([
{isSetup: true, itemName: 'fetchNoDataObj'},
{isSetup: true, itemName: 'fetchUrlSkipped'},
])(
'should render the `general` & `setup` sections when the item has no data. [$itemName]',
({isSetup, itemName}) => {
render(
);
expect(queryScreenState()).toStrictEqual({
dataSectionHeaders: ['General'],
isShowingSetup: true,
isShowingUnsupported: false,
});
}
);
it.each([
{isSetup: true, itemName: 'fetchBodySkipped'},
{isSetup: true, itemName: 'fetchWithHeaders'},
{isSetup: true, itemName: 'fetchWithRespBody'},
])(
'should render the `general` & two `headers` sections, and always the setup section, when things are setup and the item has some data. [$itemName]',
({isSetup, itemName}) => {
render(
);
expect(queryScreenState()).toStrictEqual({
dataSectionHeaders: ['General', 'Request Headers', 'Response Headers'],
isShowingUnsupported: false,
isShowingSetup: true,
});
}
);
});
});
describe('Request Tab', () => {
const visibleTab = 'request' as TabKey;
describe('Unsupported Operation', () => {
it.each([
{isSetup: false, itemName: 'img'},
{isSetup: true, itemName: 'img'},
])(
'should render the `query params` & `unsupported` sections when the span is not FETCH or XHR and isSetup=$isSetup. [$itemName]',
({isSetup}) => {
render(
);
expect(queryScreenState()).toStrictEqual({
dataSectionHeaders: ['Query String Parameters'],
isShowingSetup: false,
isShowingUnsupported: true,
});
}
);
});
describe('Supported Operation', () => {
it.each([
{isSetup: false, itemName: 'fetchNoDataObj'},
{isSetup: false, itemName: 'fetchUrlSkipped'},
{isSetup: false, itemName: 'fetchBodySkipped'},
{isSetup: false, itemName: 'fetchWithHeaders'},
{isSetup: false, itemName: 'fetchWithRespBody'},
])(
'should render the `query params` & `setup` sections when isSetup is false, no matter the item. [$itemName]',
({isSetup, itemName}) => {
render(
);
expect(queryScreenState()).toStrictEqual({
dataSectionHeaders: ['Query String Parameters'],
isShowingSetup: true,
isShowingUnsupported: false,
});
}
);
it.each([
{isSetup: true, itemName: 'fetchNoDataObj'},
{isSetup: true, itemName: 'fetchUrlSkipped'},
{isSetup: true, itemName: 'fetchBodySkipped'},
{isSetup: true, itemName: 'fetchWithHeaders'},
])(
'should render the `query params` & `setup` sections when the item has no data. [$itemName]',
({isSetup, itemName}) => {
render(
);
expect(queryScreenState()).toStrictEqual({
dataSectionHeaders: ['Query String Parameters'],
isShowingSetup: true,
isShowingUnsupported: false,
});
}
);
it.each([{isSetup: true, itemName: 'fetchWithRespBody'}])(
'should render the `query params` & `request payload` sections when things are setup and the item has some data. [$itemName]',
({isSetup, itemName}) => {
render(
);
expect(queryScreenState()).toStrictEqual({
dataSectionHeaders: ['Query String Parameters', 'Request BodySize: 0 B'],
isShowingUnsupported: false,
isShowingSetup: false,
});
}
);
});
});
describe('Response Tab', () => {
const visibleTab = 'response' as TabKey;
describe('Unsupported Operation', () => {
it.each([
{isSetup: false, itemName: 'img'},
{isSetup: true, itemName: 'img'},
])(
'should render the `unsupported` section when the span is not FETCH or XHR and isSetup=$isSetup. [$itemName]',
({isSetup}) => {
render(
);
expect(queryScreenState()).toStrictEqual({
dataSectionHeaders: [],
isShowingSetup: false,
isShowingUnsupported: true,
});
}
);
});
describe('Supported Operation', () => {
it.each([
{isSetup: false, itemName: 'fetchNoDataObj'},
{isSetup: false, itemName: 'fetchUrlSkipped'},
{isSetup: false, itemName: 'fetchBodySkipped'},
{isSetup: false, itemName: 'fetchWithHeaders'},
{isSetup: false, itemName: 'fetchWithRespBody'},
])(
'should render the `setup` section when isSetup is false, no matter the item. [$itemName]',
({isSetup, itemName}) => {
render(
);
expect(queryScreenState()).toStrictEqual({
dataSectionHeaders: [],
isShowingSetup: true,
isShowingUnsupported: false,
});
}
);
it.each([
{isSetup: true, itemName: 'fetchNoDataObj'},
{isSetup: true, itemName: 'fetchUrlSkipped'},
{isSetup: true, itemName: 'fetchBodySkipped'},
{isSetup: true, itemName: 'fetchWithHeaders'},
])(
'should render the `setup` section when the item has no data. [$itemName]',
({isSetup, itemName}) => {
render(
);
expect(queryScreenState()).toStrictEqual({
dataSectionHeaders: [],
isShowingSetup: true,
isShowingUnsupported: false,
});
}
);
it.each([{isSetup: true, itemName: 'fetchWithRespBody'}])(
'should render the `response body` section when things are setup and the item has some data. [$itemName]',
({isSetup, itemName}) => {
render(
);
expect(queryScreenState()).toStrictEqual({
dataSectionHeaders: ['Response BodySize: 0 B'],
isShowingUnsupported: false,
isShowingSetup: false,
});
}
);
});
});
});