@@ -17,14 +17,20 @@ jest.mock('inquirer', () => ({
1717 } ,
1818} ) ) ;
1919
20+ jest . mock ( '../github.js' , ( ) => ( {
21+ offerAndCreateGitHubRepo : jest . fn ( ) ,
22+ } ) ) ;
23+
2024import fs from 'fs-extra' ;
2125import { execa } from 'execa' ;
2226import inquirer from 'inquirer' ;
27+ import { offerAndCreateGitHubRepo } from '../github.js' ;
2328import { runSave } from '../save.js' ;
2429
2530const mockPathExists = fs . pathExists as jest . MockedFunction < typeof fs . pathExists > ;
2631const mockExeca = execa as jest . MockedFunction < typeof execa > ;
2732const mockPrompt = inquirer . prompt as jest . MockedFunction < typeof inquirer . prompt > ;
33+ const mockOfferAndCreateGitHubRepo = offerAndCreateGitHubRepo as jest . MockedFunction < typeof offerAndCreateGitHubRepo > ;
2834
2935const cwd = '/tmp/my-repo' ;
3036
@@ -125,14 +131,15 @@ describe('runSave', () => {
125131 . mockResolvedValueOnce ( { stdout : ' M file.ts' , stderr : '' , exitCode : 0 } )
126132 . mockResolvedValueOnce ( { stdout : '' , stderr : '' , exitCode : 0 } )
127133 . mockResolvedValueOnce ( { stdout : '' , stderr : '' , exitCode : 0 } )
134+ . mockResolvedValueOnce ( { stdout : 'https://github.com/user/repo.git' , stderr : '' , exitCode : 0 } )
128135 . mockResolvedValueOnce ( { stdout : '' , stderr : '' , exitCode : 0 } ) ;
129136 mockPrompt
130137 . mockResolvedValueOnce ( { saveChanges : true } )
131138 . mockResolvedValueOnce ( { message : 'Fix bug in save command' } ) ;
132139
133140 await runSave ( cwd ) ;
134141
135- expect ( mockExeca ) . toHaveBeenCalledTimes ( 4 ) ;
142+ expect ( mockExeca ) . toHaveBeenCalledTimes ( 5 ) ;
136143 expect ( mockExeca ) . toHaveBeenNthCalledWith ( 1 , 'git' , [ 'status' , '--porcelain' ] , {
137144 cwd,
138145 encoding : 'utf8' ,
@@ -146,7 +153,8 @@ describe('runSave', () => {
146153 '-m' ,
147154 'Fix bug in save command' ,
148155 ] , { cwd, stdio : 'inherit' } ) ;
149- expect ( mockExeca ) . toHaveBeenNthCalledWith ( 4 , 'git' , [ 'push' ] , {
156+ expect ( mockExeca ) . toHaveBeenNthCalledWith ( 4 , 'git' , [ 'remote' , 'get-url' , 'origin' ] , expect . any ( Object ) ) ;
157+ expect ( mockExeca ) . toHaveBeenNthCalledWith ( 5 , 'git' , [ 'push' ] , {
150158 cwd,
151159 stdio : 'inherit' ,
152160 } ) ;
@@ -161,6 +169,7 @@ describe('runSave', () => {
161169 . mockResolvedValueOnce ( { stdout : ' M file.ts' , stderr : '' , exitCode : 0 } )
162170 . mockResolvedValueOnce ( { stdout : '' , stderr : '' , exitCode : 0 } )
163171 . mockResolvedValueOnce ( { stdout : '' , stderr : '' , exitCode : 0 } )
172+ . mockResolvedValueOnce ( { stdout : 'https://github.com/user/repo.git' , stderr : '' , exitCode : 0 } )
164173 . mockRejectedValueOnce ( Object . assign ( new Error ( 'push failed' ) , { exitCode : 128 } ) ) ;
165174
166175 mockPrompt
@@ -200,6 +209,7 @@ describe('runSave', () => {
200209 . mockResolvedValueOnce ( { stdout : ' M file.ts' , stderr : '' , exitCode : 0 } )
201210 . mockResolvedValueOnce ( { stdout : '' , stderr : '' , exitCode : 0 } )
202211 . mockResolvedValueOnce ( { stdout : '' , stderr : '' , exitCode : 0 } )
212+ . mockResolvedValueOnce ( { stdout : 'https://github.com/user/repo.git' , stderr : '' , exitCode : 0 } )
203213 . mockRejectedValueOnce ( new Error ( 'network error' ) ) ;
204214
205215 mockPrompt
@@ -215,4 +225,48 @@ describe('runSave', () => {
215225 expect . stringContaining ( 'network error' ) ,
216226 ) ;
217227 } ) ;
228+
229+ it ( 'when no remote origin, offers to create GitHub repo and throws if user does not create' , async ( ) => {
230+ mockPathExists . mockResolvedValue ( true ) ;
231+ mockExeca
232+ . mockResolvedValueOnce ( { stdout : ' M file.ts' , stderr : '' , exitCode : 0 } )
233+ . mockResolvedValueOnce ( { stdout : '' , stderr : '' , exitCode : 0 } )
234+ . mockResolvedValueOnce ( { stdout : '' , stderr : '' , exitCode : 0 } )
235+ . mockRejectedValueOnce ( new Error ( 'no remote' ) ) ;
236+ mockOfferAndCreateGitHubRepo . mockResolvedValue ( false ) ;
237+
238+ mockPrompt
239+ . mockResolvedValueOnce ( { saveChanges : true } )
240+ . mockResolvedValueOnce ( { message : 'WIP' } ) ;
241+
242+ await expect ( runSave ( cwd ) ) . rejects . toThrow ( 'No remote origin' ) ;
243+ expect ( mockOfferAndCreateGitHubRepo ) . toHaveBeenCalledWith ( cwd ) ;
244+ expect ( consoleErrorSpy ) . toHaveBeenCalledWith (
245+ expect . stringContaining ( 'Set a remote' ) ,
246+ ) ;
247+ expect ( mockExeca ) . not . toHaveBeenCalledWith ( 'git' , [ 'push' ] , expect . any ( Object ) ) ;
248+ } ) ;
249+
250+ it ( 'when no remote origin and user creates GitHub repo, pushes successfully' , async ( ) => {
251+ mockPathExists . mockResolvedValue ( true ) ;
252+ mockExeca
253+ . mockResolvedValueOnce ( { stdout : ' M file.ts' , stderr : '' , exitCode : 0 } )
254+ . mockResolvedValueOnce ( { stdout : '' , stderr : '' , exitCode : 0 } )
255+ . mockResolvedValueOnce ( { stdout : '' , stderr : '' , exitCode : 0 } )
256+ . mockRejectedValueOnce ( new Error ( 'no remote' ) )
257+ . mockResolvedValueOnce ( { stdout : '' , stderr : '' , exitCode : 0 } ) ;
258+ mockOfferAndCreateGitHubRepo . mockResolvedValue ( true ) ;
259+
260+ mockPrompt
261+ . mockResolvedValueOnce ( { saveChanges : true } )
262+ . mockResolvedValueOnce ( { message : 'WIP' } ) ;
263+
264+ await runSave ( cwd ) ;
265+
266+ expect ( mockOfferAndCreateGitHubRepo ) . toHaveBeenCalledWith ( cwd ) ;
267+ expect ( mockExeca ) . toHaveBeenCalledWith ( 'git' , [ 'push' ] , { cwd, stdio : 'inherit' } ) ;
268+ expect ( consoleLogSpy ) . toHaveBeenCalledWith (
269+ expect . stringContaining ( 'Changes saved and pushed to GitHub successfully' ) ,
270+ ) ;
271+ } ) ;
218272} ) ;
0 commit comments