Skip to content

Commit

Permalink
changes add command (#22)
Browse files Browse the repository at this point in the history
* changes add command

* use changes add properly

* ultra basic tests

* add props

* add props
  • Loading branch information
Opeyem1a authored Oct 6, 2024
1 parent b5a0c6a commit 80d81c1
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 0 deletions.
12 changes: 12 additions & 0 deletions src/command-registry.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import ChangesAdd, { changesAddConfig } from './commands/changes/add.js';
import Help, { helpConfig } from './commands/help.js';
import Hop, { hopConfig } from './commands/hop.js';
import Sync, { syncConfig } from './commands/sync.js';
Expand All @@ -20,4 +21,15 @@ export const REGISTERED_COMMANDS: CommandGroup = {
component: Sync,
config: syncConfig,
},
changes: {
_group: {
alias: 'c',
name: 'changes',
description: 'Commands related to staged changes',
},
add: {
component: ChangesAdd,
config: changesAddConfig,
},
} as CommandGroup,
} as const;
100 changes: 100 additions & 0 deletions src/commands/changes/add.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import ChangesAdd from './add.js';
import React from 'react';
import { Text } from 'ink';
import { delay } from '../../utils/time.js';
import { describe, expect, it, vi } from 'vitest';
import { render } from '@levelbreaded/ink-testing-library';

const ARBITRARY_DELAY = 250; // ms

const mocks = vi.hoisted(() => {
return {
createGitService: vi.fn(({}) => {
return {
addAllFiles: async () => {
return new Promise((resolve) =>
setTimeout(resolve, ARBITRARY_DELAY)
);
},
};
}),
};
});

vi.mock('../../services/git.js', () => {
return {
DEFAULT_OPTIONS: {},
createGitService: mocks.createGitService,
};
});

const LOADING_MESSAGE = 'Loading...';
const SUCCESS_MESSAGE = 'Staged all changes';

describe('correctly renders changes add UI', () => {
it('runs as intended', async () => {
const actual1 = render(
<ChangesAdd
cli={{
flags: {},
unnormalizedFlags: {},
}}
input={['changes', 'add']}
/>
);

const actual2 = render(
<ChangesAdd
cli={{
flags: {},
unnormalizedFlags: {},
}}
input={['changes', 'a']}
/>
);

const ExpectedComp = () => {
return (
<Text bold color="green">
{SUCCESS_MESSAGE}
</Text>
);
};
const expected = render(<ExpectedComp />);

await delay(ARBITRARY_DELAY + 250);
expect(actual1.lastFrame()).to.equal(expected.lastFrame());
expect(actual2.lastFrame()).to.equal(expected.lastFrame());
});

it('displays a loading state while processing', async () => {
const actual1 = render(
<ChangesAdd
cli={{
flags: {},
unnormalizedFlags: {},
}}
input={['changes', 'add']}
/>
);

const actual2 = render(
<ChangesAdd
cli={{
flags: {},
unnormalizedFlags: {},
}}
input={['changes', 'a']}
/>
);

const ExpectedComp = () => {
return <Text color="cyan">{LOADING_MESSAGE}</Text>;
};
const expected = render(<ExpectedComp />);

await delay(ARBITRARY_DELAY / 2);
expect(actual1.lastFrame()).to.equal(expected.lastFrame());
expect(actual2.lastFrame()).to.equal(expected.lastFrame());
});
});
80 changes: 80 additions & 0 deletions src/commands/changes/add.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import ErrorDisplay from '../../components/error-display.js';
import React, { useEffect, useState } from 'react';
import { CommandConfig, CommandProps } from '../../types.js';
import { Text } from 'ink';
import { useGit } from '../../hooks/use-git.js';

function ChangedAdd({}: CommandProps) {
const result = useChangesAdd();

if (result.isError) {
return <ErrorDisplay error={result.error} />;
}

if (result.isLoading) {
return <Text color="cyan">Loading...</Text>;
}

return (
<Text bold color="green">
Staged all changes
</Text>
);
}

type Action = { isLoading: boolean } & (
| {
isError: false;
}
| {
isError: true;
error: Error;
}
);

type State =
| {
type: 'LOADING';
}
| {
type: 'COMPLETE';
}
| {
type: 'ERROR';
error: Error;
};

const useChangesAdd = (): Action => {
const git = useGit();
const [state, setState] = useState<State>({ type: 'LOADING' });

useEffect(() => {
git.addAllFiles()
.then(() => setState({ type: 'COMPLETE' }))
.catch((e: Error) => {
setState({ type: 'ERROR', error: e });
});
}, []);

if (state.type === 'ERROR') {
return {
isLoading: false,
isError: true,
error: state.error,
};
}

return {
isLoading: state.type === 'LOADING',
isError: false,
};
};

export const changesAddConfig: CommandConfig = {
description: 'Stage all changes.',
usage: 'changes add',
key: 'add',
aliases: ['a'],
};

export default ChangedAdd;
4 changes: 4 additions & 0 deletions src/services/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface GitService {
_git: SimpleGit;
branchLocal: () => Promise<ReturnType<SimpleGit['branchLocal']>>;
checkout: (branch: string) => Promise<ReturnType<SimpleGit['checkout']>>;
addAllFiles: () => Promise<void>;
}

export const createGitService = ({
Expand All @@ -29,5 +30,8 @@ export const createGitService = ({
branchLocal: async () => {
return gitEngine.branchLocal();
},
addAllFiles: async () => {
await gitEngine.add('.');
},
};
};

0 comments on commit 80d81c1

Please sign in to comment.