From a9cce29a7da1cc347fe39d4e2ca6ffc0f0df7919 Mon Sep 17 00:00:00 2001 From: opeyem1a Date: Sun, 22 Sep 2024 21:47:20 -0600 Subject: [PATCH 1/5] changes --- src/commands/changes/commit.tsx | 93 +++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 src/commands/changes/commit.tsx diff --git a/src/commands/changes/commit.tsx b/src/commands/changes/commit.tsx new file mode 100644 index 0000000..eb3d1cb --- /dev/null +++ b/src/commands/changes/commit.tsx @@ -0,0 +1,93 @@ +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 ChangesCommit({ input }: CommandProps) { + const [, , message] = input; + const result = useChangesCommit({ message }); + + if (result.isError) { + return ; + } + + if (result.isLoading) { + return Loading...; + } + + return ( + + Committed all changes + + ); +} + +type Action = { isLoading: boolean } & ( + | { + isError: false; + } + | { + isError: true; + error: Error; + } +); + +type State = + | { + type: 'LOADING'; + } + | { + type: 'COMPLETE'; + } + | { + type: 'ERROR'; + error: Error; + }; + +const useChangesCommit = ({ message }: { message: string }): Action => { + const git = useGit(); + const [state, setState] = useState({ type: 'LOADING' }); + + useEffect(() => { + git.commit({ message }) + .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 changesCommitConfig: CommandConfig = { + description: 'Stage and commit all changes.', + usage: 'changes commit ""', + key: 'commit', + aliases: ['c'], + validateProps: (props) => { + const { input } = props; + const [, , message] = input; + + if (!message) + return { + valid: false, + errors: ['Please provide a commit message'], + }; + + return { valid: true }; + }, +}; + +export default ChangesCommit; From 2616dde5ab1df35077cc77ae010b75c0baba2119 Mon Sep 17 00:00:00 2001 From: opeyem1a Date: Sun, 22 Sep 2024 21:48:01 -0600 Subject: [PATCH 2/5] changes --- src/app.tsx | 9 +++++---- src/command-registry.ts | 7 +++++++ src/commands/changes/commit.tsx | 3 ++- src/services/git.ts | 5 +++++ src/types.ts | 13 +++++++++---- 5 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/app.tsx b/src/app.tsx index f229472..38708c8 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { Box, Text } from 'ink'; +import { PropValidationResult } from './types.js'; import { type Result } from 'meow'; import { findCommand } from './utils/commands.js'; @@ -23,21 +24,21 @@ export default function App({ cli }: Props) { ); } - const { valid, errors } = command.config.validateProps + const validationResult = command.config.validateProps ? command.config.validateProps({ cli, input: cli.input, }) - : { valid: true, errors: [] }; + : ({ valid: true } as PropValidationResult); - if (!valid) { + if (!validationResult.valid) { return ( Invalid inputs for command:{' '} {sanitizedInput.join(' ')} - {errors?.map((error) => ( + {validationResult.errors.map((error) => ( - {error} diff --git a/src/command-registry.ts b/src/command-registry.ts index cbc5535..cb55af2 100644 --- a/src/command-registry.ts +++ b/src/command-registry.ts @@ -1,4 +1,7 @@ import ChangesAdd, { changesAddConfig } from './commands/changes/add.js'; +import ChangesCommit, { + changesCommitConfig, +} from './commands/changes/commit.js'; import Help, { helpConfig } from './commands/help.js'; import Hop, { hopConfig } from './commands/hop.js'; import Sync, { syncConfig } from './commands/sync.js'; @@ -31,5 +34,9 @@ export const REGISTERED_COMMANDS: CommandGroup = { component: ChangesAdd, config: changesAddConfig, }, + commit: { + component: ChangesCommit, + config: changesCommitConfig, + }, } as CommandGroup, } as const; diff --git a/src/commands/changes/commit.tsx b/src/commands/changes/commit.tsx index eb3d1cb..a4a11a8 100644 --- a/src/commands/changes/commit.tsx +++ b/src/commands/changes/commit.tsx @@ -6,7 +6,8 @@ import { useGit } from '../../hooks/use-git.js'; function ChangesCommit({ input }: CommandProps) { const [, , message] = input; - const result = useChangesCommit({ message }); + // todo: refactor to a sanitize input pattern + const result = useChangesCommit({ message: message! }); if (result.isError) { return ; diff --git a/src/services/git.ts b/src/services/git.ts index e913926..9d4a8ad 100644 --- a/src/services/git.ts +++ b/src/services/git.ts @@ -12,6 +12,7 @@ export interface GitService { branchLocal: () => Promise>; checkout: (branch: string) => Promise>; addAllFiles: () => Promise; + commit: (args: { message: string }) => Promise; } export const createGitService = ({ @@ -33,5 +34,9 @@ export const createGitService = ({ addAllFiles: async () => { await gitEngine.add('.'); }, + commit: async ({ message }) => { + const a = await gitEngine.commit(message); + console.log({a}) + }, }; }; diff --git a/src/types.ts b/src/types.ts index ff36360..519271b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,10 +1,15 @@ import { ComponentType } from 'react'; import { Result } from 'meow'; -type ValidateProps> = (props: T) => { - valid: boolean; - errors?: string[]; -}; +export type ValidateProps> = ( + props: T +) => PropValidationResult; + +export type PropValidationResult = + | { + valid: true; + } + | { valid: false; errors: string[] }; export interface CommandProps extends Record { cli: Pick, 'flags' | 'unnormalizedFlags'>; From 90b821a798f6fcc1c03a5aff7c312230f37a062e Mon Sep 17 00:00:00 2001 From: opeyem1a Date: Sun, 22 Sep 2024 21:48:25 -0600 Subject: [PATCH 3/5] changes --- src/services/git.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/services/git.ts b/src/services/git.ts index 9d4a8ad..3564f37 100644 --- a/src/services/git.ts +++ b/src/services/git.ts @@ -35,8 +35,7 @@ export const createGitService = ({ await gitEngine.add('.'); }, commit: async ({ message }) => { - const a = await gitEngine.commit(message); - console.log({a}) + await gitEngine.commit(message); }, }; }; From 52eb69a282ceced6d1235d5a099cabc337e118c8 Mon Sep 17 00:00:00 2001 From: opeyem1a Date: Sun, 6 Oct 2024 15:13:44 -0600 Subject: [PATCH 4/5] add basic tests --- src/commands/changes/commit.test.tsx | 101 +++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 src/commands/changes/commit.test.tsx diff --git a/src/commands/changes/commit.test.tsx b/src/commands/changes/commit.test.tsx new file mode 100644 index 0000000..268ada8 --- /dev/null +++ b/src/commands/changes/commit.test.tsx @@ -0,0 +1,101 @@ +import ChangesCommit from './commit.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 { + commit: async ({ message }) => { + console.log(message); + 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 = 'Committed all changes'; + +describe('correctly renders changes commit UI', () => { + it('runs as intended', async () => { + const actual1 = render( + + ); + + const actual2 = render( + + ); + + const ExpectedComp = () => { + return ( + + {SUCCESS_MESSAGE} + + ); + }; + const expected = render(); + + 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( + + ); + + const actual2 = render( + + ); + + const ExpectedComp = () => { + return {LOADING_MESSAGE}; + }; + const expected = render(); + + await delay(ARBITRARY_DELAY / 2); + expect(actual1.lastFrame()).to.equal(expected.lastFrame()); + expect(actual2.lastFrame()).to.equal(expected.lastFrame()); + }); +}); From 24c1884584a749df1bb654664ce748a8f8476ef2 Mon Sep 17 00:00:00 2001 From: opeyem1a Date: Sun, 6 Oct 2024 15:20:11 -0600 Subject: [PATCH 5/5] fix types --- src/commands/changes/commit.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/changes/commit.test.tsx b/src/commands/changes/commit.test.tsx index 268ada8..0bb4ee8 100644 --- a/src/commands/changes/commit.test.tsx +++ b/src/commands/changes/commit.test.tsx @@ -11,7 +11,7 @@ const mocks = vi.hoisted(() => { return { createGitService: vi.fn(({}) => { return { - commit: async ({ message }) => { + commit: async ({ message }: { message: string }) => { console.log(message); return new Promise((resolve) => setTimeout(resolve, ARBITRARY_DELAY)