123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630 |
- import React from 'react';
- import {shallow} from 'enzyme';
- import Cookies from 'js-cookie';
- import _ from 'lodash';
- import {Client} from 'app/api';
- import CursorPoller from 'app/utils/cursorPoller';
- import LoadingError from 'app/components/loadingError';
- import ErrorRobot from 'app/components/errorRobot';
- import Stream from 'app/views/stream/stream';
- import EnvironmentStore from 'app/stores/environmentStore';
- import {setActiveEnvironment} from 'app/actionCreators/environments';
- import {browserHistory} from 'react-router';
- import TagStore from 'app/stores/tagStore';
- jest.mock('app/stores/groupStore');
- const DEFAULT_LINKS_HEADER =
- '<http://127.0.0.1:8000/api/0/projects/org-slug/project-slug/issues/?cursor=1443575731:0:1>; rel="previous"; results="false"; cursor="1443575731:0:1", ' +
- '<http://127.0.0.1:8000/api/0/projects/org-slug/project-slug/issues/?cursor=1443575731:0:0>; rel="next"; results="true"; cursor="1443575731:0:0';
- describe('Stream', function() {
- let sandbox;
- let context;
- let wrapper;
- let props;
- let organization;
- let team;
- let project;
- let savedSearch;
- let groupListRequest;
- beforeEach(function() {
- sandbox = sinon.sandbox.create();
- organization = TestStubs.Organization({
- id: '1337',
- slug: 'org-slug',
- });
- team = TestStubs.Team({
- id: '2448',
- });
- project = TestStubs.ProjectDetails({
- id: 3559,
- name: 'Foo Project',
- slug: 'project-slug',
- firstEvent: true,
- });
- savedSearch = {id: '789', query: 'is:unresolved', name: 'test'};
- groupListRequest = MockApiClient.addMockResponse({
- url: '/projects/org-slug/project-slug/issues/',
- body: [TestStubs.Group()],
- headers: {
- Link: DEFAULT_LINKS_HEADER,
- },
- });
- MockApiClient.addMockResponse({
- url: '/projects/org-slug/project-slug/searches/',
- body: [savedSearch],
- });
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/processingissues/',
- method: 'GET',
- });
- sandbox.stub(browserHistory, 'push');
- context = {
- project,
- organization,
- team,
- };
- TagStore.init();
- props = {
- setProjectNavSection: function() {},
- location: {query: {query: 'is:unresolved'}, search: 'query=is:unresolved'},
- params: {orgId: organization.slug, projectId: project.slug},
- tags: TagStore.getAllTags(),
- tagsLoading: false,
- };
- });
- afterEach(function() {
- sandbox.restore();
- MockApiClient.clearMockResponses();
- });
- describe('fetchData()', function() {
- describe('complete handler', function() {
- beforeEach(function() {
- wrapper = shallow(<Stream {...props} />, {
- context,
- });
- sandbox.stub(CursorPoller.prototype, 'setEndpoint');
- });
- it('should reset the poller endpoint and sets cursor URL', function() {
- const stream = wrapper.instance();
- stream.state.pageLinks = DEFAULT_LINKS_HEADER;
- stream.state.realtimeActive = true;
- stream.fetchData();
- expect(
- CursorPoller.prototype.setEndpoint.calledWith(
- 'http://127.0.0.1:8000/api/0/projects/org-slug/project-slug/issues/?cursor=1443575731:0:1'
- )
- ).toBe(true);
- });
- it('should not enable the poller if realtimeActive is false', function() {
- const stream = wrapper.instance();
- stream.state.pageLinks = DEFAULT_LINKS_HEADER;
- stream.state.realtimeActive = false;
- stream.fetchData();
- expect(CursorPoller.prototype.setEndpoint.notCalled).toBeTruthy();
- });
- it("should not enable the poller if the 'previous' link has results", function() {
- const pageLinks =
- '<http://127.0.0.1:8000/api/0/projects/org-slug/project-slug/issues/?cursor=1443575731:0:1>; rel="previous"; results="true"; cursor="1443575731:0:1", ' +
- '<http://127.0.0.1:8000/api/0/projects/org-slug/project-slug/issues/?cursor=1443575731:0:0>; rel="next"; results="true"; cursor="1443575731:0:0';
- MockApiClient.addMockResponse({
- url: '/projects/org-slug/project-slug/issues/',
- body: [TestStubs.Group()],
- headers: {
- Link: pageLinks,
- },
- });
- wrapper = shallow(<Stream {...props} />, {
- context,
- });
- const stream = wrapper.instance();
- stream.setState({
- pageLinks,
- realtimeActive: true,
- });
- stream.fetchData();
- expect(CursorPoller.prototype.setEndpoint.notCalled).toBeTruthy();
- });
- }); // complete handler
- it('calls fetchData once on mount for a saved search', async function() {
- props.location = {query: {}};
- props.params.searchId = '1';
- wrapper = shallow(<Stream {...props} />, {
- context,
- });
- await wrapper.update();
- expect(groupListRequest).toHaveBeenCalledTimes(1);
- });
- it('calls fetchData once on mount if there is a query', async function() {
- wrapper = shallow(<Stream {...props} />, {
- context,
- });
- await wrapper.update();
- expect(groupListRequest).toHaveBeenCalledTimes(1);
- });
- it('should cancel any previous, unfinished fetches', function() {
- const requestCancel = sandbox.stub();
- let requestOptions;
- sandbox.stub(Client.prototype, 'request', function(url, options) {
- requestOptions = options;
- return {
- cancel: requestCancel,
- };
- });
- // NOTE: fetchData called once after render automatically
- const stream = wrapper.instance();
- // 2nd fetch should call cancel
- stream.fetchData();
- stream.fetchData();
- expect(requestCancel.calledOnce).toBeTruthy();
- expect(stream.lastRequest).toBeTruthy();
- // when request "completes", lastRequest is cleared
- requestOptions.complete({
- getResponseHeader: () => DEFAULT_LINKS_HEADER,
- });
- expect(stream.lastRequest).toBeNull();
- });
- it('sends environment attribute', function() {
- const requestCancel = sandbox.stub();
- let requestOptions;
- sandbox.stub(Client.prototype, 'request', function(url, options) {
- requestOptions = options;
- return {
- cancel: requestCancel,
- };
- });
- const stream = wrapper.instance();
- stream.state.activeEnvironment = {name: 'prod'};
- stream.state.query = 'is:unresolved environment:prod';
- stream.fetchData();
- expect(requestOptions.data.query).toContain('environment:prod');
- expect(requestOptions.data.environment).toBe('prod');
- });
- });
- describe('fetchSavedSearches()', function() {
- it('handles valid search id', async function() {
- const streamProps = {
- setProjectNavSection: function() {},
- params: {orgId: 'org-slug', projectId: 'project-slug', searchId: '789'},
- location: {query: {}, search: ''},
- };
- wrapper = shallow(<Stream {...streamProps} />, {
- context,
- });
- await wrapper.update();
- expect(wrapper.instance().state.searchId).toBe('789');
- expect(wrapper.instance().state.query).toBe('is:unresolved');
- });
- it('handles invalid search id', async function() {
- const streamProps = {
- setProjectNavSection: function() {},
- params: {orgId: 'org-slug', projectId: 'project-slug', searchId: 'invalid'},
- location: {query: {}, search: ''},
- };
- wrapper = shallow(<Stream {...streamProps} />, {
- context,
- });
- await wrapper.update();
- expect(wrapper.instance().state.searchId).toBeNull();
- expect(wrapper.instance().state.query).toBe('');
- });
- it('handles default saved search (no search id or query)', async function() {
- const streamProps = {
- ...props,
- location: {query: {}, search: ''},
- };
- MockApiClient.addMockResponse({
- url: '/projects/org-slug/project-slug/searches/',
- body: [
- {...savedSearch, isDefault: false},
- {
- id: 'default',
- query: 'is:unresolved assigned:me',
- name: 'default',
- isDefault: true,
- },
- ],
- });
- wrapper = shallow(<Stream {...streamProps} />, {
- context,
- });
- await wrapper.update();
- expect(wrapper.instance().state.searchId).toBe('default');
- expect(wrapper.instance().state.query).toBe('is:unresolved assigned:me');
- });
- });
- describe('render()', function() {
- beforeEach(function() {
- wrapper = shallow(<Stream {...props} />, {
- context,
- });
- });
- it('displays a loading indicator when component is loading', function() {
- wrapper.setState({loading: true});
- expect(wrapper.find('.loading')).toBeTruthy();
- });
- it('displays a loading indicator when data is loading', function() {
- wrapper.setState({dataLoading: true});
- expect(wrapper.find('.loading')).toBeTruthy();
- });
- it('displays an error when component has errored', function() {
- wrapper.setState({
- error: 'Something bad happened',
- loading: false,
- dataLoading: false,
- });
- expect(wrapper.find(LoadingError).length).toBeTruthy();
- });
- it('displays the group list', function() {
- wrapper.setState({
- error: false,
- groupIds: ['1'],
- loading: false,
- dataLoading: false,
- });
- expect(wrapper).toMatchSnapshot();
- expect(wrapper.find('.ref-group-list').length).toBeTruthy();
- });
- it('displays empty with no ids', function() {
- wrapper.setState({
- error: false,
- groupIds: [],
- loading: false,
- dataLoading: false,
- });
- expect(wrapper.find('EmptyStateWarning').length).toBeTruthy();
- });
- describe('no first event sent', function() {
- it('shows "awaiting events" message when no events have been sent', function() {
- context.project.firstEvent = false;
- wrapper.setState({
- error: false,
- groupIds: [],
- loading: false,
- dataLoading: false,
- });
- expect(wrapper.find(ErrorRobot)).toHaveLength(1);
- });
- it('does not show "awaiting events" when an event is recieved', function() {
- context.project.firstEvent = false;
- wrapper.setState({
- error: false,
- groupIds: ['1'],
- loading: false,
- dataLoading: false,
- });
- expect(wrapper.find('.ref-group-list').length).toBeTruthy();
- });
- });
- it('does not have real time event updates when events exist', function() {
- wrapper = shallow(<Stream {...wrapper.instance().props} />, {
- context: {
- ...context,
- project: {
- ...context.project,
- firstEvent: true,
- },
- },
- });
- expect(wrapper.state('realtimeActive')).toBe(false);
- });
- it('does not have real time event updates enabled when cookie is present (even if there are no events)', function() {
- Cookies.set('realtimeActive', 'false');
- wrapper = shallow(<Stream {...wrapper.instance().props} />, {
- context: {
- ...context,
- project: {
- ...context.project,
- firstEvent: false,
- },
- },
- });
- wrapper.setState({
- error: false,
- groupIds: [],
- loading: false,
- dataLoading: false,
- });
- Cookies.remove('realtimeActive');
- expect(wrapper.state('realtimeActive')).toBe(false);
- });
- it('has real time event updates enabled when there are no events', function() {
- wrapper = shallow(<Stream {...wrapper.instance().props} />, {
- context: {
- ...context,
- project: {
- ...context.project,
- firstEvent: false,
- },
- },
- });
- wrapper.setState({
- error: false,
- groupIds: [],
- loading: false,
- dataLoading: false,
- });
- expect(wrapper.state('realtimeActive')).toBe(true);
- });
- });
- describe('toggles environment', function() {
- beforeEach(function() {
- wrapper = shallow(<Stream {...props} />, {
- context,
- });
- });
- it('select all environments', function() {
- EnvironmentStore.loadInitialData(TestStubs.Environments());
- setActiveEnvironment(null);
- wrapper.setState({
- error: false,
- groupIds: ['1'],
- loading: false,
- dataLoading: false,
- });
- expect(wrapper).toMatchSnapshot();
- });
- });
- describe('componentWillMount()', function() {
- afterEach(function() {
- Cookies.remove('realtimeActive');
- });
- it('reads the realtimeActive state from a cookie', function() {
- Cookies.set('realtimeActive', 'false');
- const stream = wrapper.instance();
- expect(stream.getInitialState()).toHaveProperty('realtimeActive', false);
- });
- it('reads the true realtimeActive state from a cookie', function() {
- Cookies.set('realtimeActive', 'true');
- const stream = wrapper.instance();
- expect(stream.getInitialState()).toHaveProperty('realtimeActive', true);
- });
- });
- describe('onRealtimeChange', function() {
- it('sets the realtimeActive state', function() {
- const stream = wrapper.instance();
- stream.state.realtimeActive = false;
- stream.onRealtimeChange(true);
- expect(stream.state.realtimeActive).toEqual(true);
- expect(Cookies.get('realtimeActive')).toEqual('true');
- stream.onRealtimeChange(false);
- expect(stream.state.realtimeActive).toEqual(false);
- expect(Cookies.get('realtimeActive')).toEqual('false');
- });
- });
- describe('getInitialState', function() {
- it('handles query', function() {
- const expected = {
- groupIds: [],
- selectAllActive: false,
- multiSelected: false,
- anySelected: false,
- statsPeriod: '24h',
- realtimeActive: false,
- pageLinks: '',
- loading: false,
- dataLoading: true,
- error: false,
- searchId: null,
- query: 'is:unresolved',
- sort: 'date',
- };
- const actual = wrapper.instance().getInitialState();
- expect(_.pick(actual, _.keys(expected))).toEqual(expected);
- });
- it('handles no searchId or query', async function() {
- const streamProps = {
- ...props,
- location: {query: {sort: 'freq'}, search: 'sort=freq'},
- };
- const expected = {
- groupIds: [],
- selectAllActive: false,
- multiSelected: false,
- anySelected: false,
- statsPeriod: '24h',
- realtimeActive: false,
- loading: false,
- dataLoading: false,
- error: false,
- query: '',
- sort: 'freq',
- searchId: null,
- };
- wrapper = shallow(<Stream {...streamProps} />, {
- context,
- });
- await wrapper.update();
- const stream = wrapper.instance();
- const actual = stream.state;
- expect(_.pick(actual, _.keys(expected))).toEqual(expected);
- });
- it('handles valid searchId in routing params', async function() {
- const streamProps = {
- ...props,
- location: {query: {sort: 'freq'}, search: 'sort=freq'},
- params: {orgId: 'org-slug', projectId: 'project-slug', searchId: '789'},
- };
- const expected = {
- groupIds: [],
- selectAllActive: false,
- multiSelected: false,
- anySelected: false,
- statsPeriod: '24h',
- realtimeActive: false,
- loading: false,
- dataLoading: false,
- error: false,
- query: 'is:unresolved',
- sort: 'freq',
- searchId: '789',
- };
- wrapper = shallow(<Stream {...streamProps} />, {
- context,
- });
- wrapper.setState({
- savedSearchList: [{id: '789', query: 'is:unresolved', name: 'test'}],
- });
- await wrapper.update();
- const actual = wrapper.instance().state;
- expect(_.pick(actual, _.keys(expected))).toEqual(expected);
- });
- it('handles invalid searchId in routing params', async function() {
- const streamProps = {
- ...props,
- location: {query: {sort: 'freq'}, search: 'sort=freq'},
- params: {orgId: 'org-slug', projectId: 'project-slug', searchId: '799'},
- };
- const expected = {
- groupIds: [],
- selectAllActive: false,
- multiSelected: false,
- anySelected: false,
- statsPeriod: '24h',
- realtimeActive: false,
- loading: false,
- dataLoading: false,
- error: false,
- query: '',
- sort: 'freq',
- searchId: null,
- };
- wrapper = shallow(<Stream {...streamProps} />, {
- context,
- });
- await wrapper.update();
- const stream = wrapper.instance();
- const actual = stream.state;
- expect(_.pick(actual, _.keys(expected))).toEqual(expected);
- });
- });
- describe('getQueryState', function() {
- it('handles changed search id', async function() {
- const nextProps = {
- ...props,
- location: {
- pathname: '/org-slug/project-slug/searches/789/',
- },
- params: {orgId: 'org-slug', projectId: 'project-slug', searchId: '789'},
- };
- wrapper = shallow(<Stream {...props} />, {
- context,
- });
- await wrapper.update();
- const stream = wrapper.instance();
- const nextState = stream.getQueryState(nextProps);
- expect(nextState).toEqual(
- expect.objectContaining({searchId: '789', query: 'is:unresolved'})
- );
- });
- it('handles changed querystring', function() {
- const nextProps = {
- ...props,
- location: {
- query: {
- query: 'is:unresolved assigned:me',
- },
- },
- };
- const stream = shallow(<Stream {...props} />, {
- context,
- }).instance();
- const nextState = stream.getQueryState(nextProps);
- expect(nextState).toEqual(
- expect.objectContaining({searchId: null, query: 'is:unresolved assigned:me'})
- );
- });
- });
- });
|