@@ -139,4 +139,109 @@ describe('Process Instance Integration Tests (requires Camunda 8 at localhost:80
139139 assert . ok ( outputWithAlias . includes ( 'completed' ) , 'Output with await alias should indicate process completed' ) ;
140140 assert . ok ( outputWithAlias . includes ( 'variables' ) , 'Output with await alias should contain variables' ) ;
141141 } ) ;
142+
143+ test ( 'get process instance with --diagram flag saves PNG to file' , async ( ) => {
144+ // Deploy and create a process instance
145+ await deploy ( [ 'tests/fixtures/simple.bpmn' ] , { } ) ;
146+ const result = await createProcessInstance ( {
147+ processDefinitionId : 'simple-process' ,
148+ } ) ;
149+
150+ assert . ok ( result , 'Create result should exist' ) ;
151+ const instanceKey = result . processInstanceKey . toString ( ) ;
152+
153+ // Run CLI command to generate diagram with --output flag
154+ const { execSync } = await import ( 'node:child_process' ) ;
155+ const { mkdtempSync } = await import ( 'node:fs' ) ;
156+ const { tmpdir } = await import ( 'node:os' ) ;
157+ const tmpDir = mkdtempSync ( join ( tmpdir ( ) , 'c8ctl-diagram-test-' ) ) ;
158+ const outputPath = join ( tmpDir , 'diagram.png' ) ;
159+
160+ try {
161+ const output = execSync (
162+ `node src/index.ts get pi --key ${ instanceKey } --diagram --output ${ outputPath } ` ,
163+ { encoding : 'utf8' , cwd : process . cwd ( ) , stdio : 'pipe' }
164+ ) ;
165+
166+ // Verify the file was created
167+ assert . ok ( existsSync ( outputPath ) , 'Diagram PNG file should be created' ) ;
168+
169+ // Verify the file has content (PNG files start with specific bytes)
170+ const { readFileSync } = await import ( 'node:fs' ) ;
171+ const fileContent = readFileSync ( outputPath ) ;
172+ assert . ok ( fileContent . length > 0 , 'PNG file should have content' ) ;
173+ // Verify PNG signature (starts with 0x89504E47)
174+ assert . strictEqual ( fileContent [ 0 ] , 0x89 , 'PNG file should start with PNG signature byte 1' ) ;
175+ assert . strictEqual ( fileContent [ 1 ] , 0x50 , 'PNG file should start with PNG signature byte 2' ) ;
176+ assert . strictEqual ( fileContent [ 2 ] , 0x4E , 'PNG file should start with PNG signature byte 3' ) ;
177+ assert . strictEqual ( fileContent [ 3 ] , 0x47 , 'PNG file should start with PNG signature byte 4' ) ;
178+
179+ // Verify success message in output
180+ assert . ok ( output . includes ( 'Diagram saved' ) , 'Output should indicate diagram was saved' ) ;
181+ } finally {
182+ // Cleanup: remove temp directory
183+ const { rmSync } = await import ( 'node:fs' ) ;
184+ rmSync ( tmpDir , { recursive : true , force : true } ) ;
185+ }
186+ } ) ;
187+
188+ test ( 'get process instance with --diagram flag without --output prints inline' , async ( ) => {
189+ // Deploy and create a process instance
190+ await deploy ( [ 'tests/fixtures/simple.bpmn' ] , { } ) ;
191+ const result = await createProcessInstance ( {
192+ processDefinitionId : 'simple-process' ,
193+ } ) ;
194+
195+ assert . ok ( result , 'Create result should exist' ) ;
196+ const instanceKey = result . processInstanceKey . toString ( ) ;
197+
198+ // Run CLI command to generate diagram without --output flag
199+ const { execSync } = await import ( 'node:child_process' ) ;
200+
201+ try {
202+ const output = execSync (
203+ `node src/index.ts get pi --key ${ instanceKey } --diagram` ,
204+ { encoding : 'utf8' , cwd : process . cwd ( ) , stdio : 'pipe' }
205+ ) ;
206+
207+ // Verify iTerm2 inline image protocol is present in output
208+ // The protocol uses OSC 1337 escape sequence: \x1b]1337;File=...
209+ assert . ok ( output . includes ( '\x1b]1337;File=' ) , 'Output should contain iTerm2 inline image protocol' ) ;
210+ assert . ok ( output . includes ( 'inline=1' ) , 'Output should indicate inline display mode' ) ;
211+ } catch ( error : any ) {
212+ // On systems without Chrome/Chromium, this test may fail
213+ // Verify it fails with a helpful error message
214+ if ( error . stderr && error . stderr . includes ( 'No Chrome or Chromium browser found' ) ) {
215+ assert . ok ( true , 'Test skipped: Chrome/Chromium not installed' ) ;
216+ } else {
217+ throw error ;
218+ }
219+ }
220+ } ) ;
221+
222+ test ( 'get process instance with --diagram handles non-existent process instance' , async ( ) => {
223+ // Run CLI command with a non-existent process instance key
224+ const { execSync } = await import ( 'node:child_process' ) ;
225+ const nonExistentKey = '9999999999999' ;
226+
227+ try {
228+ execSync (
229+ `node src/index.ts get pi --key ${ nonExistentKey } --diagram` ,
230+ { encoding : 'utf8' , cwd : process . cwd ( ) , stdio : 'pipe' }
231+ ) ;
232+ assert . fail ( 'Should have thrown an error for non-existent process instance' ) ;
233+ } catch ( error : any ) {
234+ // CLI should exit with non-zero code
235+ assert . ok ( error . status !== 0 , 'CLI should exit with non-zero status for non-existent process instance' ) ;
236+ // Check that error output contains an error message
237+ const hasErrorMessage = error . stderr && (
238+ error . stderr . includes ( 'Failed' ) ||
239+ error . stderr . includes ( 'NOT_FOUND' ) ||
240+ error . stderr . includes ( '✗' ) ||
241+ error . stderr . includes ( 'Error' )
242+ ) ;
243+ assert . ok ( hasErrorMessage ,
244+ `CLI should output error message for non-existent process instance. Got stderr: ${ error . stderr } ` ) ;
245+ }
246+ } ) ;
142247} ) ;
0 commit comments