diff --git a/center/build.gradle b/center/build.gradle index 47fcb1622..0d948641e 100644 --- a/center/build.gradle +++ b/center/build.gradle @@ -27,9 +27,12 @@ tasks.withType(JavaCompile) { } dependencies { - testImplementation 'org.mockito:mockito-core:3.12.4' - testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: springBootWebVersion - testImplementation 'me.paulschwarz:spring-dotenv:2.3.0' + compileOnly 'dev.langchain4j:langchain4j:0.11.0' + compileOnly 'dev.langchain4j:langchain4j-pinecone:0.11.0' + implementation 'org.dom4j:dom4j:2.1.4' + testCompile 'org.mockito:mockito-core:3.12.4' + testCompile group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: springBootWebVersion + testCompile 'me.paulschwarz:spring-dotenv:2.3.0' compile project(":common") compile project(":taps_to_cases:runner") diff --git a/center/src/main/java/com/microsoft/hydralab/center/controller/TestDetailController.java b/center/src/main/java/com/microsoft/hydralab/center/controller/TestDetailController.java index 0cdbd1b4e..0ae79721e 100644 --- a/center/src/main/java/com/microsoft/hydralab/center/controller/TestDetailController.java +++ b/center/src/main/java/com/microsoft/hydralab/center/controller/TestDetailController.java @@ -8,6 +8,7 @@ import com.alibaba.fastjson.JSONObject; import com.microsoft.hydralab.center.service.StorageTokenManageService; import com.microsoft.hydralab.center.service.TestDataService; +import com.microsoft.hydralab.center.service.generation.MaestroCaseGenerationService; import com.microsoft.hydralab.common.entity.agent.Result; import com.microsoft.hydralab.common.entity.center.SysUser; import com.microsoft.hydralab.common.entity.common.AndroidTestUnit; @@ -26,9 +27,11 @@ import com.microsoft.hydralab.common.util.FileUtil; import com.microsoft.hydralab.common.util.HydraLabRuntimeException; import com.microsoft.hydralab.common.util.LogUtils; +import com.microsoft.hydralab.common.util.PageNode; import com.microsoft.hydralab.t2c.runner.T2CJsonGenerator; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.Assertions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; @@ -47,11 +50,13 @@ import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.List; import static com.microsoft.hydralab.center.util.CenterConstant.CENTER_TEMP_FILE_DIR; @@ -72,6 +77,8 @@ public class TestDetailController { AttachmentService attachmentService; @Resource StorageServiceClientProxy storageServiceClientProxy; + @Resource + MaestroCaseGenerationService maestroCaseGenerationService; /** * Authenticated USER: @@ -412,4 +419,52 @@ public Result generateT2CJsonFromSmartTest(@CurrentSecurityContext SysUs return Result.ok(t2cJson); } + @GetMapping(value = {"/api/test/generateMaestro/{fileId}"}, produces = MediaType.APPLICATION_JSON_VALUE) + public Result generateMaestroFromSmartTest(@CurrentSecurityContext SysUser requestor, + @PathVariable(value = "fileId") String fileId, + @RequestParam(value = "testRunId") String testRunId, + HttpServletResponse response) throws IOException { + if (requestor == null) { + return Result.error(HttpStatus.UNAUTHORIZED.value(), "unauthorized"); + } + + File graphZipFile = loadGraphFile(fileId); + File graphFile = new File(graphZipFile.getParentFile().getAbsolutePath(), Const.SmartTestConfig.GRAPH_FILE_NAME); + TestRun testRun = testDataService.findTestRunById(testRunId); + TestTask testTask = testDataService.getTestTaskDetail(testRun.getTestTaskId()); + + PageNode rootNode = maestroCaseGenerationService.parserXMLToPageNode(graphFile.getAbsolutePath()); + Assertions.assertNotNull(rootNode, "parser xml to page node failed"); + rootNode.setPageName(testTask.getPkgName()); + System.out.println(rootNode); + List explorePaths = new ArrayList<>(); + maestroCaseGenerationService.explorePageNodePath(rootNode, "", "", explorePaths); + File caseZipFile = maestroCaseGenerationService.generateCaseFile(rootNode, explorePaths); + + if (caseZipFile == null) { + return Result.error(HttpStatus.BAD_REQUEST.value(), "The file was not downloaded"); + } + try { + FileInputStream in = new FileInputStream(caseZipFile); + ServletOutputStream out = response.getOutputStream(); + response.setContentType("application/octet-stream;charset=UTF-8"); + response.setHeader("Content-Disposition", "attachment;filename=" + caseZipFile.getName()); + int len; + byte[] buffer = new byte[1024 * 10]; + while ((len = in.read(buffer)) != -1) { + out.write(buffer, 0, len); + } + out.flush(); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + response.flushBuffer(); + caseZipFile.delete(); + } + + return Result.ok(); + } + } diff --git a/center/src/main/java/com/microsoft/hydralab/center/service/LongChainExample.java b/center/src/main/java/com/microsoft/hydralab/center/service/LongChainExample.java new file mode 100644 index 000000000..8aa738f25 --- /dev/null +++ b/center/src/main/java/com/microsoft/hydralab/center/service/LongChainExample.java @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package com.microsoft.hydralab.center.service; + +import dev.langchain4j.model.input.structured.StructuredPrompt; + +/** + * @author zhoule + * @date 07/13/2023 + */ + +public class LongChainExample { + + @StructuredPrompt({ + "I want you to act as a software tester. I will provide a route map of a mobile application and it will be your job to write a test case. ", + "The case should be in maestro script format. This is a maestro example", + "{{maestroExample}}", + "Firstly I will introduce the format of the route map.", + "1. It is a unidirectional ordered graph in xml format, the nodes attribute are the pages of app and the id property of each node is the unique id of page. " + + "By the way the id of node equals -1 means the app has not been opened.", + "2. The edges attributes means the only way of jumping from a page to another page. The source property is the unique id of original page and the target property " + + "is the unique id of the page after jumping. The attvalue of each edge means the operation type such launch app, click button, click testview etc..", + "The commands that maestro supported is in the site https://maestro.mobile.dev/api-reference/commands.", + "Requirements:", + "1. the case should start from node which id is -1.", + "2. the case must follow the direction of the edge.", + "3. the case should jump as many pages as possible of the app.", + "4. the page can be visited only once", + "5. you can't use the back command", + "6. add comment to case declare current page id", + "The first route map is {{routeMap}}", + "please generate a maestro script for this route map." + }) + static class MaestroCaseGeneration { + + String maestroExample; + String routeMap; + } + +} diff --git a/center/src/main/java/com/microsoft/hydralab/center/service/generation/AbstractCaseGeneration.java b/center/src/main/java/com/microsoft/hydralab/center/service/generation/AbstractCaseGeneration.java new file mode 100644 index 000000000..838640aab --- /dev/null +++ b/center/src/main/java/com/microsoft/hydralab/center/service/generation/AbstractCaseGeneration.java @@ -0,0 +1,112 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package com.microsoft.hydralab.center.service.generation; + +import com.microsoft.hydralab.common.util.PageNode; +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.dom4j.io.SAXReader; +import org.springframework.util.StringUtils; + +import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author zhoule + * @date 07/21/2023 + */ + +public abstract class AbstractCaseGeneration { + public PageNode parserXMLToPageNode(String xmlFilePath) { + // read xml file, get page node and action info + Document document = null; + SAXReader saxReader = new SAXReader(); + try { + document = saxReader.read(xmlFilePath); + } catch (DocumentException e) { + throw new RuntimeException(e); + } + List pages = document.getRootElement().element("graph").element("nodes").elements("node"); + List actions = document.getRootElement().element("graph").element("edges").elements("edge"); + + Map pageNodes = new HashMap<>(); + // init page node + for (Element page : pages) { + PageNode pageNode = new PageNode(); + int id = Integer.parseInt(page.attributeValue("id")); + pageNode.setId(id); + pageNodes.put(id, pageNode); + } + // init action info + for (Element action : actions) { + int source = Integer.parseInt(action.attributeValue("source")); + int target = Integer.parseInt(action.attributeValue("target")); + if (source == target) { + continue; + } + int actionId = Integer.parseInt(action.attributeValue("id")); + //link action to page + pageNodes.get(source).getActionInfoList().add(parserAction(action)); + //link page to page + pageNodes.get(source).getChildPageNodeMap().put(actionId, pageNodes.get(target)); + } + return pageNodes.get(0); + } + + private PageNode.ActionInfo parserAction(Element element) { + PageNode.ActionInfo actionInfo = new PageNode.ActionInfo(); + Map arguments = new HashMap<>(); + actionInfo.setId(Integer.parseInt(element.attributeValue("id"))); + actionInfo.setActionType("click"); + + PageNode.ElementInfo elementInfo = new PageNode.ElementInfo(); + String sourceCode = element.element("attvalues").element("attvalue").attributeValue("value"); + elementInfo.setText(extractElementAttr("Text", sourceCode)); + elementInfo.setClassName(extractElementAttr("Class", sourceCode)); + elementInfo.setClickable(Boolean.parseBoolean(extractElementAttr("Clickable", sourceCode))); + elementInfo.setResourceId(extractElementAttr("ResourceID", sourceCode)); + actionInfo.setTestElement(elementInfo); + if (!StringUtils.isEmpty(elementInfo.getText())) { + arguments.put("defaultValue", elementInfo.getText()); + } else if (!StringUtils.isEmpty(elementInfo.getResourceId())) { + arguments.put("id", elementInfo.getResourceId()); + } + actionInfo.setArguments(arguments); + return actionInfo; + } + + private String extractElementAttr(String attrName, String elementStr) { + String[] attrs = elementStr.split(attrName + ": "); + if (attrs.length > 1 && !attrs[1].startsWith(",")) { + return attrs[1].split(",")[0]; + } + return ""; + } + + /** + * explore all path of page node + * + * @param pageNode + * @param nodePath + * @param action + * @param explorePaths + */ + public void explorePageNodePath(PageNode pageNode, String nodePath, String action, List explorePaths) { + if (pageNode.getChildPageNodeMap().isEmpty()) { + explorePaths.add(new PageNode.ExplorePath(nodePath + "_" + pageNode.getId(), action)); + return; + } + for (Map.Entry entry : pageNode.getChildPageNodeMap().entrySet()) { + explorePageNodePath(entry.getValue(), StringUtils.isEmpty(nodePath) ? String.valueOf(pageNode.getId()) : nodePath + "_" + pageNode.getId(), + StringUtils.isEmpty(action) ? String.valueOf(entry.getKey()) : action + "," + entry.getKey(), explorePaths); + } + } + + public abstract File generateCaseFile(PageNode pageNode, List explorePaths); + + public abstract File generateCaseFile(PageNode pageNode, PageNode.ExplorePath explorePaths, File caseFolder); +} diff --git a/center/src/main/java/com/microsoft/hydralab/center/service/generation/MaestroCaseGenerationService.java b/center/src/main/java/com/microsoft/hydralab/center/service/generation/MaestroCaseGenerationService.java new file mode 100644 index 000000000..cf22a7e71 --- /dev/null +++ b/center/src/main/java/com/microsoft/hydralab/center/service/generation/MaestroCaseGenerationService.java @@ -0,0 +1,107 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package com.microsoft.hydralab.center.service.generation; + +import com.microsoft.hydralab.center.util.CenterConstant; +import com.microsoft.hydralab.common.util.DateUtil; +import com.microsoft.hydralab.common.util.FileUtil; +import com.microsoft.hydralab.common.util.HydraLabRuntimeException; +import com.microsoft.hydralab.common.util.PageNode; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * @author zhoule + * @date 07/14/2023 + */ + +@Service +public class MaestroCaseGenerationService extends AbstractCaseGeneration { + /** + * generate maestro case files and zip them + * + * @param pageNode + * @param explorePaths + * @return + */ + @Override + public File generateCaseFile(PageNode pageNode, List explorePaths) { + // create temp folder to store case files + File tempFolder = new File(CenterConstant.CENTER_TEMP_FILE_DIR, DateUtil.fileNameDateFormat.format(new Date())); + if (!tempFolder.exists()) { + tempFolder.mkdirs(); + } + // generate case files + for (PageNode.ExplorePath explorePath : explorePaths) { + generateCaseFile(pageNode, explorePath, tempFolder); + } + if (tempFolder.listFiles().length == 0) { + return null; + } + // zip temp folder + File zipFile = new File(tempFolder.getParent() + "/" + tempFolder.getName() + ".zip"); + FileUtil.zipFile(tempFolder.getAbsolutePath(), zipFile.getAbsolutePath()); + FileUtil.deleteFile(tempFolder); + return zipFile; + } + + @Override + public File generateCaseFile(PageNode pageNode, PageNode.ExplorePath explorePath, File caseFolder) { + File maestroCaseFile = new File(caseFolder, explorePath.getPath() + ".yaml"); + String caseContent = buildConfigSection(pageNode.getPageName()); + caseContent += buildDelimiter(); + caseContent += buildCommandSection("launch", null); + String[] actionIds = explorePath.getActions().split(","); + PageNode pageNodeCopy = pageNode; + for (String actionId : actionIds) { + PageNode.ActionInfo action = pageNodeCopy.getActionInfoList().stream().filter(actionInfo -> actionInfo.getId() == Integer.parseInt(actionId)).findFirst().get(); + caseContent += buildCommandSection(action.getActionType(), action.getArguments()); + pageNodeCopy = pageNodeCopy.getChildPageNodeMap().get(Integer.parseInt(actionId)); + } + caseContent += buildCommandSection("stop", null); + FileUtil.writeToFile(caseContent, maestroCaseFile.getAbsolutePath()); + return maestroCaseFile; + } + + private String buildConfigSection(String appId) { + return "appId: " + appId + "\n"; + } + + private String buildDelimiter() { + return "---\n"; + } + + private String buildCommandSection(String actionType, Map arguments) { + String command = "-"; + switch (actionType) { + case "launch": + command = command + " launchApp\n"; + break; + case "click": + command = command + " tapOn:"; + if (arguments.size() == 0) { + throw new HydraLabRuntimeException("arguments is empty"); + } + if (arguments.containsKey("defaultValue")) { + command = command + " " + arguments.get("defaultValue") + "\n"; + break; + } + command = command + "\n"; + for (String key : arguments.keySet()) { + command = command + " " + key + ": \"" + arguments.get(key) + "\"\n"; + } + break; + case "stop": + command = command + " stopApp\n"; + break; + default: + throw new HydraLabRuntimeException("Unsupported action type: " + actionType); + } + return command; + } +} diff --git a/center/src/main/java/com/microsoft/hydralab/center/service/generation/T2CCaseGenerationService.java b/center/src/main/java/com/microsoft/hydralab/center/service/generation/T2CCaseGenerationService.java new file mode 100644 index 000000000..91f91d2c4 --- /dev/null +++ b/center/src/main/java/com/microsoft/hydralab/center/service/generation/T2CCaseGenerationService.java @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package com.microsoft.hydralab.center.service.generation; + +import com.microsoft.hydralab.common.util.PageNode; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.util.List; + +/** + * @author zhoule + * @date 07/21/2023 + */ + +@Service +public class T2CCaseGenerationService extends AbstractCaseGeneration { + @Override + public File generateCaseFile(PageNode pageNode, List explorePaths) { + return null; + } + + @Override + public File generateCaseFile(PageNode pageNode, PageNode.ExplorePath explorePaths, File caseFolder) { + return null; + } +} diff --git a/center/src/main/resources/prompts/case_generation/_.template b/center/src/main/resources/prompts/case_generation/_.template new file mode 100644 index 000000000..e69de29bb diff --git a/center/src/main/resources/prompts/exploration/_.template b/center/src/main/resources/prompts/exploration/_.template new file mode 100644 index 000000000..e69de29bb diff --git a/center/src/main/resources/prompts/result_analysis/exception_analysis.template b/center/src/main/resources/prompts/result_analysis/exception_analysis.template new file mode 100644 index 000000000..e69de29bb diff --git a/center/src/test/java/com/microsoft/hydralab/center/service/LongChainExampleTest.java b/center/src/test/java/com/microsoft/hydralab/center/service/LongChainExampleTest.java new file mode 100644 index 000000000..5963d239d --- /dev/null +++ b/center/src/test/java/com/microsoft/hydralab/center/service/LongChainExampleTest.java @@ -0,0 +1,48 @@ +package com.microsoft.hydralab.center.service; + +import dev.langchain4j.data.message.AiMessage; +import dev.langchain4j.model.chat.ChatLanguageModel; +import dev.langchain4j.model.openai.OpenAiChatModel; +import dev.langchain4j.model.output.Result; +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.io.SAXReader; +import org.junit.jupiter.api.Test; + +class LongChainExampleTest { + static String apiKey = "**************"; // https://platform.openai.com/account/api-keys + static ChatLanguageModel model = OpenAiChatModel.withApiKey(apiKey); + + @Test + void testChain() { + + LongChainExample.MaestroCaseGeneration prompt = new LongChainExample.MaestroCaseGeneration(); + Document document = null; + SAXReader saxReader = new SAXReader(); + try { + document = saxReader.read("src/test/resources/test_route_map.xml"); + } catch (DocumentException e) { + throw new RuntimeException(e); + } + prompt.maestroExample = "appId: {the id of test app }\n" + + "---\n" + + "- launchApp\n" + + "- tapOn: \"Create new contact\"\n" + + "- tapOn: \"First name\"\n" + + "- inputRandomPersonName\n" + + "- tapOn: \"Last name\"\n" + + "- inputRandomPersonName\n" + + "- tapOn: \"Phone\"\n" + + "- inputRandomNumber:\n" + + " length: 10\n" + + "- back\n" + + "- tapOn: \"Email\"\n" + + "- inputRandomEmail\n" + + "- tapOn: \"Save\""; + prompt.routeMap = document.asXML(); + + Result result = model.sendUserMessage(prompt); + System.out.println(result.get().text()); + } + +} \ No newline at end of file diff --git a/center/src/test/java/com/microsoft/hydralab/center/service/TestCaseGenerationServiceTest.java b/center/src/test/java/com/microsoft/hydralab/center/service/TestCaseGenerationServiceTest.java new file mode 100644 index 000000000..4ed2b43c1 --- /dev/null +++ b/center/src/test/java/com/microsoft/hydralab/center/service/TestCaseGenerationServiceTest.java @@ -0,0 +1,31 @@ +package com.microsoft.hydralab.center.service; + +import com.microsoft.hydralab.center.service.generation.MaestroCaseGenerationService; +import com.microsoft.hydralab.center.test.BaseTest; +import com.microsoft.hydralab.common.util.PageNode; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import javax.annotation.Resource; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +class TestCaseGenerationServiceTest extends BaseTest { + + @Resource + MaestroCaseGenerationService caseGenerationService; + + @Test + void testParserXMLToPageNode() { + PageNode rootNode = caseGenerationService.parserXMLToPageNode("src/test/resources/test_route_map.xml"); + Assertions.assertNotNull(rootNode, "parser xml to page node failed"); + rootNode.setPageName("com.microsoft.appmanager"); + System.out.println(rootNode); + List explorePaths = new ArrayList<>(); + caseGenerationService.explorePageNodePath(rootNode, "", "", explorePaths); + Assertions.assertEquals(explorePaths.size(), 16, "explore path size is not correct"); + File caseZipFile = caseGenerationService.generateCaseFile(rootNode, explorePaths); + Assertions.assertTrue(caseZipFile.exists()); + } +} \ No newline at end of file diff --git a/center/src/test/resources/test_route_map.xml b/center/src/test/resources/test_route_map.xml new file mode 100644 index 000000000..d203f0a3c --- /dev/null +++ b/center/src/test/resources/test_route_map.xml @@ -0,0 +1,272 @@ + + + + NetworkX 2.8.8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common/src/main/java/com/microsoft/hydralab/common/util/PageNode.java b/common/src/main/java/com/microsoft/hydralab/common/util/PageNode.java new file mode 100644 index 000000000..65ef1633b --- /dev/null +++ b/common/src/main/java/com/microsoft/hydralab/common/util/PageNode.java @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package com.microsoft.hydralab.common.util; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author zhoule + * @date 07/14/2023 + */ + +@Data +public class PageNode { + int id; + String pageName; + List actionInfoList = new ArrayList<>(); + // key: action id, value: child page node + Map childPageNodeMap = new HashMap<>(); + + @Data + public static class ElementInfo { + int index; + String className; + String text; + boolean clickable; + String resourceId; + } + + @Data + public static class ActionInfo { + int id; + ElementInfo testElement; + String actionType; + String driverId; + + String description; + Map arguments; + boolean isOptional; + } + + @Data + public static class ExplorePath { + String path; + String actions; + + public ExplorePath(String path, String action) { + this.path = path; + this.actions = action; + } + } +} diff --git a/react/src/component/TestReportView.jsx b/react/src/component/TestReportView.jsx index 014c05342..d8129ebfe 100644 --- a/react/src/component/TestReportView.jsx +++ b/react/src/component/TestReportView.jsx @@ -753,35 +753,69 @@ export default class TestReportView extends BaseView { generateJSON() { if (this.state.selectedPath.length === 0) { - this.setState({ - snackbarIsShown: true, - snackbarSeverity: "error", - snackbarMessage: "Please select a path!" - }) - return + axios({ + url: `/api/test/generateMaestro/` + this.state.graphFileId + "?testRunId=" + this.state.task.deviceTestResults[0].id, + method: 'GET', + responseType: 'blob' + }).then((res) => { + if (res.data.type.includes('application/json')) { + let reader = new FileReader() + reader.onload = function () { + let result = JSON.parse(reader.result) + if (result.code !== 200) { + this.setState({ + snackbarIsShown: true, + snackbarSeverity: "error", + snackbarMessage: "The file could not be downloaded" + }) + } + } + reader.readAsText(res.data) + } else { + const href = URL.createObjectURL(res.data); + const link = document.createElement('a'); + link.href = href; + const filename = res.headers["content-disposition"].replace('attachment;filename=', ''); + link.setAttribute('download', 'maestro_case_'+filename); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(href); + + if (res.data.code === 200) { + this.setState({ + snackbarIsShown: true, + snackbarSeverity: "success", + snackbarMessage: "Maestro case file downloaded" + }) + } + } + }).catch(this.snackBarError); + }else{ + axios({ + url: `/api/test/generateT2C/` + this.state.graphFileId + "?testRunId=" + this.state.task.deviceTestResults[0].id + "&path=" + this.state.selectedPath.join(','), + method: 'GET', + }).then((res) => { + var blob = new Blob([res.data.content]); + const href = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = href; + link.setAttribute('download', this.state.task.pkgName + '_t2c.json'); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(href); + + if (res.data.code === 200) { + this.setState({ + snackbarIsShown: true, + snackbarSeverity: "success", + snackbarMessage: "T2C JSON file downloaded" + }) + } + }).catch(this.snackBarError); } - axios({ - url: `/api/test/generateT2C/` + this.state.graphFileId + "?testRunId=" + this.state.task.deviceTestResults[0].id + "&path=" + this.state.selectedPath.join(','), - method: 'GET', - }).then((res) => { - var blob = new Blob([res.data.content]); - const href = URL.createObjectURL(blob); - const link = document.createElement('a'); - link.href = href; - link.setAttribute('download', this.state.task.pkgName + '_t2c.json'); - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - URL.revokeObjectURL(href); - - if (res.data.code === 200) { - this.setState({ - snackbarIsShown: true, - snackbarSeverity: "success", - snackbarMessage: "T2C JSON file downloaded" - }) - } - }).catch(this.snackBarError); + } getDeviceLabel = (name) => {