123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- import {mountWithTheme} from 'sentry-test/enzyme';
- import {initializeOrg} from 'sentry-test/initializeOrg';
- import ProjectsStore from 'app/stores/projectsStore';
- import ReleasesList from 'app/views/releases/list/';
- import {DisplayOption, SortOption, StatusOption} from 'app/views/releases/list/utils';
- describe('ReleasesList', function () {
- const {organization, routerContext, router} = initializeOrg();
- const props = {
- router,
- organization,
- selection: {
- projects: [],
- datetime: {
- period: '14d',
- },
- },
- params: {orgId: organization.slug},
- location: {
- query: {
- query: 'derp',
- sort: SortOption.SESSIONS,
- healthStatsPeriod: '24h',
- somethingBad: 'XXX',
- status: StatusOption.ACTIVE,
- },
- },
- };
- let wrapper, endpointMock, sessionApiMock;
- beforeEach(async function () {
- ProjectsStore.loadInitialData(organization.projects);
- endpointMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/releases/',
- body: [
- TestStubs.Release({version: '1.0.0'}),
- TestStubs.Release({version: '1.0.1'}),
- {
- ...TestStubs.Release({version: 'af4f231ec9a8'}),
- projects: [
- {
- id: 4383604,
- name: 'Sentry-IOS-Shop',
- slug: 'sentry-ios-shop',
- hasHealthData: false,
- },
- ],
- },
- ],
- });
- sessionApiMock = MockApiClient.addMockResponse({
- url: `/organizations/org-slug/sessions/`,
- body: null,
- });
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/projects/',
- body: [],
- });
- wrapper = mountWithTheme(<ReleasesList {...props} />, routerContext);
- await tick();
- wrapper.update();
- });
- afterEach(function () {
- ProjectsStore.reset();
- MockApiClient.clearMockResponses();
- });
- it('renders list', function () {
- const items = wrapper.find('StyledPanel');
- expect(items).toHaveLength(3);
- expect(items.at(0).text()).toContain('1.0.0');
- expect(items.at(0).text()).toContain('Adoption');
- expect(items.at(1).text()).toContain('1.0.1');
- expect(items.at(1).find('CountColumn').at(1).text()).toContain('\u2014');
- expect(items.at(2).text()).toContain('af4f231ec9a8');
- expect(items.at(2).find('Header').text()).toContain('Project');
- });
- it('displays the right empty state', function () {
- let location;
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/releases/',
- body: [],
- });
- location = {query: {}};
- wrapper = mountWithTheme(
- <ReleasesList {...props} location={location} />,
- routerContext
- );
- expect(wrapper.find('StyledPanel')).toHaveLength(0);
- expect(wrapper.find('ReleasePromo').text()).toContain('Demystify Releases');
- location = {query: {statsPeriod: '30d'}};
- wrapper = mountWithTheme(
- <ReleasesList {...props} location={location} />,
- routerContext
- );
- expect(wrapper.find('StyledPanel')).toHaveLength(0);
- expect(wrapper.find('ReleasePromo').text()).toContain('Demystify Releases');
- location = {query: {query: 'abc'}};
- wrapper = mountWithTheme(
- <ReleasesList {...props} location={location} />,
- routerContext
- );
- expect(wrapper.find('EmptyMessage').text()).toEqual(
- "There are no releases that match: 'abc'."
- );
- location = {query: {sort: SortOption.SESSIONS, statsPeriod: '7d'}};
- wrapper = mountWithTheme(
- <ReleasesList {...props} location={location} />,
- routerContext
- );
- expect(wrapper.find('EmptyMessage').text()).toEqual(
- 'There are no releases with data in the last 7 days.'
- );
- location = {query: {sort: SortOption.USERS_24_HOURS, statsPeriod: '7d'}};
- wrapper = mountWithTheme(
- <ReleasesList {...props} location={location} />,
- routerContext
- );
- expect(wrapper.find('EmptyMessage').text()).toEqual(
- 'There are no releases with active user data (users in the last 24 hours).'
- );
- });
- it('searches for a release', function () {
- const input = wrapper.find('input');
- expect(endpointMock).toHaveBeenCalledWith(
- '/organizations/org-slug/releases/',
- expect.objectContaining({
- query: expect.objectContaining({query: 'derp'}),
- })
- );
- expect(input.prop('value')).toBe('derp');
- input.simulate('change', {target: {value: 'a'}}).simulate('submit');
- expect(router.push).toHaveBeenCalledWith({
- query: expect.objectContaining({query: 'a'}),
- });
- });
- it('sorts releases', function () {
- expect(endpointMock).toHaveBeenCalledWith(
- '/organizations/org-slug/releases/',
- expect.objectContaining({
- query: expect.objectContaining({
- sort: SortOption.SESSIONS,
- }),
- })
- );
- const sortDropdown = wrapper.find('ReleaseListSortOptions');
- const sortByOptions = sortDropdown.find('DropdownItem span');
- const dateCreatedOption = sortByOptions.at(0);
- expect(sortByOptions).toHaveLength(5);
- expect(dateCreatedOption.text()).toEqual('Date Created');
- const healthStatsControls = wrapper.find('CountColumn span').first();
- expect(healthStatsControls.text()).toEqual('Count');
- dateCreatedOption.simulate('click');
- expect(router.push).toHaveBeenCalledWith({
- query: expect.objectContaining({
- sort: SortOption.DATE,
- }),
- });
- });
- it('display the right Crash Free column', async function () {
- const displayDropdown = wrapper.find('ReleaseListDisplayOptions');
- const activeDisplay = displayDropdown.find('DropdownButton button');
- expect(activeDisplay.text()).toEqual('DisplaySessions');
- const displayOptions = displayDropdown.find('DropdownItem');
- expect(displayOptions).toHaveLength(2);
- const crashFreeSessionsOption = displayOptions.at(0);
- expect(crashFreeSessionsOption.props().isActive).toEqual(true);
- expect(crashFreeSessionsOption.text()).toEqual('Sessions');
- const crashFreeUsersOption = displayOptions.at(1);
- expect(crashFreeUsersOption.text()).toEqual('Users');
- expect(crashFreeUsersOption.props().isActive).toEqual(false);
- crashFreeUsersOption.find('span').simulate('click');
- expect(router.push).toHaveBeenCalledWith({
- query: expect.objectContaining({
- display: DisplayOption.USERS,
- }),
- });
- });
- it('displays archived releases', function () {
- const archivedWrapper = mountWithTheme(
- <ReleasesList {...props} location={{query: {status: StatusOption.ARCHIVED}}} />,
- routerContext
- );
- expect(endpointMock).toHaveBeenLastCalledWith(
- '/organizations/org-slug/releases/',
- expect.objectContaining({
- query: expect.objectContaining({status: StatusOption.ARCHIVED}),
- })
- );
- expect(archivedWrapper.find('ReleaseArchivedNotice').exists()).toBeTruthy();
- const statusOptions = archivedWrapper
- .find('ReleaseListStatusOptions')
- .first()
- .find('DropdownItem span');
- const statusActiveOption = statusOptions.at(0);
- const statusArchivedOption = statusOptions.at(1);
- expect(statusOptions).toHaveLength(2);
- expect(statusActiveOption.text()).toEqual('Active');
- expect(statusArchivedOption.text()).toEqual('Archived');
- statusActiveOption.simulate('click');
- expect(router.push).toHaveBeenLastCalledWith({
- query: expect.objectContaining({
- status: StatusOption.ACTIVE,
- }),
- });
- expect(wrapper.find('ReleaseArchivedNotice').exists()).toBeFalsy();
- statusArchivedOption.simulate('click');
- expect(router.push).toHaveBeenLastCalledWith({
- query: expect.objectContaining({
- status: StatusOption.ARCHIVED,
- }),
- });
- });
- it('calls api with only explicitly permitted query params', function () {
- expect(endpointMock).toHaveBeenCalledWith(
- '/organizations/org-slug/releases/',
- expect.objectContaining({
- query: expect.not.objectContaining({
- somethingBad: 'XXX',
- }),
- })
- );
- });
- it('calls session api for health data', async function () {
- expect(sessionApiMock).toHaveBeenCalledTimes(3);
- expect(sessionApiMock).toHaveBeenCalledWith(
- '/organizations/org-slug/sessions/',
- expect.objectContaining({
- query: expect.objectContaining({
- field: ['sum(session)'],
- groupBy: ['project', 'release', 'session.status'],
- interval: '1d',
- query: 'release:1.0.0 OR release:1.0.1 OR release:af4f231ec9a8',
- statsPeriod: '14d',
- }),
- })
- );
- expect(sessionApiMock).toHaveBeenCalledWith(
- '/organizations/org-slug/sessions/',
- expect.objectContaining({
- query: expect.objectContaining({
- field: ['sum(session)'],
- groupBy: ['project'],
- interval: '1h',
- query: undefined,
- statsPeriod: '24h',
- }),
- })
- );
- expect(sessionApiMock).toHaveBeenCalledWith(
- '/organizations/org-slug/sessions/',
- expect.objectContaining({
- query: expect.objectContaining({
- field: ['sum(session)'],
- groupBy: ['project', 'release'],
- interval: '1h',
- query: 'release:1.0.0 OR release:1.0.1 OR release:af4f231ec9a8',
- statsPeriod: '24h',
- }),
- })
- );
- });
- it('shows health rows only for selected projects in global header', function () {
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/releases/',
- body: [
- {
- ...TestStubs.Release({version: '2.0.0'}),
- projects: [
- {
- id: 1,
- name: 'Test',
- slug: 'test',
- },
- {
- id: 2,
- name: 'Test2',
- slug: 'test2',
- },
- {
- id: 3,
- name: 'Test3',
- slug: 'test3',
- },
- ],
- },
- ],
- });
- const healthSection = mountWithTheme(
- <ReleasesList {...props} selection={{projects: [2]}} />,
- routerContext
- ).find('ReleaseHealth');
- const hiddenProjectsMessage = healthSection.find('HiddenProjectsMessage');
- expect(hiddenProjectsMessage.text()).toBe('2 hidden projects');
- expect(hiddenProjectsMessage.find('Tooltip').prop('title')).toBe('test, test3');
- expect(healthSection.find('ProjectRow').length).toBe(1);
- expect(healthSection.find('ProjectBadge').text()).toBe('test2');
- });
- it('does not hide health rows when "All Projects" are selected in global header', function () {
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/releases/',
- body: [TestStubs.Release({version: '2.0.0'})],
- });
- const healthSection = mountWithTheme(
- <ReleasesList {...props} selection={{projects: [-1]}} />,
- routerContext
- ).find('ReleaseHealth');
- expect(healthSection.find('HiddenProjectsMessage').exists()).toBeFalsy();
- expect(healthSection.find('ProjectRow').length).toBe(1);
- });
- });
|