123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692 |
- import {act, render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
- import ProjectActions from 'sentry/actions/projectActions';
- import ProjectsStore from 'sentry/stores/projectsStore';
- import Projects from 'sentry/utils/projects';
- describe('utils.projects', function () {
- const renderer = jest.fn(() => null);
- const createWrapper = props =>
- render(<Projects orgId="org-slug" children={renderer} {...props} />); // eslint-disable-line
- beforeEach(function () {
- renderer.mockClear();
- MockApiClient.clearMockResponses();
- act(() =>
- ProjectsStore.loadInitialData([
- TestStubs.Project({id: '1', slug: 'foo'}),
- TestStubs.Project({id: '2', slug: 'bar'}),
- ])
- );
- });
- afterEach(async function () {
- act(() => ProjectsStore.loadInitialData([]));
- await tick();
- });
- describe('with predefined list of slugs', function () {
- it('gets projects that are in the ProjectsStore', function () {
- createWrapper({slugs: ['foo', 'bar']});
- // This is initial state
- expect(renderer).toHaveBeenCalledWith(
- expect.objectContaining({
- fetching: false,
- isIncomplete: null,
- hasMore: null,
- projects: [
- expect.objectContaining({
- id: '1',
- slug: 'foo',
- }),
- expect.objectContaining({
- id: '2',
- slug: 'bar',
- }),
- ],
- })
- );
- });
- it('fetches projects from API if not found in store', async function () {
- const request = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/projects/',
- query: {
- query: 'slug:a slug:b',
- },
- body: [
- TestStubs.Project({
- id: '100',
- slug: 'a',
- }),
- TestStubs.Project({
- id: '101',
- slug: 'b',
- }),
- ],
- });
- createWrapper({slugs: ['foo', 'a', 'b']});
- // This is initial state
- expect(renderer).toHaveBeenCalledWith(
- expect.objectContaining({
- fetching: true,
- isIncomplete: null,
- hasMore: null,
- projects: [
- {slug: 'a'},
- {slug: 'b'},
- expect.objectContaining({
- id: '1',
- slug: 'foo',
- }),
- ],
- })
- );
- await waitFor(() =>
- expect(request).toHaveBeenCalledWith(
- expect.anything(),
- expect.objectContaining({
- query: {
- query: 'slug:a slug:b',
- collapse: ['latestDeploys'],
- },
- })
- )
- );
- expect(renderer).toHaveBeenCalledWith(
- expect.objectContaining({
- fetching: false,
- isIncomplete: false,
- hasMore: null,
- projects: [
- expect.objectContaining({
- id: '100',
- slug: 'a',
- }),
- expect.objectContaining({
- id: '101',
- slug: 'b',
- }),
- expect.objectContaining({
- id: '1',
- slug: 'foo',
- }),
- ],
- })
- );
- });
- it('only has partial results from API', async function () {
- const request = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/projects/',
- body: [
- TestStubs.Project({
- id: '100',
- slug: 'a',
- }),
- ],
- });
- createWrapper({slugs: ['foo', 'a', 'b']});
- // This is initial state
- expect(renderer).toHaveBeenCalledWith(
- expect.objectContaining({
- fetching: true,
- isIncomplete: null,
- hasMore: null,
- projects: [
- {slug: 'a'},
- {slug: 'b'},
- expect.objectContaining({
- id: '1',
- slug: 'foo',
- }),
- ],
- })
- );
- await waitFor(() =>
- expect(request).toHaveBeenCalledWith(
- expect.anything(),
- expect.objectContaining({
- query: {
- query: 'slug:a slug:b',
- collapse: ['latestDeploys'],
- },
- })
- )
- );
- expect(renderer).toHaveBeenCalledWith(
- expect.objectContaining({
- fetching: false,
- isIncomplete: true,
- hasMore: null,
- projects: [
- expect.objectContaining({
- id: '100',
- slug: 'a',
- }),
- {
- slug: 'b',
- },
- expect.objectContaining({
- id: '1',
- slug: 'foo',
- }),
- ],
- })
- );
- });
- it('responds to updated projects from the project store', async function () {
- createWrapper({slugs: ['foo', 'bar']});
- await waitFor(() =>
- expect(renderer).toHaveBeenCalledWith(
- expect.objectContaining({
- fetching: false,
- isIncomplete: null,
- hasMore: null,
- projects: [
- expect.objectContaining({
- id: '1',
- slug: 'foo',
- }),
- expect.objectContaining({
- id: '2',
- slug: 'bar',
- }),
- ],
- })
- )
- );
- const newTeam = TestStubs.Team();
- act(() => ProjectActions.addTeamSuccess(newTeam, 'foo'));
- await waitFor(() =>
- expect(renderer).toHaveBeenCalledWith(
- expect.objectContaining({
- fetching: false,
- isIncomplete: null,
- hasMore: null,
- projects: [
- expect.objectContaining({
- id: '1',
- slug: 'foo',
- teams: [newTeam],
- }),
- expect.objectContaining({
- id: '2',
- slug: 'bar',
- }),
- ],
- })
- )
- );
- });
- });
- describe('with predefined list of project ids', function () {
- it('gets project ids that are in the ProjectsStore', function () {
- createWrapper({projectIds: [1, 2]});
- // This is initial state
- expect(renderer).toHaveBeenCalledWith(
- expect.objectContaining({
- fetching: false,
- isIncomplete: null,
- hasMore: null,
- projects: [
- expect.objectContaining({
- id: '1',
- slug: 'foo',
- }),
- expect.objectContaining({
- id: '2',
- slug: 'bar',
- }),
- ],
- })
- );
- });
- it('fetches projects from API if ids not found in store', async function () {
- const request = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/projects/',
- query: {
- all_projects: '1',
- collapse: ['latestDeploys'],
- },
- body: [
- TestStubs.Project({
- id: '1',
- slug: 'foo',
- }),
- TestStubs.Project({
- id: '100',
- slug: 'a',
- }),
- TestStubs.Project({
- id: '101',
- slug: 'b',
- }),
- ],
- });
- createWrapper({projectIds: [1, 100, 101]});
- // This is initial state
- expect(renderer).toHaveBeenCalledWith(
- expect.objectContaining({
- fetching: true,
- isIncomplete: null,
- hasMore: null,
- projects: [],
- })
- );
- await waitFor(() =>
- expect(request).toHaveBeenCalledWith(
- expect.anything(),
- expect.objectContaining({
- query: {
- collapse: ['latestDeploys'],
- },
- })
- )
- );
- expect(renderer).toHaveBeenCalledWith(
- expect.objectContaining({
- fetching: false,
- isIncomplete: null,
- hasMore: false,
- projects: [
- expect.objectContaining({
- id: '1',
- slug: 'foo',
- }),
- expect.objectContaining({
- id: '100',
- slug: 'a',
- }),
- expect.objectContaining({
- id: '101',
- slug: 'b',
- }),
- ],
- })
- );
- });
- });
- describe('with no pre-defined projects', function () {
- let request;
- beforeEach(async function () {
- request = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/projects/',
- body: [
- TestStubs.Project({
- id: '100',
- slug: 'a',
- }),
- TestStubs.Project({
- id: '101',
- slug: 'b',
- }),
- ],
- headers: {
- Link:
- '<http://127.0.0.1:8000/api/0/organizations/org-slug/projects/?cursor=1443575731:0:1>; rel="previous"; results="true"; cursor="1443575731:0:1", ' +
- '<http://127.0.0.1:8000/api/0/organizations/org-slug/projects/?cursor=1443575731:0:0>; rel="next"; results="true"; cursor="1443575731:0:0',
- },
- });
- act(() => ProjectsStore.loadInitialData([]));
- await tick();
- });
- it('fetches projects from API', async function () {
- createWrapper();
- // This is initial state
- expect(renderer).toHaveBeenCalledWith(
- expect.objectContaining({
- fetching: true,
- isIncomplete: null,
- hasMore: null,
- projects: [],
- })
- );
- await waitFor(() =>
- expect(request).toHaveBeenCalledWith(
- expect.anything(),
- expect.objectContaining({
- query: {
- collapse: ['latestDeploys'],
- },
- })
- )
- );
- expect(renderer).toHaveBeenCalledWith(
- expect.objectContaining({
- fetching: false,
- isIncomplete: null,
- hasMore: true,
- projects: [
- expect.objectContaining({
- id: '100',
- slug: 'a',
- }),
- expect.objectContaining({
- id: '101',
- slug: 'b',
- }),
- ],
- })
- );
- });
- it('queries API for more projects and replaces results', async function () {
- const myRenderer = jest.fn(({onSearch}) => (
- <input onChange={({target}) => onSearch(target.value)} />
- ));
- createWrapper({children: myRenderer});
- // This is initial state
- expect(myRenderer).toHaveBeenCalledWith(
- expect.objectContaining({
- fetching: true,
- isIncomplete: null,
- hasMore: null,
- projects: [],
- })
- );
- request.mockClear();
- request = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/projects/',
- body: [
- TestStubs.Project({
- id: '102',
- slug: 'test1',
- }),
- TestStubs.Project({
- id: '103',
- slug: 'test2',
- }),
- ],
- });
- userEvent.type(screen.getByRole('textbox'), 'test');
- expect(request).toHaveBeenCalledWith(
- expect.anything(),
- expect.objectContaining({
- query: {
- query: 'test',
- collapse: ['latestDeploys'],
- },
- })
- );
- await waitFor(() =>
- expect(myRenderer).toHaveBeenLastCalledWith(
- expect.objectContaining({
- fetching: false,
- isIncomplete: null,
- hasMore: false,
- projects: [
- expect.objectContaining({
- id: '102',
- slug: 'test1',
- }),
- expect.objectContaining({
- id: '103',
- slug: 'test2',
- }),
- ],
- })
- )
- );
- });
- it('queries API for more projects and appends results', async function () {
- const myRenderer = jest.fn(({onSearch}) => (
- <input onChange={({target}) => onSearch(target.value, {append: true})} />
- ));
- createWrapper({children: myRenderer});
- request.mockClear();
- request = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/projects/',
- body: [
- TestStubs.Project({
- id: '102',
- slug: 'test1',
- }),
- TestStubs.Project({
- id: '103',
- slug: 'test2',
- }),
- ],
- });
- userEvent.type(screen.getByRole('textbox'), 'test');
- expect(request).toHaveBeenCalledWith(
- expect.anything(),
- expect.objectContaining({
- query: {
- query: 'test',
- collapse: ['latestDeploys'],
- },
- })
- );
- await waitFor(() =>
- expect(myRenderer).toHaveBeenLastCalledWith(
- expect.objectContaining({
- fetching: false,
- isIncomplete: null,
- hasMore: false,
- projects: [
- expect.objectContaining({
- id: '100',
- slug: 'a',
- }),
- expect.objectContaining({
- id: '101',
- slug: 'b',
- }),
- expect.objectContaining({
- id: '102',
- slug: 'test1',
- }),
- expect.objectContaining({
- id: '103',
- slug: 'test2',
- }),
- ],
- })
- )
- );
- // Should not have duplicates
- userEvent.type(screen.getByRole('textbox'), 'test');
- await waitFor(() =>
- expect(myRenderer).toHaveBeenLastCalledWith(
- expect.objectContaining({
- projects: [
- expect.objectContaining({
- id: '100',
- slug: 'a',
- }),
- expect.objectContaining({
- id: '101',
- slug: 'b',
- }),
- expect.objectContaining({
- id: '102',
- slug: 'test1',
- }),
- expect.objectContaining({
- id: '103',
- slug: 'test2',
- }),
- ],
- })
- )
- );
- });
- });
- describe('with all projects prop', function () {
- const loadProjects = jest.spyOn(ProjectActions, 'loadProjects');
- let mockProjects;
- let request;
- beforeEach(function () {
- mockProjects = [
- TestStubs.Project({
- id: '100',
- slug: 'a',
- }),
- TestStubs.Project({
- id: '101',
- slug: 'b',
- }),
- TestStubs.Project({
- id: '102',
- slug: 'c',
- }),
- ];
- request = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/projects/',
- query: {
- all_projects: '1',
- collapse: ['latestDeploys'],
- },
- body: mockProjects,
- });
- loadProjects.mockReset();
- act(() => ProjectsStore.reset());
- });
- it('can query for a list of all projects and save it to the store', async function () {
- createWrapper({allProjects: true});
- // This is initial state
- expect(renderer).toHaveBeenCalledWith(
- expect.objectContaining({
- fetching: true,
- isIncomplete: null,
- hasMore: null,
- projects: [],
- })
- );
- // wait for request to resolve
- await waitFor(() =>
- expect(request).toHaveBeenCalledWith(
- expect.anything(),
- expect.objectContaining({
- query: {all_projects: 1, collapse: ['latestDeploys']},
- })
- )
- );
- expect(renderer).toHaveBeenCalledWith(
- expect.objectContaining({
- fetching: false,
- isIncomplete: null,
- hasMore: false,
- projects: mockProjects,
- })
- );
- // expect the store action to be called
- expect(loadProjects).toHaveBeenCalledWith(mockProjects);
- });
- it('does not refetch projects that are already loaded in the store', async function () {
- act(() => ProjectsStore.loadInitialData(mockProjects));
- createWrapper({allProjects: true});
- await waitFor(() =>
- expect(renderer).toHaveBeenCalledWith(
- expect.objectContaining({
- fetching: false,
- isIncomplete: null,
- hasMore: false,
- projects: mockProjects,
- })
- )
- );
- expect(request).not.toHaveBeenCalled();
- expect(loadProjects).not.toHaveBeenCalled();
- });
- it('responds to updated projects from the project store', async function () {
- act(() => ProjectsStore.loadInitialData(mockProjects));
- createWrapper({allProjects: true});
- await waitFor(() =>
- expect(renderer).toHaveBeenCalledWith(
- expect.objectContaining({
- fetching: false,
- isIncomplete: null,
- hasMore: false,
- projects: mockProjects,
- })
- )
- );
- const newTeam = TestStubs.Team();
- act(() => ProjectActions.addTeamSuccess(newTeam, 'a'));
- // Expect new team information to be available
- await waitFor(() =>
- expect(renderer).toHaveBeenCalledWith(
- expect.objectContaining({
- fetching: false,
- isIncomplete: null,
- hasMore: false,
- projects: [
- expect.objectContaining({
- id: '100',
- slug: 'a',
- teams: [newTeam],
- }),
- expect.objectContaining({
- id: '101',
- slug: 'b',
- }),
- expect.objectContaining({
- id: '102',
- slug: 'c',
- }),
- ],
- })
- )
- );
- });
- });
- });
|