2323 tcFileKey string
2424 tcFrameID string
2525 tcFrameName string
26+ tcScreenID string
2627)
2728
2829// CSV columns are mapped to test case fields:
@@ -43,13 +44,18 @@ By default, files must follow the path pattern:
4344
4445Alternatively, use --file-key (and optionally --frame-id, --frame-name) to
4546upload CSV files from any location without following the path convention.
47+
48+ You can also use --screen-id to upload by screen ID instead of frame ID.
4649` ,
4750 Example : ` # Upload using path convention
4851 momorph upload testcases .momorph/testcases/xxx/9276:19907-TOP_Channel.csv
4952
5053 # Upload from any location with explicit metadata
5154 momorph upload testcases ~/data/tc.csv --file-key=xxx --frame-id=9276:19907
5255
56+ # Upload using screen ID
57+ momorph upload testcases ~/data/tc.csv --screen-id=42
58+
5359 # Upload all testcases in a directory recursively
5460 momorph upload testcases --dir .momorph/testcases/ -r
5561
@@ -66,6 +72,7 @@ func init() {
6672 uploadTestcasesCmd .Flags ().StringVar (& tcFileKey , "file-key" , "" , "Figma file key (required when CSV is not in .momorph/ path)" )
6773 uploadTestcasesCmd .Flags ().StringVar (& tcFrameID , "frame-id" , "" , "Figma frame ID (optional, used with --file-key)" )
6874 uploadTestcasesCmd .Flags ().StringVar (& tcFrameName , "frame-name" , "" , "Frame name (optional, used with --file-key)" )
75+ uploadTestcasesCmd .Flags ().StringVar (& tcScreenID , "screen-id" , "" , "Screen ID (MoMorph integer, alternative to --frame-id)" )
6976 uploadCmd .AddCommand (uploadTestcasesCmd )
7077}
7178
@@ -91,8 +98,13 @@ func runUploadTestcases(cmd *cobra.Command, args []string) error {
9198 return nil
9299 }
93100
94- // Determine if using flags mode (--file-key provided)
95- useFlags := tcFileKey != ""
101+ // Validate conflicting flags
102+ if tcScreenID != "" && (tcFileKey != "" || tcFrameID != "" ) {
103+ return fmt .Errorf ("--screen-id cannot be used together with --file-key or --frame-id" )
104+ }
105+
106+ // Determine if using flags mode (--file-key or --screen-id provided)
107+ useFlags := tcFileKey != "" || tcScreenID != ""
96108
97109 // Build parsed metadata from flags when in flags mode
98110 var flagsParsed * upload.ParsedFilePath
@@ -116,8 +128,9 @@ func runUploadTestcases(cmd *cobra.Command, args []string) error {
116128 if ! useFlags {
117129 fmt .Println ("\n Make sure files are in the correct path format:" )
118130 fmt .Println (" .momorph/testcases/{file_key}/{frame_id}-{frame_name}.csv" )
119- fmt .Println ("\n Or use --file-key to upload from any location:" )
131+ fmt .Println ("\n Or use --file-key or --screen-id to upload from any location:" )
120132 fmt .Println (" momorph upload testcases myfile.csv --file-key=<figma_file_key>" )
133+ fmt .Println (" momorph upload testcases myfile.csv --screen-id=<screen_id>" )
121134 }
122135 return nil
123136 }
@@ -147,8 +160,12 @@ func runUploadTestcases(cmd *cobra.Command, args []string) error {
147160 parsed , _ = upload .ParseFilePath (f )
148161 }
149162 fmt .Printf (" - %s\n " , filepath .Base (f ))
150- fmt .Printf (" File Key: %s\n " , parsed .FileKey )
151- fmt .Printf (" Frame ID: %s\n " , parsed .FrameID )
163+ if tcScreenID != "" {
164+ fmt .Printf (" Screen ID: %s\n " , tcScreenID )
165+ } else {
166+ fmt .Printf (" File Key: %s\n " , parsed .FileKey )
167+ fmt .Printf (" Frame ID: %s\n " , parsed .FrameID )
168+ }
152169 fmt .Printf (" Frame Name: %s\n " , parsed .FrameName )
153170 }
154171 return nil
@@ -163,7 +180,7 @@ func runUploadTestcases(cmd *cobra.Command, args []string) error {
163180
164181 // Upload files
165182 fmt .Printf ("\n Uploading %d test case file(s)...\n " , len (validFiles ))
166- results := uploadTestcaseFiles (ctx , client , validFiles , flagsParsed , tcUploadContinue )
183+ results := uploadTestcaseFiles (ctx , client , validFiles , flagsParsed , tcScreenID , tcUploadContinue )
167184
168185 // Combine with skipped files
169186 allResults := append (skipped , results ... )
@@ -174,7 +191,7 @@ func runUploadTestcases(cmd *cobra.Command, args []string) error {
174191 return nil
175192}
176193
177- func uploadTestcaseFiles (ctx context.Context , client * graphql.Client , files []string , flagsParsed * upload.ParsedFilePath , continueOnError bool ) []upload.UploadResult {
194+ func uploadTestcaseFiles (ctx context.Context , client * graphql.Client , files []string , flagsParsed * upload.ParsedFilePath , screenID string , continueOnError bool ) []upload.UploadResult {
178195 var results []upload.UploadResult
179196
180197 for i , file := range files {
@@ -188,7 +205,7 @@ func uploadTestcaseFiles(ctx context.Context, client *graphql.Client, files []st
188205 fileName := filepath .Base (file )
189206 fmt .Printf (" [%d/%d] %s " , i + 1 , len (files ), fileName )
190207
191- result := uploadSingleTestcaseFile (ctx , client , file , flagsParsed )
208+ result := uploadSingleTestcaseFile (ctx , client , file , flagsParsed , screenID )
192209 results = append (results , result )
193210
194211 switch result .Status {
@@ -211,9 +228,92 @@ func uploadTestcaseFiles(ctx context.Context, client *graphql.Client, files []st
211228
212229// uploadSingleTestcaseFile uploads a single testcase CSV file. When flagsParsed
213230// is non-nil it is used as metadata instead of parsing from the file path.
214- func uploadSingleTestcaseFile (ctx context.Context , client * graphql.Client , filePath string , flagsParsed * upload.ParsedFilePath ) upload.UploadResult {
231+ // When screenID != "", the frame is resolved by screen_id instead of file-key + frame-id.
232+ func uploadSingleTestcaseFile (ctx context.Context , client * graphql.Client , filePath string , flagsParsed * upload.ParsedFilePath , screenID string ) upload.UploadResult {
215233 fileName := filepath .Base (filePath )
216234
235+ // Screen ID mode: resolve frame by screen_id directly
236+ if screenID != "" {
237+ // Parse CSV file
238+ frameName := ""
239+ if flagsParsed != nil {
240+ frameName = flagsParsed .FrameName
241+ }
242+ content , err := upload .ParseTestcasesCSV (filePath , frameName )
243+ if err != nil {
244+ return upload.UploadResult {
245+ FilePath : filePath ,
246+ FileName : fileName ,
247+ Status : upload .StatusFailed ,
248+ Error : err ,
249+ Message : fmt .Sprintf ("Failed to parse CSV: %v" , err ),
250+ }
251+ }
252+
253+ if len (content .TestCases ) == 0 {
254+ return upload.UploadResult {
255+ FilePath : filePath ,
256+ FileName : fileName ,
257+ Status : upload .StatusSkipped ,
258+ Message : "CSV file contains no test cases" ,
259+ }
260+ }
261+
262+ logger .Debug ("Parsed %d test cases from %s" , len (content .TestCases ), fileName )
263+
264+ // Check if test cases already exist for this screen
265+ existingTestCases , err := client .GetFrameTestCasesByScreenID (ctx , screenID )
266+ if err != nil {
267+ logger .Debug ("No existing test cases found: %v" , err )
268+ }
269+
270+ if len (existingTestCases ) > 0 {
271+ logger .Debug ("Updating existing test case ID: %d" , existingTestCases [0 ].ID )
272+ _ , err = client .UpdateFrameTestcase (ctx , existingTestCases [0 ].ID , content )
273+ if err != nil {
274+ return upload.UploadResult {
275+ FilePath : filePath ,
276+ FileName : fileName ,
277+ Status : upload .StatusFailed ,
278+ Error : err ,
279+ Message : fmt .Sprintf ("Failed to update test case: %v" , err ),
280+ }
281+ }
282+ } else {
283+ // Get frame by screen_id to get internal ID
284+ frame , err := client .GetFrameByScreenID (ctx , screenID )
285+ if err != nil {
286+ return upload.UploadResult {
287+ FilePath : filePath ,
288+ FileName : fileName ,
289+ Status : upload .StatusFailed ,
290+ Error : err ,
291+ Message : fmt .Sprintf ("Frame not found for screen_id=%s: %v" , screenID , err ),
292+ }
293+ }
294+
295+ logger .Debug ("Creating new test case for frame ID: %d (screen_id=%s)" , frame .ID , screenID )
296+
297+ _ , err = client .InsertFrameTestcase (ctx , frame .ID , content )
298+ if err != nil {
299+ return upload.UploadResult {
300+ FilePath : filePath ,
301+ FileName : fileName ,
302+ Status : upload .StatusFailed ,
303+ Error : err ,
304+ Message : fmt .Sprintf ("Failed to insert test case: %v" , err ),
305+ }
306+ }
307+ }
308+
309+ return upload.UploadResult {
310+ FilePath : filePath ,
311+ FileName : fileName ,
312+ Status : upload .StatusSuccess ,
313+ Message : fmt .Sprintf ("Uploaded %d test cases" , len (content .TestCases )),
314+ }
315+ }
316+
217317 // Resolve metadata: use flags or parse from path
218318 var parsed * upload.ParsedFilePath
219319 if flagsParsed != nil {
@@ -261,7 +361,7 @@ func uploadSingleTestcaseFile(ctx context.Context, client *graphql.Client, fileP
261361 FilePath : filePath ,
262362 FileName : fileName ,
263363 Status : upload .StatusFailed ,
264- Message : "frame-id is required for uploading test cases (use --frame-id flag)" ,
364+ Message : "frame-id is required for uploading test cases (use --frame-id or --screen-id flag)" ,
265365 }
266366 }
267367
0 commit comments