1
1
import * as azdev from "azure-devops-node-api" ;
2
2
import { AzureCliCredential } from "@azure/identity" ;
3
+ import * as TestPlanApi from "azure-devops-node-api/TestPlanApi" ;
4
+ import * as TestInterfaces from "azure-devops-node-api/interfaces/TestPlanInterfaces" ;
3
5
import * as fs from "fs" ;
4
6
import * as path from "path" ;
5
7
@@ -21,14 +23,9 @@ async function run() {
21
23
"AZURE_DEVOPS_TEST_PLAN_ID:" ,
22
24
process . env . AZURE_DEVOPS_TEST_PLAN_ID
23
25
) ;
24
- console . log (
25
- "AZURE_DEVOPS_TEST_SUITE_ID:" ,
26
- process . env . AZURE_DEVOPS_TEST_SUITE_ID
27
- ) ;
28
26
const orgUrl = process . env . AZURE_DEVOPS_ORG_URL ! ;
29
27
const project = process . env . AZURE_DEVOPS_PROJECT ! ;
30
28
const planId = parseInt ( process . env . AZURE_DEVOPS_TEST_PLAN_ID ! ) ;
31
- const suiteId = parseInt ( process . env . AZURE_DEVOPS_TEST_SUITE_ID ! ) ;
32
29
33
30
// Get Azure AD token using Azure CLI credential
34
31
const credential = new AzureCliCredential ( ) ;
@@ -47,126 +44,152 @@ async function run() {
47
44
const testApi = await connection . getTestApi ( ) ;
48
45
console . log ( "Test API client initialized successfully" ) ;
49
46
50
- const testCases = await testApi . getTestCases ( project , planId , suiteId ) ;
51
- console . log ( "Test cases fetched successfully" ) ;
52
- console . log (
53
- `Found ${ testCases . length } test cases in plan ${ planId } , suite ${ suiteId } `
54
- ) ;
55
- for ( const tc of testCases ) {
56
- if ( tc . testCase ) {
57
- console . log ( `Test case - ${ tc . testCase . id } ` ) ;
58
-
59
- // Fetch the work item JSON from the url
60
- if ( tc . testCase . url ) {
61
- try {
62
- // Use the Azure AD token for authentication
63
- const response = await fetch ( tc . testCase . url , {
64
- headers : {
65
- Authorization : `Bearer ${ token . token } ` ,
66
- "Content-Type" : "application/json" ,
67
- } ,
68
- } ) ;
69
- if ( ! response . ok ) {
70
- console . error (
71
- `Failed to fetch work item: ${ tc . testCase . url } , status: ${ response . status } `
72
- ) ;
73
- continue ;
74
- }
75
-
76
- // Define a minimal type for workItem
77
- type WorkItem = {
78
- fields ?: {
79
- [ key : string ] : any ;
80
- } ;
81
- } ;
82
- const workItem = ( await response . json ( ) ) as WorkItem ;
83
-
84
- // The content fianlly to write into the file
85
- const outputLines : string [ ] = [ ] ;
47
+ const suites = await getTestSuitesForPlan ( connection , project , planId ) ;
48
+ if ( suites ) {
49
+ for ( const suite of suites ) {
50
+ const suiteId = suite . id ;
51
+ console . log ( "AZURE_DEVOPS_TEST_SUITE_ID:" , suiteId ) ;
52
+ const testCases = await testApi . getTestCases ( project , planId , suiteId ) ;
53
+ console . log ( "Test cases fetched successfully" ) ;
54
+ console . log (
55
+ `Found ${ testCases . length } test cases in plan ${ planId } , suite ${ suiteId } `
56
+ ) ;
57
+ for ( const tc of testCases ) {
58
+ if ( tc . testCase ) {
59
+ console . log ( `Test case - ${ tc . testCase . id } ` ) ;
86
60
87
- // Get the URL of the work item
88
- outputLines . push (
89
- `#URL: https://msazure.visualstudio.com/Microsoft%20Teams%20Extensibility/_workitems/edit/${ tc . testCase . id } `
90
- ) ;
91
-
92
- // Get the title of the work itme
93
- const title = workItem . fields ?. [ "System.Title" ] ;
94
- if ( title ) {
95
- outputLines . push ( `#Title: ${ title } ` ) ;
96
- }
61
+ // Fetch the work item JSON from the url
62
+ if ( tc . testCase . url ) {
63
+ try {
64
+ // Use the Azure AD token for authentication
65
+ const response = await fetch ( tc . testCase . url , {
66
+ headers : {
67
+ Authorization : `Bearer ${ token . token } ` ,
68
+ "Content-Type" : "application/json" ,
69
+ } ,
70
+ } ) ;
71
+ if ( ! response . ok ) {
72
+ console . error (
73
+ `Failed to fetch work item: ${ tc . testCase . url } , status: ${ response . status } `
74
+ ) ;
75
+ continue ;
76
+ }
97
77
98
- // Get the author of the work item
99
- const assignedTo = workItem . fields ?. [ "System.AssignedTo" ] ;
100
- const uniqueName =
101
- assignedTo && typeof assignedTo === "object"
102
- ? assignedTo . uniqueName
103
- : undefined ;
104
- if ( uniqueName ) {
105
- outputLines . push ( `#Author: ${ uniqueName } ` ) ;
106
- }
78
+ // Define a minimal type for workItem
79
+ type WorkItem = {
80
+ fields ?: {
81
+ [ key : string ] : any ;
82
+ } ;
83
+ } ;
84
+ const workItem = ( await response . json ( ) ) as WorkItem ;
107
85
108
- //if (tags && tags.includes("VSCUSE")) {
109
- const steps = workItem . fields ?. [ "Microsoft.VSTS.TCM.Steps" ] ;
110
- if ( typeof steps === "string" ) {
111
- const stepBlocks = steps . match ( / < s t e p [ \s \S ] * ?< \/ s t e p > / gi) || [ ] ;
86
+ // The content fianlly to write into the file
87
+ const outputLines : string [ ] = [ ] ;
112
88
113
- stepBlocks . forEach ( ( stepBlock , idx ) => {
114
- const paramMatch = stepBlock . match (
115
- / < p a r a m e t e r i z e d S t r i n g [ ^ > ] * > ( [ \s \S ] * ? ) < \/ p a r a m e t e r i z e d S t r i n g > / i
89
+ // Get the URL of the work item
90
+ outputLines . push (
91
+ `#URL: https://msazure.visualstudio.com/Microsoft%20Teams%20Extensibility/_workitems/edit/ ${ tc . testCase . id } `
116
92
) ;
117
- let text = "" ;
118
- if ( paramMatch && paramMatch [ 1 ] ) {
119
- const html = paramMatch [ 1 ]
120
- . replace ( / & l t ; / g, "<" )
121
- . replace ( / & g t ; / g, ">" ) ;
122
- const pMatch =
123
- html . match ( / < p > ( [ \s \S ] * ?) < \/ p > / i) ||
124
- html . match ( / < P > ( [ \s \S ] * ?) < \/ P > / i) ;
125
- if ( pMatch && pMatch [ 1 ] ) {
126
- text = pMatch [ 1 ] . replace ( / < [ ^ > ] + > / g, "" ) . trim ( ) ;
127
- }
93
+
94
+ // Get the title of the work itme
95
+ const title = workItem . fields ?. [ "System.Title" ] ;
96
+ if ( title ) {
97
+ outputLines . push ( `#Title: ${ title } ` ) ;
128
98
}
129
- if ( ! text ) {
130
- const match =
131
- stepBlock . match ( / < p > ( [ \s \S ] * ?) < \/ p > / i) ||
132
- stepBlock . match ( / < P > ( [ \s \S ] * ?) < \/ P > / i) ;
133
- if ( match && match [ 1 ] ) {
134
- text = match [ 1 ] . replace ( / < [ ^ > ] + > / g, "" ) . trim ( ) ;
135
- }
99
+
100
+ // Get the author of the work item
101
+ const assignedTo = workItem . fields ?. [ "System.AssignedTo" ] ;
102
+ const uniqueName =
103
+ assignedTo && typeof assignedTo === "object"
104
+ ? assignedTo . uniqueName
105
+ : undefined ;
106
+ if ( uniqueName ) {
107
+ outputLines . push ( `#Author: ${ uniqueName } ` ) ;
136
108
}
137
- if ( text ) {
138
- outputLines . push ( text ) ;
109
+
110
+ //if (tags && tags.includes("VSCUSE")) {
111
+ const steps = workItem . fields ?. [ "Microsoft.VSTS.TCM.Steps" ] ;
112
+ if ( typeof steps === "string" ) {
113
+ const stepBlocks = steps . match ( / < s t e p [ \s \S ] * ?< \/ s t e p > / gi) || [ ] ;
114
+
115
+ stepBlocks . forEach ( ( stepBlock , idx ) => {
116
+ const paramMatch = stepBlock . match (
117
+ / < p a r a m e t e r i z e d S t r i n g [ ^ > ] * > ( [ \s \S ] * ?) < \/ p a r a m e t e r i z e d S t r i n g > / i
118
+ ) ;
119
+ let text = "" ;
120
+ if ( paramMatch && paramMatch [ 1 ] ) {
121
+ const html = paramMatch [ 1 ]
122
+ . replace ( / & l t ; / g, "<" )
123
+ . replace ( / & g t ; / g, ">" ) ;
124
+ const pMatch =
125
+ html . match ( / < p > ( [ \s \S ] * ?) < \/ p > / i) ||
126
+ html . match ( / < P > ( [ \s \S ] * ?) < \/ P > / i) ;
127
+ if ( pMatch && pMatch [ 1 ] ) {
128
+ text = pMatch [ 1 ] . replace ( / < [ ^ > ] + > / g, "" ) . trim ( ) ;
129
+ }
130
+ }
131
+ if ( ! text ) {
132
+ const match =
133
+ stepBlock . match ( / < p > ( [ \s \S ] * ?) < \/ p > / i) ||
134
+ stepBlock . match ( / < P > ( [ \s \S ] * ?) < \/ P > / i) ;
135
+ if ( match && match [ 1 ] ) {
136
+ text = match [ 1 ] . replace ( / < [ ^ > ] + > / g, "" ) . trim ( ) ;
137
+ }
138
+ }
139
+ if ( text ) {
140
+ outputLines . push ( text ) ;
141
+ }
142
+ } ) ;
143
+ const filePath = path . join ( outputDir , `${ tc . testCase . id } .txt` ) ;
144
+ fs . writeFileSync ( filePath , outputLines . join ( "\n" ) , {
145
+ encoding : "utf8" ,
146
+ } ) ;
147
+ console . log (
148
+ `Wrote steps for test case ${ tc . testCase . id } to ${ filePath } `
149
+ ) ;
150
+ console . log (
151
+ `The file content is: \n${ outputLines . join (
152
+ "\n"
153
+ ) } \n [End of file]`
154
+ ) ;
155
+ } else {
156
+ console . log ( `The type is: ${ typeof steps } ` ) ;
139
157
}
140
- } ) ;
141
- const filePath = path . join ( outputDir , `${ tc . testCase . id } .txt` ) ;
142
- fs . writeFileSync ( filePath , outputLines . join ( "\n" ) , {
143
- encoding : "utf8" ,
144
- } ) ;
145
- console . log (
146
- `Wrote steps for test case ${ tc . testCase . id } to ${ filePath } `
147
- ) ;
148
- console . log (
149
- `The file content is: \n${ outputLines . join (
150
- "\n"
151
- ) } \n [End of file]`
152
- ) ;
153
- } else {
154
- console . log ( `The type is: ${ typeof steps } ` ) ;
158
+ //}
159
+ } catch ( err ) {
160
+ console . error (
161
+ `Error fetching work item for test case ${ tc . testCase . id } :` ,
162
+ err
163
+ ) ;
164
+ }
155
165
}
156
- //}
157
- } catch ( err ) {
166
+ } else {
158
167
console . error (
159
- `Error fetching work item for test case ${ tc . testCase . id } :` ,
160
- err
168
+ `- Warning: Test case is undefined or missing details.`
161
169
) ;
162
170
}
163
171
}
164
- } else {
165
- console . error ( `- Warning: Test case is undefined or missing details.` ) ;
166
172
}
167
173
}
168
174
}
169
175
176
+ async function getTestSuitesForPlan (
177
+ connection : azdev . WebApi ,
178
+ project : string ,
179
+ planId : number
180
+ ) : Promise < TestInterfaces . TestSuite [ ] | undefined > {
181
+ try {
182
+ const testPlanApi : TestPlanApi . ITestPlanApi =
183
+ await connection . getTestPlanApi ( ) ;
184
+ const suites : TestInterfaces . TestSuite [ ] =
185
+ await testPlanApi . getTestSuitesForPlan ( project , planId ) ;
186
+ return suites ;
187
+ } catch ( err ) {
188
+ console . error ( "Error retrieving test suites:" , err ) ;
189
+ return undefined ;
190
+ }
191
+ }
192
+
170
193
run ( ) . catch ( ( err ) => {
171
194
console . error ( "Error:" , err ) ;
172
195
process . exit ( 1 ) ;
0 commit comments