From 1a870070f99ec6b0cfa4f42cb1151dbeba90b273 Mon Sep 17 00:00:00 2001 From: greg mooney Date: Thu, 24 Oct 2024 15:28:26 +0200 Subject: [PATCH 1/3] Enable push/pull buttons for empty repos --- src/components/Toolbar.tsx | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/components/Toolbar.tsx b/src/components/Toolbar.tsx index 5445a66e..947eb290 100644 --- a/src/components/Toolbar.tsx +++ b/src/components/Toolbar.tsx @@ -115,6 +115,11 @@ export interface IToolbarState { * Boolean indicating whether a refresh is currently in progress. */ refreshInProgress: boolean; + + /** + * Boolean indicating whether a remote exists. + */ + hasRemote: boolean; } /** @@ -132,10 +137,24 @@ export class Toolbar extends React.Component { this.state = { branchMenu: false, tab: 0, - refreshInProgress: false + refreshInProgress: false, + hasRemote: false }; } + /** + * Check whether or not the repo has any remotes + */ + async componentDidMount(): Promise { + try { + const remotes = await this.props.model.getRemotes(); + const hasRemote = remotes.length > 0 ? true : false; + this.setState({ hasRemote }); + } catch (err) { + console.error(err); + } + } + /** * Renders the component. * @@ -160,10 +179,7 @@ export class Toolbar extends React.Component { const activeBranch = this.props.branches.filter( branch => branch.is_current_branch ); - // FIXME whether the repository as a remote or not should be done through a call to `git remote` - const hasRemote = this.props.branches.some( - branch => branch.is_remote_branch - ); + const hasRemote = this.state.hasRemote; const hasUpstream = activeBranch[0]?.upstream !== null; return ( From 8dbc07e62a50ed1006af6c50093cea11f9dd9884 Mon Sep 17 00:00:00 2001 From: greg mooney Date: Fri, 25 Oct 2024 11:42:29 +0200 Subject: [PATCH 2/3] Update Toolbar tests --- .../test-components/Toolbar.spec.tsx | 158 +++++++++++------- 1 file changed, 99 insertions(+), 59 deletions(-) diff --git a/src/__tests__/test-components/Toolbar.spec.tsx b/src/__tests__/test-components/Toolbar.spec.tsx index 9bd0b85c..29877448 100644 --- a/src/__tests__/test-components/Toolbar.spec.tsx +++ b/src/__tests__/test-components/Toolbar.spec.tsx @@ -1,6 +1,6 @@ import { nullTranslator } from '@jupyterlab/translation'; import '@testing-library/jest-dom'; -import { render, screen } from '@testing-library/react'; +import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import 'jest'; import * as React from 'react'; @@ -8,11 +8,26 @@ import { IToolbarProps, Toolbar } from '../../components/Toolbar'; import * as git from '../../git'; import { GitExtension } from '../../model'; import { badgeClass } from '../../style/Toolbar'; -import { DEFAULT_REPOSITORY_PATH, mockedRequestAPI } from '../utils'; import { CommandIDs } from '../../tokens'; +import { + DEFAULT_REPOSITORY_PATH, + defaultMockedResponses, + mockedRequestAPI +} from '../utils'; jest.mock('../../git'); +const REMOTES = [ + { + name: 'test', + url: 'https://test.com' + }, + { + name: 'origin', + url: 'https://origin.com' + } +]; + async function createModel() { const model = new GitExtension(); model.pathRepository = DEFAULT_REPOSITORY_PATH; @@ -65,7 +80,18 @@ describe('Toolbar', () => { jest.restoreAllMocks(); const mock = git as jest.Mocked; - mock.requestAPI.mockImplementation(mockedRequestAPI() as any); + mock.requestAPI.mockImplementation( + mockedRequestAPI({ + responses: { + ...defaultMockedResponses, + 'remote/show': { + body: () => { + return { code: 0, remotes: REMOTES }; + } + } + } + }) as any + ); model = await createModel(); }); @@ -79,12 +105,14 @@ describe('Toolbar', () => { }); describe('render', () => { - it('should display a button to pull the latest changes', () => { + it('should display a button to pull the latest changes', async () => { render(); - expect( - screen.getAllByRole('button', { name: 'Pull latest changes' }) - ).toBeDefined(); + await waitFor(() => { + expect( + screen.getAllByRole('button', { name: 'Pull latest changes' }) + ).toBeDefined(); + }); expect( screen @@ -93,22 +121,26 @@ describe('Toolbar', () => { ).toHaveClass('MuiBadge-invisible'); }); - it('should display a badge on pull icon if behind', () => { + it('should display a badge on pull icon if behind', async () => { render(); - expect( - screen - .getByRole('button', { name: /^Pull latest changes/ }) - .parentElement?.querySelector(`.${badgeClass} > .MuiBadge-badge`) - ).not.toHaveClass('MuiBadge-invisible'); + await waitFor(() => { + expect( + screen + .getByRole('button', { name: /^Pull latest changes/ }) + .parentElement?.querySelector(`.${badgeClass} > .MuiBadge-badge`) + ).not.toHaveClass('MuiBadge-invisible'); + }); }); - it('should display a button to push the latest changes', () => { + it('should display a button to push the latest changes', async () => { render(); - expect( - screen.getAllByRole('button', { name: 'Push committed changes' }) - ).toBeDefined(); + await waitFor(() => { + expect( + screen.getAllByRole('button', { name: 'Push committed changes' }) + ).toBeDefined(); + }); expect( screen @@ -117,14 +149,16 @@ describe('Toolbar', () => { ).toHaveClass('MuiBadge-invisible'); }); - it('should display a badge on push icon if behind', () => { + it('should display a badge on push icon if behind', async () => { render(); - expect( - screen - .getByRole('button', { name: /^Push committed changes/ }) - .parentElement?.querySelector(`.${badgeClass} > .MuiBadge-badge`) - ).not.toHaveClass('MuiBadge-invisible'); + await waitFor(() => { + expect( + screen + .getByRole('button', { name: /^Push committed changes/ }) + .parentElement?.querySelector(`.${badgeClass} > .MuiBadge-badge`) + ).not.toHaveClass('MuiBadge-invisible'); + }); }); it('should display a button to refresh the current repository', () => { @@ -177,7 +211,7 @@ describe('Toolbar', () => { }); }); - describe('pull changes', () => { + describe('push/pull changes with remote', () => { it('should pull changes when the button to pull the latest changes is clicked', async () => { const mockedExecute = jest.fn(); render( @@ -190,29 +224,21 @@ describe('Toolbar', () => { /> ); - await userEvent.click( - screen.getByRole('button', { name: 'Pull latest changes' }) - ); + await waitFor(async () => { + await userEvent.click( + screen.getByRole('button', { name: 'Pull latest changes' }) + ); + }); expect(mockedExecute).toHaveBeenCalledTimes(1); expect(mockedExecute).toHaveBeenCalledWith(CommandIDs.gitPull); }); - it('should not pull changes when the pull button is clicked but there is no remote branch', async () => { + it('should push changes when the button to push the latest changes is clicked', async () => { const mockedExecute = jest.fn(); render( { /> ); - await userEvent.click( - screen.getAllByRole('button', { - name: 'No remote repository defined' - })[0] - ); + await waitFor(async () => { + await userEvent.click( + screen.getByRole('button', { name: 'Push committed changes' }) + ); + }); - expect(mockedExecute).toHaveBeenCalledTimes(0); + expect(mockedExecute).toHaveBeenCalledTimes(1); + expect(mockedExecute).toHaveBeenCalledWith(CommandIDs.gitPush); }); }); - describe('push changes', () => { - it('should push changes when the button to push the latest changes is clicked', async () => { + describe('push/pull changes without remote', () => { + beforeEach(async () => { + jest.restoreAllMocks(); + + const mock = git as jest.Mocked; + mock.requestAPI.mockImplementation( + mockedRequestAPI({ + responses: { + ...defaultMockedResponses, + 'remote/show': { + body: () => { + return { code: -1, remotes: [] }; + } + } + } + }) as any + ); + + model = await createModel(); + }); + + it('should not pull changes when the pull button is clicked but there is no remote branch', async () => { const mockedExecute = jest.fn(); render( { })} /> ); + await userEvent.click( - screen.getByRole('button', { name: 'Push committed changes' }) + screen.getAllByRole('button', { + name: 'No remote repository defined' + })[0] ); - expect(mockedExecute).toHaveBeenCalledTimes(1); - expect(mockedExecute).toHaveBeenCalledWith(CommandIDs.gitPush); + + expect(mockedExecute).toHaveBeenCalledTimes(0); }); it('should not push changes when the push button is clicked but there is no remote branch', async () => { @@ -254,16 +304,6 @@ describe('Toolbar', () => { render( Date: Fri, 25 Oct 2024 12:32:23 +0200 Subject: [PATCH 3/3] Update tests again --- .../test-components/Toolbar.spec.tsx | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/__tests__/test-components/Toolbar.spec.tsx b/src/__tests__/test-components/Toolbar.spec.tsx index 29877448..7ab92af3 100644 --- a/src/__tests__/test-components/Toolbar.spec.tsx +++ b/src/__tests__/test-components/Toolbar.spec.tsx @@ -224,12 +224,16 @@ describe('Toolbar', () => { /> ); - await waitFor(async () => { - await userEvent.click( + await waitFor(() => { + expect( screen.getByRole('button', { name: 'Pull latest changes' }) - ); + ).toBeDefined(); }); + await userEvent.click( + screen.getByRole('button', { name: 'Pull latest changes' }) + ); + expect(mockedExecute).toHaveBeenCalledTimes(1); expect(mockedExecute).toHaveBeenCalledWith(CommandIDs.gitPull); }); @@ -246,12 +250,16 @@ describe('Toolbar', () => { /> ); - await waitFor(async () => { - await userEvent.click( + await waitFor(() => { + expect( screen.getByRole('button', { name: 'Push committed changes' }) - ); + ).toBeDefined(); }); + await userEvent.click( + screen.getByRole('button', { name: 'Push committed changes' }) + ); + expect(mockedExecute).toHaveBeenCalledTimes(1); expect(mockedExecute).toHaveBeenCalledWith(CommandIDs.gitPush); });