From f0464d4b60d0b32711ecd9a961062c202e9cfd9f Mon Sep 17 00:00:00 2001 From: Takanori Matsumoto Date: Tue, 3 Sep 2024 08:51:37 +0900 Subject: [PATCH 1/5] test(e2e): add end-to-end tests for git push functionality test(utils): refactor prepareEnvironment to use prepareTempDir function for consistency --- test/e2e/gitPush.test.ts | 194 +++++++++++++++++++++++++++++++++++++++ test/e2e/utils.ts | 6 +- 2 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 test/e2e/gitPush.test.ts diff --git a/test/e2e/gitPush.test.ts b/test/e2e/gitPush.test.ts new file mode 100644 index 00000000..408471e8 --- /dev/null +++ b/test/e2e/gitPush.test.ts @@ -0,0 +1,194 @@ +import path from 'path'; +import 'cli-testing-library/extend-expect'; +import { exec } from 'child_process'; +import { prepareTempDir } from './utils'; +import { promisify } from 'util'; +import { render } from 'cli-testing-library'; +import { resolve } from 'path'; +import { rm } from 'fs'; +const fsExec = promisify(exec); +const fsRemove = promisify(rm); + +/** + * git remote -v + * + * [no remotes] + */ +const prepareNoRemoteGitRepository = async (): Promise<{ + gitDir: string; + cleanup: () => Promise; +}> => { + const tempDir = await prepareTempDir(); + await fsExec('git init test', { cwd: tempDir }); + const gitDir = path.resolve(tempDir, 'test'); + + const cleanup = async () => { + return fsRemove(tempDir, { recursive: true }); + }; + return { + gitDir, + cleanup + }; +}; + +/** + * git remote -v + * + * origin /tmp/remote.git (fetch) + * origin /tmp/remote.git (push) + */ +const prepareOneRemoteGitRepository = async (): Promise<{ + gitDir: string; + cleanup: () => Promise; +}> => { + const tempDir = await prepareTempDir(); + await fsExec('git init --bare remote.git', { cwd: tempDir }); + await fsExec('git clone remote.git test', { cwd: tempDir }); + const gitDir = path.resolve(tempDir, 'test'); + + const cleanup = async () => { + return fsRemove(tempDir, { recursive: true }); + }; + return { + gitDir, + cleanup + }; +}; + +/** + * git remote -v + * + * origin /tmp/remote.git (fetch) + * origin /tmp/remote.git (push) + * other ../remote2.git (fetch) + * other ../remote2.git (push) + */ +const prepareTwoRemotesGitRepository = async (): Promise<{ + gitDir: string; + cleanup: () => Promise; +}> => { + const tempDir = await prepareTempDir(); + await fsExec('git init --bare remote.git', { cwd: tempDir }); + await fsExec('git init --bare other.git', { cwd: tempDir }); + await fsExec('git clone remote.git test', { cwd: tempDir }); + const gitDir = path.resolve(tempDir, 'test'); + await fsExec('git remote add other ../other.git', { cwd: gitDir }); + + const cleanup = async () => { + return fsRemove(tempDir, { recursive: true }); + }; + return { + gitDir, + cleanup + }; +}; + +it('cli flow to do nothing when no remote', async () => { + const { gitDir, cleanup } = await prepareNoRemoteGitRepository(); + + await render('echo', [`'console.log("Hello World");' > index.ts`], { + cwd: gitDir + }); + await render('git', ['add index.ts'], { cwd: gitDir }); + + const { queryByText, findByText, userEvent } = await render( + `OCO_AI_PROVIDER='test' node`, + [resolve('./out/cli.cjs')], + { cwd: gitDir } + ); + expect(await findByText('Confirm the commit message?')).toBeInTheConsole(); + userEvent.keyboard('[Enter]'); + + expect( + await queryByText('Choose a remote to push to') + ).not.toBeInTheConsole(); + expect( + await queryByText('Do you want to run `git push`?') + ).not.toBeInTheConsole(); + expect( + await queryByText('Successfully pushed all commits to origin') + ).not.toBeInTheConsole(); + + await cleanup(); +}); + +it('cli flow to do nothing when GIT_PUSH set to false', async () => { + const { gitDir, cleanup } = await prepareOneRemoteGitRepository(); + + await render('echo', [`'console.log("Hello World");' > index.ts`], { + cwd: gitDir + }); + await render('git', ['add index.ts'], { cwd: gitDir }); + + const { queryByText, findByText, userEvent } = await render( + `OCO_AI_PROVIDER='test' OCO_GITPUSH='false' node`, + [resolve('./out/cli.cjs')], + { cwd: gitDir } + ); + expect(await findByText('Confirm the commit message?')).toBeInTheConsole(); + userEvent.keyboard('[Enter]'); + + expect( + await queryByText('Choose a remote to push to') + ).not.toBeInTheConsole(); + expect( + await queryByText('Do you want to run `git push`?') + ).not.toBeInTheConsole(); + expect( + await queryByText('Successfully pushed all commits to origin') + ).not.toBeInTheConsole(); + + await cleanup(); +}); + +it('cli flow to push git branch when one remote is set', async () => { + const { gitDir, cleanup } = await prepareOneRemoteGitRepository(); + + await render('echo', [`'console.log("Hello World");' > index.ts`], { + cwd: gitDir + }); + await render('git', ['add index.ts'], { cwd: gitDir }); + + const { findByText, userEvent } = await render( + `OCO_AI_PROVIDER='test' node`, + [resolve('./out/cli.cjs')], + { cwd: gitDir } + ); + expect(await findByText('Confirm the commit message?')).toBeInTheConsole(); + userEvent.keyboard('[Enter]'); + + expect(await findByText('Choose a remote to push to')).toBeInTheConsole(); + userEvent.keyboard('[Enter]'); + + expect( + await findByText('Successfully pushed all commits to origin') + ).toBeInTheConsole(); + + await cleanup(); +}); + +it('cli flow to push git branch when two remotes are set', async () => { + const { gitDir, cleanup } = await prepareTwoRemotesGitRepository(); + + await render('echo', [`'console.log("Hello World");' > index.ts`], { + cwd: gitDir + }); + await render('git', ['add index.ts'], { cwd: gitDir }); + + const { findByText, userEvent } = await render( + `OCO_AI_PROVIDER='test' node`, + [resolve('./out/cli.cjs')], + { cwd: gitDir } + ); + expect(await findByText('Confirm the commit message?')).toBeInTheConsole(); + userEvent.keyboard('[Enter]'); + + expect(await findByText('Choose a remote to push to')).toBeInTheConsole(); + userEvent.keyboard('[Enter]'); + + expect( + await findByText('Successfully pushed all commits to origin') + ).toBeInTheConsole(); + + await cleanup(); +}); diff --git a/test/e2e/utils.ts b/test/e2e/utils.ts index 73f909c5..6ae56633 100644 --- a/test/e2e/utils.ts +++ b/test/e2e/utils.ts @@ -15,7 +15,7 @@ export const prepareEnvironment = async (): Promise<{ gitDir: string; cleanup: () => Promise; }> => { - const tempDir = await fsMakeTempDir(path.join(tmpdir(), 'opencommit-test-')); + const tempDir = await prepareTempDir(); // Create a remote git repository int the temp directory. This is necessary to execute the `git push` command await fsExec('git init --bare remote.git', { cwd: tempDir }); await fsExec('git clone remote.git test', { cwd: tempDir }); @@ -30,4 +30,8 @@ export const prepareEnvironment = async (): Promise<{ } } +export const prepareTempDir = async(): Promise => { + return await fsMakeTempDir(path.join(tmpdir(), 'opencommit-test-')); +} + export const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); From 6063780600320cee4e74c8b896629e52b33987df Mon Sep 17 00:00:00 2001 From: Takanori Matsumoto Date: Wed, 4 Sep 2024 22:39:55 +0900 Subject: [PATCH 2/5] fix(commit.ts): add early return if OCO_GITPUSH is false to prevent unnecessary operations refactor(commit.ts): simplify condition for single remote check by removing redundant config check --- src/commands/commit.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/commands/commit.ts b/src/commands/commit.ts index 5bb9de85..8d098818 100644 --- a/src/commands/commit.ts +++ b/src/commands/commit.ts @@ -107,13 +107,16 @@ ${chalk.grey('——————————————————')}` const remotes = await getGitRemotes(); + // user isn't pushing, return early + if (config.OCO_GITPUSH === false) return; + if (!remotes.length) { const { stdout } = await execa('git', ['push']); if (stdout) outro(stdout); process.exit(0); } - if (remotes.length === 1 && config.OCO_GITPUSH !== true) { + if (remotes.length === 1) { const isPushConfirmedByUser = await confirm({ message: 'Do you want to run `git push`?' }); From abcb6b8d75077fa9f749f9ce97e4432a43f1e1ad Mon Sep 17 00:00:00 2001 From: Takanori Matsumoto Date: Wed, 4 Sep 2024 22:44:01 +0900 Subject: [PATCH 3/5] test(gitPush.test.ts): add test cases for OCO_GITPUSH environment variable and improve test descriptions --- test/e2e/gitPush.test.ts | 197 +++++++++++++++++++++------------------ 1 file changed, 104 insertions(+), 93 deletions(-) diff --git a/test/e2e/gitPush.test.ts b/test/e2e/gitPush.test.ts index 408471e8..611f8d04 100644 --- a/test/e2e/gitPush.test.ts +++ b/test/e2e/gitPush.test.ts @@ -83,112 +83,123 @@ const prepareTwoRemotesGitRepository = async (): Promise<{ }; }; -it('cli flow to do nothing when no remote', async () => { - const { gitDir, cleanup } = await prepareNoRemoteGitRepository(); - - await render('echo', [`'console.log("Hello World");' > index.ts`], { - cwd: gitDir +describe('cli flow to push git branch', () => { + it('do nothing when OCO_GITPUSH is set to false', async () => { + const { gitDir, cleanup } = await prepareNoRemoteGitRepository(); + + await render('echo', [`'console.log("Hello World");' > index.ts`], { + cwd: gitDir + }); + await render('git', ['add index.ts'], { cwd: gitDir }); + + const { queryByText, findByText, userEvent } = await render( + `OCO_AI_PROVIDER='test' OCO_GITPUSH='false' node`, + [resolve('./out/cli.cjs')], + { cwd: gitDir } + ); + expect(await findByText('Confirm the commit message?')).toBeInTheConsole(); + userEvent.keyboard('[Enter]'); + + expect( + await queryByText('Choose a remote to push to') + ).not.toBeInTheConsole(); + expect( + await queryByText('Do you want to run `git push`?') + ).not.toBeInTheConsole(); + expect( + await queryByText('Successfully pushed all commits to origin') + ).not.toBeInTheConsole(); + expect( + await queryByText('Command failed with exit code 1') + ).not.toBeInTheConsole(); + + await cleanup(); }); - await render('git', ['add index.ts'], { cwd: gitDir }); - - const { queryByText, findByText, userEvent } = await render( - `OCO_AI_PROVIDER='test' node`, - [resolve('./out/cli.cjs')], - { cwd: gitDir } - ); - expect(await findByText('Confirm the commit message?')).toBeInTheConsole(); - userEvent.keyboard('[Enter]'); - - expect( - await queryByText('Choose a remote to push to') - ).not.toBeInTheConsole(); - expect( - await queryByText('Do you want to run `git push`?') - ).not.toBeInTheConsole(); - expect( - await queryByText('Successfully pushed all commits to origin') - ).not.toBeInTheConsole(); - - await cleanup(); -}); - -it('cli flow to do nothing when GIT_PUSH set to false', async () => { - const { gitDir, cleanup } = await prepareOneRemoteGitRepository(); - await render('echo', [`'console.log("Hello World");' > index.ts`], { - cwd: gitDir + it('push and cause error when there is no remote', async () => { + const { gitDir, cleanup } = await prepareNoRemoteGitRepository(); + + await render('echo', [`'console.log("Hello World");' > index.ts`], { + cwd: gitDir + }); + await render('git', ['add index.ts'], { cwd: gitDir }); + + const { queryByText, findByText, userEvent } = await render( + `OCO_AI_PROVIDER='test' node`, + [resolve('./out/cli.cjs')], + { cwd: gitDir } + ); + expect(await findByText('Confirm the commit message?')).toBeInTheConsole(); + userEvent.keyboard('[Enter]'); + + expect( + await queryByText('Choose a remote to push to') + ).not.toBeInTheConsole(); + expect( + await queryByText('Do you want to run `git push`?') + ).not.toBeInTheConsole(); + expect( + await queryByText('Successfully pushed all commits to origin') + ).not.toBeInTheConsole(); + + expect( + await findByText('Command failed with exit code 1') + ).toBeInTheConsole(); + + await cleanup(); }); - await render('git', ['add index.ts'], { cwd: gitDir }); - - const { queryByText, findByText, userEvent } = await render( - `OCO_AI_PROVIDER='test' OCO_GITPUSH='false' node`, - [resolve('./out/cli.cjs')], - { cwd: gitDir } - ); - expect(await findByText('Confirm the commit message?')).toBeInTheConsole(); - userEvent.keyboard('[Enter]'); - - expect( - await queryByText('Choose a remote to push to') - ).not.toBeInTheConsole(); - expect( - await queryByText('Do you want to run `git push`?') - ).not.toBeInTheConsole(); - expect( - await queryByText('Successfully pushed all commits to origin') - ).not.toBeInTheConsole(); - - await cleanup(); -}); -it('cli flow to push git branch when one remote is set', async () => { - const { gitDir, cleanup } = await prepareOneRemoteGitRepository(); + it('push when one remote is set', async () => { + const { gitDir, cleanup } = await prepareOneRemoteGitRepository(); - await render('echo', [`'console.log("Hello World");' > index.ts`], { - cwd: gitDir - }); - await render('git', ['add index.ts'], { cwd: gitDir }); + await render('echo', [`'console.log("Hello World");' > index.ts`], { + cwd: gitDir + }); + await render('git', ['add index.ts'], { cwd: gitDir }); - const { findByText, userEvent } = await render( - `OCO_AI_PROVIDER='test' node`, - [resolve('./out/cli.cjs')], - { cwd: gitDir } - ); - expect(await findByText('Confirm the commit message?')).toBeInTheConsole(); - userEvent.keyboard('[Enter]'); + const { findByText, userEvent } = await render( + `OCO_AI_PROVIDER='test' node`, + [resolve('./out/cli.cjs')], + { cwd: gitDir } + ); + expect(await findByText('Confirm the commit message?')).toBeInTheConsole(); + userEvent.keyboard('[Enter]'); - expect(await findByText('Choose a remote to push to')).toBeInTheConsole(); - userEvent.keyboard('[Enter]'); + expect( + await findByText('Do you want to run `git push`?') + ).toBeInTheConsole(); + userEvent.keyboard('[Enter]'); - expect( - await findByText('Successfully pushed all commits to origin') - ).toBeInTheConsole(); + expect( + await findByText('Successfully pushed all commits to origin') + ).toBeInTheConsole(); - await cleanup(); -}); + await cleanup(); + }); -it('cli flow to push git branch when two remotes are set', async () => { - const { gitDir, cleanup } = await prepareTwoRemotesGitRepository(); + it('push when two remotes are set', async () => { + const { gitDir, cleanup } = await prepareTwoRemotesGitRepository(); - await render('echo', [`'console.log("Hello World");' > index.ts`], { - cwd: gitDir - }); - await render('git', ['add index.ts'], { cwd: gitDir }); + await render('echo', [`'console.log("Hello World");' > index.ts`], { + cwd: gitDir + }); + await render('git', ['add index.ts'], { cwd: gitDir }); - const { findByText, userEvent } = await render( - `OCO_AI_PROVIDER='test' node`, - [resolve('./out/cli.cjs')], - { cwd: gitDir } - ); - expect(await findByText('Confirm the commit message?')).toBeInTheConsole(); - userEvent.keyboard('[Enter]'); + const { findByText, userEvent } = await render( + `OCO_AI_PROVIDER='test' node`, + [resolve('./out/cli.cjs')], + { cwd: gitDir } + ); + expect(await findByText('Confirm the commit message?')).toBeInTheConsole(); + userEvent.keyboard('[Enter]'); - expect(await findByText('Choose a remote to push to')).toBeInTheConsole(); - userEvent.keyboard('[Enter]'); + expect(await findByText('Choose a remote to push to')).toBeInTheConsole(); + userEvent.keyboard('[Enter]'); - expect( - await findByText('Successfully pushed all commits to origin') - ).toBeInTheConsole(); + expect( + await findByText('Successfully pushed all commits to origin') + ).toBeInTheConsole(); - await cleanup(); + await cleanup(); + }); }); From 38ba6639684baeffcf90b1913bdbcd573acd26ea Mon Sep 17 00:00:00 2001 From: Takanori Matsumoto Date: Wed, 4 Sep 2024 22:46:44 +0900 Subject: [PATCH 4/5] docs(README.md): correct typo in the section about git push prompt configuration --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d21e7e7f..2c4bc240 100644 --- a/README.md +++ b/README.md @@ -224,7 +224,7 @@ All available languages are currently listed in the [i18n](https://github.com/di ### Push to git (gonna be deprecated) -A prompt to ushing to git is on by default but if you would like to turn it off just use: +A prompt for pushing to git is on by default but if you would like to turn it off just use: ```sh oco config set OCO_GITPUSH=false From 0be27e253a329c2a6a69b57bd924b254fe870826 Mon Sep 17 00:00:00 2001 From: Takanori Matsumoto Date: Wed, 4 Sep 2024 22:59:07 +0900 Subject: [PATCH 5/5] test(e2e): update prompt text for git push confirmation Update the end-to-end tests to reflect the new prompt text "Do you want to run `git push`?" instead of "Choose a remote to push to". --- test/e2e/oneFile.test.ts | 4 ++-- test/e2e/prompt-module/commitlint.test.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/e2e/oneFile.test.ts b/test/e2e/oneFile.test.ts index ddfbc10a..b3e25f96 100644 --- a/test/e2e/oneFile.test.ts +++ b/test/e2e/oneFile.test.ts @@ -17,7 +17,7 @@ it('cli flow to generate commit message for 1 new file (staged)', async () => { expect(await findByText('Confirm the commit message?')).toBeInTheConsole(); userEvent.keyboard('[Enter]'); - expect(await findByText('Choose a remote to push to')).toBeInTheConsole(); + expect(await findByText('Do you want to run `git push`?')).toBeInTheConsole(); userEvent.keyboard('[Enter]'); expect(await findByText('Successfully pushed all commits to origin')).toBeInTheConsole(); @@ -46,7 +46,7 @@ it('cli flow to generate commit message for 1 changed file (not staged)', async expect(await findByText('Successfully committed')).toBeInTheConsole(); - expect(await findByText('Choose a remote to push to')).toBeInTheConsole(); + expect(await findByText('Do you want to run `git push`?')).toBeInTheConsole(); userEvent.keyboard('[Enter]'); expect(await findByText('Successfully pushed all commits to origin')).toBeInTheConsole(); diff --git a/test/e2e/prompt-module/commitlint.test.ts b/test/e2e/prompt-module/commitlint.test.ts index a78ebb61..ab849994 100644 --- a/test/e2e/prompt-module/commitlint.test.ts +++ b/test/e2e/prompt-module/commitlint.test.ts @@ -209,7 +209,7 @@ describe('cli flow to generate commit message using @commitlint prompt-module', oco.userEvent.keyboard('[Enter]'); expect( - await oco.findByText('Choose a remote to push to') + await oco.findByText('Do you want to run `git push`?') ).toBeInTheConsole(); oco.userEvent.keyboard('[Enter]');