1- import { mkdir , rm , writeFile } from 'node:fs/promises' ;
1+ import { mkdir , rm , stat , writeFile } from 'node:fs/promises' ;
22import { join } from 'node:path' ;
33import { type SimpleGit , simpleGit } from 'simple-git' ;
4- import { expect } from 'vitest' ;
4+ import { afterAll , beforeAll , beforeEach , expect } from 'vitest' ;
55import {
66 getCurrentBranchOrTag ,
77 getGitRoot ,
@@ -12,149 +12,161 @@ import {
1212} from './git' ;
1313import { toUnixPath } from './transform' ;
1414
15- describe ( 'git utils in a git repo with a branch and commits' , ( ) => {
16- const baseDir = join ( process . cwd ( ) , 'tmp' , 'testing-git-repo' ) ;
17- const changesDir = join ( baseDir , 'changes-dir' ) ;
18- let git : SimpleGit ;
15+ // we need a separate folder that is not cleaned in `global-setup.ts`, otherwise the tests can't execute in parallel
16+ const gitTestFolder = 'git-test' ;
17+ describe ( 'git utils in a git repo' , ( ) => {
18+ const baseDir = join ( process . cwd ( ) , gitTestFolder ) ;
19+ let emptyGit : SimpleGit ;
1920
2021 beforeAll ( async ( ) => {
2122 await mkdir ( baseDir , { recursive : true } ) ;
22- await writeFile ( join ( baseDir , 'README.md' ) , '# hello-world\n' ) ;
23-
24- git = simpleGit ( baseDir ) ;
25- await git . init ( ) ;
26-
27- await git . addConfig ( 'user.name' , 'John Doe' ) ;
28- await git . addConfig ( 'user.email' , 'john.doe@example.com' ) ;
29-
30- await git . add ( 'README.md' ) ;
31- await git . commit ( 'Create README' ) ;
32-
33- await git . checkout ( [ 'master' ] ) ;
23+ emptyGit = simpleGit ( baseDir ) ;
24+ await emptyGit . init ( ) ;
25+ await emptyGit . addConfig ( 'user.name' , 'John Doe' ) ;
26+ await emptyGit . addConfig ( 'user.email' , 'john.doe@example.com' ) ;
3427 } ) ;
3528
3629 afterAll ( async ( ) => {
3730 await rm ( baseDir , { recursive : true , force : true } ) ;
3831 } ) ;
3932
40- beforeEach ( async ( ) => {
41- await git . checkout ( [ '-b' , 'feature-branch' ] ) ;
42- await git . checkout ( [ 'master' ] ) ;
33+ describe ( 'without a branch and commits' , ( ) => {
34+ it ( 'getCurrentBranchOrTag should throw if no branch or tag is given' , async ( ) => {
35+ await expect ( getCurrentBranchOrTag ( emptyGit ) ) . rejects . toThrow (
36+ 'Could not get current tag or branch.' ,
37+ ) ;
38+ } ) ;
4339 } ) ;
4440
45- afterEach ( async ( ) => {
46- // @TODO try why restore/stash/clean/reset hard etc does not work
47- await rm ( changesDir , { recursive : true , force : true } ) ;
48- await git . checkout ( [ 'master' ] ) ;
49- await git . deleteLocalBranch ( 'feature-branch' ) ;
50- } ) ;
41+ describe ( 'with a branch and commits clean' , ( ) => {
42+ beforeAll ( async ( ) => {
43+ await writeFile ( join ( baseDir , 'README.md' ) , '# hello-world\n' ) ;
44+ await emptyGit . add ( 'README.md' ) ;
45+ await emptyGit . commit ( 'Create README' ) ;
5146
52- it ( 'should log latest commit' , async ( ) => {
53- const gitCommitDateRegex =
54- / ^ ( M o n | T u e | W e d | T h u | F r i | S a t | S u n ) ( J a n | F e b | M a r | A p r | M a y | J u n | J u l | A u g | S e p | O c t | N o v | D e c ) \d { 1 , 2 } \d { 2 } : \d { 2 } : \d { 2 } \d { 4 } [ + | - ] \d { 4 } $ / ;
47+ await emptyGit . branch ( [ 'feature-branch' ] ) ;
48+ await emptyGit . checkout ( [ 'master' ] ) ;
49+ } ) ;
5550
56- await expect ( getLatestCommit ( git ) ) . resolves . toEqual ( {
57- hash : expect . stringMatching ( / ^ [ \d a - f ] { 40 } $ / ) ,
58- message : 'Create README' ,
59- author : 'John Doe' ,
60- date : expect . stringMatching ( gitCommitDateRegex ) ,
51+ afterAll ( async ( ) => {
52+ await emptyGit . checkout ( [ 'master' ] ) ;
53+ await emptyGit . deleteLocalBranch ( 'feature-branch' ) ;
6154 } ) ;
62- } ) ;
6355
64- it ( 'should find Git root ' , async ( ) => {
65- await expect ( getGitRoot ( git ) ) . resolves . toBe ( toUnixPath ( baseDir ) ) ;
66- } ) ;
56+ it ( 'should log latest commit ' , async ( ) => {
57+ const gitCommitDateRegex =
58+ / ^ ( M o n | T u e | W e d | T h u | F r i | S a t | S u n ) ( J a n | F e b | M a r | A p r | M a y | J u n | J u l | A u g | S e p | O c t | N o v | D e c ) \d { 1 , 2 } \d { 2 } : \d { 2 } : \d { 2 } \d { 4 } [ + | - ] \d { 4 } $ / ;
6759
68- it ( 'should convert absolute path to relative Git path' , async ( ) => {
69- await expect (
70- toGitPath ( join ( process . cwd ( ) , 'src' , 'utils.ts' ) ) ,
71- ) . resolves . toBe ( 'src/utils.ts' ) ;
72- } ) ;
60+ await expect ( getLatestCommit ( emptyGit ) ) . resolves . toEqual ( {
61+ hash : expect . stringMatching ( / ^ [ \d a - f ] { 40 } $ / ) ,
62+ message : 'Create README' ,
63+ author : 'John Doe' ,
64+ date : expect . stringMatching ( gitCommitDateRegex ) ,
65+ } ) ;
66+ } ) ;
7367
74- it ( 'should convert relative Windows path to relative Git path' , async ( ) => {
75- await expect ( toGitPath ( 'Backend\\API\\Startup.cs' ) ) . resolves . toBe (
76- 'Backend/API/Startup.cs' ,
77- ) ;
78- } ) ;
68+ it ( 'should find Git root' , async ( ) => {
69+ await expect ( getGitRoot ( emptyGit ) ) . resolves . toBe ( toUnixPath ( baseDir ) ) ;
70+ } ) ;
7971
80- it ( 'should keep relative Unix path as is (already a Git path) ' , async ( ) => {
81- await expect ( toGitPath ( 'Backend/API/Startup.cs' ) ) . resolves . toBe (
82- 'Backend/API/Startup.cs' ,
83- ) ;
84- } ) ;
72+ it ( 'should convert absolute path to relative Git path' , async ( ) => {
73+ await expect (
74+ toGitPath ( join ( baseDir , 'src' , 'utils.ts' ) , emptyGit ) ,
75+ ) . resolves . toBe ( 'src/utils.ts' ) ;
76+ } ) ;
8577
86- it ( 'guardAgainstLocalChanges should throw if history is dirty' , async ( ) => {
87- await mkdir ( changesDir , { recursive : true } ) ;
88- await writeFile ( join ( changesDir , 'change.md' ) , '# hello-change\n' ) ;
89- await expect ( guardAgainstLocalChanges ( git ) ) . rejects . toThrow (
90- 'Working directory needs to be clean before we you can proceed. Commit your local changes or stash them.' ,
91- ) ;
92- } ) ;
78+ it ( 'should convert relative Windows path to relative Git path' , async ( ) => {
79+ await expect (
80+ toGitPath ( 'Backend\\API\\Startup.cs' , emptyGit ) ,
81+ ) . resolves . toBe ( '../Backend/API/Startup.cs' ) ;
82+ } ) ;
9383
94- it ( 'guardAgainstLocalChanges should not throw if history is clean' , async ( ) => {
95- await expect ( guardAgainstLocalChanges ( git ) ) . resolves . toBeUndefined ( ) ;
96- } ) ;
84+ it ( 'should keep relative Unix path as is (already a Git path)' , async ( ) => {
85+ await expect ( toGitPath ( 'Backend/API/Startup.cs' , emptyGit ) ) . resolves . toBe (
86+ '../Backend/API/Startup.cs' ,
87+ ) ;
88+ } ) ;
9789
98- it ( 'safeCheckout should checkout target branch in clean state' , async ( ) => {
99- await expect ( git . branch ( ) ) . resolves . toEqual (
100- expect . objectContaining ( { current : 'master' } ) ,
101- ) ;
102- await expect (
103- safeCheckout ( 'feature-branch' , { } , git ) ,
104- ) . resolves . toBeUndefined ( ) ;
105- await expect ( git . branch ( ) ) . resolves . toEqual (
106- expect . objectContaining ( { current : 'feature-branch' } ) ,
107- ) ;
108- } ) ;
90+ it ( 'getCurrentBranchOrTag should log current branch' , async ( ) => {
91+ await expect ( getCurrentBranchOrTag ( emptyGit ) ) . resolves . toBe ( 'master' ) ;
92+ } ) ;
10993
110- it ( 'safeCheckout should throw if history is dirty' , async ( ) => {
111- await mkdir ( changesDir , { recursive : true } ) ;
112- await writeFile ( join ( changesDir , 'change.md' ) , '# hello-change\n' ) ;
113- await expect ( safeCheckout ( 'master' , { } , git ) ) . rejects . toThrow (
114- 'Working directory needs to be clean before we you can proceed. Commit your local changes or stash them.' ,
115- ) ;
116- } ) ;
94+ it ( 'guardAgainstLocalChanges should not throw if history is clean' , async ( ) => {
95+ await expect ( guardAgainstLocalChanges ( emptyGit ) ) . resolves . toBeUndefined ( ) ;
96+ } ) ;
11797
118- it ( 'safeCheckout should clean local changes and check out to feature-branch' , async ( ) => {
119- // needs to get reset to be clean
120- await mkdir ( changesDir , { recursive : true } ) ;
121- await writeFile ( join ( changesDir , 'change.md' ) , '# hello-change\n' ) ;
122- // needs to get cleaned to be clean
123- await writeFile ( join ( baseDir , 'README.md' ) , '# hello-world-2\n' ) ;
124-
125- await expect (
126- safeCheckout ( 'feature-branch' , { forceCleanStatus : true } , git ) ,
127- ) . resolves . toBeUndefined ( ) ;
128- await expect ( git . branch ( ) ) . resolves . toEqual (
129- expect . objectContaining ( { current : 'feature-branch' } ) ,
130- ) ;
131- await expect ( git . status ( ) ) . resolves . toEqual (
132- expect . objectContaining ( { files : [ ] } ) ,
133- ) ;
134- } ) ;
98+ it ( 'safeCheckout should checkout feature-branch in clean state' , async ( ) => {
99+ await expect (
100+ safeCheckout ( 'feature-branch' , { } , emptyGit ) ,
101+ ) . resolves . toBeUndefined ( ) ;
102+ await expect ( emptyGit . branch ( ) ) . resolves . toEqual (
103+ expect . objectContaining ( { current : 'feature-branch' } ) ,
104+ ) ;
105+ } ) ;
135106
136- it ( 'getCurrentBranchOrTag should log current branch' , async ( ) => {
137- await expect ( getCurrentBranchOrTag ( git ) ) . resolves . toBe ( 'master' ) ;
107+ it ( 'safeCheckout should throw if a given branch does not exist' , async ( ) => {
108+ await expect (
109+ safeCheckout ( 'non-existing-branch' , { } , emptyGit ) ,
110+ ) . rejects . toThrow (
111+ "pathspec 'non-existing-branch' did not match any file(s) known to git" ,
112+ ) ;
113+ } ) ;
138114 } ) ;
139- } ) ;
140115
141- describe ( 'git utils in a git repo without a branch and commits' , ( ) => {
142- const baseDir = join ( process . cwd ( ) , 'tmp' , 'testing-git-repo' ) ;
143- let git : SimpleGit ;
116+ describe ( 'with a branch and commits dirty' , ( ) => {
117+ const newFilePath = join ( baseDir , 'new-file.md' ) ;
144118
145- beforeAll ( async ( ) => {
146- await mkdir ( baseDir , { recursive : true } ) ;
147- git = simpleGit ( baseDir ) ;
148- await git . init ( ) ;
149- } ) ;
119+ beforeAll ( async ( ) => {
120+ await writeFile ( join ( baseDir , 'README.md' ) , '# hello-world\n' ) ;
121+ await emptyGit . add ( 'README.md' ) ;
122+ await emptyGit . commit ( 'Create README' ) ;
150123
151- afterAll ( async ( ) => {
152- await rm ( baseDir , { recursive : true , force : true } ) ;
153- } ) ;
124+ await emptyGit . branch ( [ 'feature-branch' ] ) ;
125+ await emptyGit . checkout ( [ 'master' ] ) ;
126+ } ) ;
154127
155- it ( 'getCurrentBranchOrTag should throw if no branch is given' , async ( ) => {
156- await expect ( getCurrentBranchOrTag ( git ) ) . rejects . toThrow (
157- 'Could not get current tag or branch.' ,
158- ) ;
128+ beforeEach ( async ( ) => {
129+ await writeFile ( newFilePath , '# New File\n' ) ;
130+ } ) ;
131+
132+ afterEach ( async ( ) => {
133+ try {
134+ const s = await stat ( newFilePath ) ;
135+ if ( s . isFile ( ) ) {
136+ await rm ( newFilePath ) ;
137+ }
138+ } catch {
139+ // file not present (already cleaned)
140+ }
141+ } ) ;
142+
143+ afterAll ( async ( ) => {
144+ await emptyGit . checkout ( [ 'master' ] ) ;
145+ await emptyGit . deleteLocalBranch ( 'feature-branch' ) ;
146+ } ) ;
147+
148+ it ( 'safeCheckout should clean local changes and check out to feature-branch' , async ( ) => {
149+ await expect (
150+ safeCheckout ( 'feature-branch' , { forceCleanStatus : true } , emptyGit ) ,
151+ ) . resolves . toBeUndefined ( ) ;
152+ await expect ( emptyGit . branch ( ) ) . resolves . toEqual (
153+ expect . objectContaining ( { current : 'feature-branch' } ) ,
154+ ) ;
155+ await expect ( emptyGit . status ( ) ) . resolves . toEqual (
156+ expect . objectContaining ( { files : [ ] } ) ,
157+ ) ;
158+ } ) ;
159+
160+ it ( 'safeCheckout should throw if history is dirty' , async ( ) => {
161+ await expect ( safeCheckout ( 'master' , { } , emptyGit ) ) . rejects . toThrow (
162+ 'Working directory needs to be clean before we you can proceed. Commit your local changes or stash them.' ,
163+ ) ;
164+ } ) ;
165+
166+ it ( 'guardAgainstLocalChanges should throw if history is dirty' , async ( ) => {
167+ await expect ( guardAgainstLocalChanges ( emptyGit ) ) . rejects . toThrow (
168+ 'Working directory needs to be clean before we you can proceed. Commit your local changes or stash them.' ,
169+ ) ;
170+ } ) ;
159171 } ) ;
160172} ) ;
0 commit comments