diff --git a/karate-core/src/main/java/com/intuit/karate/cucumber/FeatureWrapper.java b/karate-core/src/main/java/com/intuit/karate/cucumber/FeatureWrapper.java index 8ac8f23da..84587c527 100644 --- a/karate-core/src/main/java/com/intuit/karate/cucumber/FeatureWrapper.java +++ b/karate-core/src/main/java/com/intuit/karate/cucumber/FeatureWrapper.java @@ -91,8 +91,8 @@ public static FeatureWrapper fromStream(InputStream is, ScriptEnv scriptEnv, Str public String joinLines(int startLine, int endLine) { StringBuilder sb = new StringBuilder(); - if (endLine > lines.size() - 1) { - endLine = lines.size() - 1; + if (endLine > lines.size()) { + endLine = lines.size(); } for (int i = startLine; i < endLine; i++) { String line = lines.get(i); @@ -198,6 +198,10 @@ public FeatureWrapper removeLine(int index) { return new FeatureWrapper(joinLines(), scriptEnv, path, callTag); } + public FeatureWrapper replaceText(String newText) { + return new FeatureWrapper(newText, scriptEnv, path, callTag); + } + private FeatureWrapper(String text, ScriptEnv scriptEnv, String path, String callTag) { this.path = path; this.callTag = callTag; diff --git a/karate-core/src/main/java/com/intuit/karate/ui/App.java b/karate-core/src/main/java/com/intuit/karate/ui/App.java index 6ce0e7f48..81f90047f 100644 --- a/karate-core/src/main/java/com/intuit/karate/ui/App.java +++ b/karate-core/src/main/java/com/intuit/karate/ui/App.java @@ -27,6 +27,7 @@ import com.intuit.karate.ScriptBindings; import com.intuit.karate.convert.ConvertUtils; import com.intuit.karate.convert.PostmanItem; +import com.intuit.karate.cucumber.FeatureWrapper; import javafx.application.Application; import javafx.geometry.Insets; import javafx.scene.Scene; @@ -50,9 +51,20 @@ public class App extends Application { private final FileChooser fileChooser = new FileChooser(); + private boolean needsNameToSave = false; private File workingDir = new File("."); private final BorderPane rootPane = new BorderPane(); + private static final String DEFAULT_FEATURE_NAME = "noname.feature"; + private static final String DEFAULT_FEATURE_TEXT = "Feature: brief description of what is being tested\n\n" + + "Scenario: description of this scenario\n" + + "# steps for this scenario\n" + + "Given url 'https://duckduckgo.com'\n" + + "And param q = 'intuit karate'\n" + + "When method GET\nThen status 200\n\n" + + "Scenario: a different scenario\n" + + "# steps for this other scenario"; + public static Font getDefaultFont() { return Font.font("Courier"); } @@ -61,17 +73,28 @@ private File chooseFile(Stage stage, String description, String extension) { fileChooser.setTitle("Choose Feature File"); fileChooser.setInitialDirectory(workingDir); FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter(description, extension); - fileChooser.getExtensionFilters().add(extFilter); + fileChooser.getExtensionFilters().setAll(extFilter); return fileChooser.showOpenDialog(stage); } + private File chooseFileToSave(Stage stage, String description, String extension, String initialName) { + fileChooser.setTitle("Save Feature To File"); + fileChooser.setInitialDirectory(workingDir); + FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("*.feature files", "*.feature"); + fileChooser.getExtensionFilters().setAll(extFilter); + fileChooser.setInitialFileName(initialName); + return fileChooser.showSaveDialog(stage); + } + void initUi(File file, String envString, Stage stage) { AppSession session = new AppSession(file, envString); rootPane.setTop(session.headerPanel); rootPane.setCenter(session.featurePanel); rootPane.setRight(session.varsPanel); rootPane.setBottom(session.logPanel); + initNewFileAction(session.headerPanel, envString, stage); initFileOpenAction(session.headerPanel, envString, stage); + initFileSaveAction(session, envString, stage); initDirectoryOpenAction(session.headerPanel, envString, stage); initImportOpenAction(session.headerPanel, envString, stage); workingDir = file.getParentFile(); @@ -79,11 +102,39 @@ void initUi(File file, String envString, Stage stage) { private void initFileOpenAction(HeaderPanel header, String envString, Stage stage) { header.setFileOpenAction(e -> { - if(rootPane.getLeft() != null) { + if (rootPane.getLeft() != null) { rootPane.setLeft(null); } File file = chooseFile(stage, "*.feature files", "*.feature"); initUi(file, envString, stage); + needsNameToSave = false; + }); + } + + private void initNewFileAction(HeaderPanel header, String envString, Stage stage) { + header.setNewFileAction(e -> { + if (rootPane.getLeft() != null) { + rootPane.setLeft(null); + } + initUi(new File(initializeNoNameFeature()), envString, stage); + }); + } + + private void initFileSaveAction(AppSession session, String envString, Stage stage) { + session.headerPanel.setFileSaveAction(e -> { + File file; + FeatureWrapper feature = session.getFeature(); + if (needsNameToSave) { + String suggestedName = feature.getFeature().getGherkinFeature().getName(); + file = chooseFileToSave(stage, "*.feature files", "*.feature", suggestedName); + } else { + file = new File(feature.getPath()); + } + FileUtils.writeToFile(file, feature.getText()); + if (needsNameToSave) { + needsNameToSave = false; + initUi(file, envString, stage); + } }); } @@ -115,30 +166,34 @@ private void initImportOpenAction(HeaderPanel header, String envString, Stage st String json = FileUtils.toString(file); List items = ConvertUtils.readPostmanJson(json); String featureText = ConvertUtils.toKarateFeature(file.getName(), items); - String featurePath = FileUtils.replaceFileExtension(file.getPath(), "feature"); - File featureFile = new File(featurePath); - FileUtils.writeToFile(featureFile, featureText); - initUi(featureFile, envString, stage); + File noNameFeature = new File(workingDir, DEFAULT_FEATURE_NAME); + FileUtils.writeToFile(noNameFeature, featureText); + needsNameToSave = true; + initUi(noNameFeature, envString, stage); }); } + + private String initializeNoNameFeature() { + needsNameToSave = true; + File noNameFeature = new File(workingDir, DEFAULT_FEATURE_NAME); + FileUtils.writeToFile(noNameFeature, DEFAULT_FEATURE_TEXT); + return noNameFeature.getPath(); + } @Override public void start(Stage stage) throws Exception { + String fileName = null; List params = getParameters().getUnnamed(); String envString = System.getProperty(ScriptBindings.KARATE_ENV); if (!params.isEmpty()) { - String fileName = params.get(0); + fileName = params.get(0); if (params.size() > 1) { envString = params.get(1); } - initUi(new File(fileName), envString, stage); } else { - HeaderPanel header = new HeaderPanel(); - rootPane.setTop(header); - initFileOpenAction(header, envString, stage); - initDirectoryOpenAction(header, envString, stage); - initImportOpenAction(header, envString, stage); + fileName = initializeNoNameFeature(); } + initUi(new File(fileName), envString, stage); Scene scene = new Scene(rootPane, 900, 750); stage.setScene(scene); diff --git a/karate-core/src/main/java/com/intuit/karate/ui/AppSession.java b/karate-core/src/main/java/com/intuit/karate/ui/AppSession.java index 24a7cf4f9..de45e0482 100644 --- a/karate-core/src/main/java/com/intuit/karate/ui/AppSession.java +++ b/karate-core/src/main/java/com/intuit/karate/ui/AppSession.java @@ -142,6 +142,11 @@ public void replace(StepWrapper step, String text) { headerPanel.initTextContent(); } + public void replaceFeature(String text) { + feature = feature.replaceText(text); + featurePanel.refresh(); + } + public ObservableList getVars() { if (backend.getStepDefs() == null) { return FXCollections.emptyObservableList(); diff --git a/karate-core/src/main/java/com/intuit/karate/ui/FeaturePanel.java b/karate-core/src/main/java/com/intuit/karate/ui/FeaturePanel.java index 5d62f7899..f5f0b1358 100644 --- a/karate-core/src/main/java/com/intuit/karate/ui/FeaturePanel.java +++ b/karate-core/src/main/java/com/intuit/karate/ui/FeaturePanel.java @@ -86,4 +86,10 @@ public void action(AppAction action) { } } + public void refresh() { + sectionPanels.clear(); + content.getChildren().clear(); + addSections(); + } + } diff --git a/karate-core/src/main/java/com/intuit/karate/ui/HeaderPanel.java b/karate-core/src/main/java/com/intuit/karate/ui/HeaderPanel.java index cf4e3c277..5b73b7751 100644 --- a/karate-core/src/main/java/com/intuit/karate/ui/HeaderPanel.java +++ b/karate-core/src/main/java/com/intuit/karate/ui/HeaderPanel.java @@ -47,7 +47,9 @@ public class HeaderPanel extends BorderPane { private final HBox content; private final AppSession session; + private final MenuItem newFileMenuItem; private final MenuItem openFileMenuItem; + private final MenuItem saveFileMenuItem; private final MenuItem openDirectoryMenuItem; private final MenuItem openImportMenuItem; private final TextArea textContent; @@ -76,11 +78,12 @@ public HeaderPanel(AppSession session) { }); MenuBar menuBar = new MenuBar(); Menu fileMenu = new Menu("File"); + newFileMenuItem = new MenuItem("New"); openFileMenuItem = new MenuItem("Open"); - fileMenu.getItems().addAll(openFileMenuItem); - + saveFileMenuItem = new MenuItem("Save"); openDirectoryMenuItem = new MenuItem("Load Directory"); - fileMenu.getItems().addAll(openDirectoryMenuItem); + fileMenu.getItems().addAll(newFileMenuItem, openFileMenuItem, + openDirectoryMenuItem, saveFileMenuItem); Menu importMenu = new Menu("Import"); openImportMenuItem = new MenuItem("Open"); @@ -113,11 +116,19 @@ public HeaderPanel(AppSession session) { private String getContentButtonText(boolean visible) { return visible ? "Hide Raw" : "Show Raw"; } + + public void setNewFileAction(EventHandler handler) { + newFileMenuItem.setOnAction(handler); + } public void setFileOpenAction(EventHandler handler) { openFileMenuItem.setOnAction(handler); } + public void setFileSaveAction(EventHandler handler) { + saveFileMenuItem.setOnAction(handler); + } + public void setDirectoryOpenAction(EventHandler handler) { openDirectoryMenuItem.setOnAction(handler); } @@ -134,12 +145,11 @@ public void initTextContent() { public void rebuildFeatureIfTextChanged() { String newText = textContent.getText(); if (!newText.equals(oldText)) { - Alert alert = new Alert(AlertType.INFORMATION); - alert.setHeaderText("Read Only"); - alert.setTitle("Not Implemented"); - alert.setContentText("Raw text editing is not supported."); - alert.show(); - textContent.setText(oldText); + try { + session.replaceFeature(newText); + } catch (Exception e) { + e.printStackTrace(); + } } } diff --git a/karate-core/src/test/java/com/intuit/karate/cucumber/CucumberUtilsTest.java b/karate-core/src/test/java/com/intuit/karate/cucumber/CucumberUtilsTest.java index 914ac2658..9a90243af 100644 --- a/karate-core/src/test/java/com/intuit/karate/cucumber/CucumberUtilsTest.java +++ b/karate-core/src/test/java/com/intuit/karate/cucumber/CucumberUtilsTest.java @@ -99,7 +99,7 @@ public void testInsert() { fw = fw.addLine(9, "Then assert 2 == 2"); List lines = fw.getLines(); printLines(lines); - assertEquals(16, lines.size()); + assertEquals(17, lines.size()); assertEquals(1, fw.getSections().size()); } @@ -115,7 +115,7 @@ public void testEdit() { fw = fw.replaceLines(line, line, "Then assert 2 == 2"); List lines = fw.getLines(); printLines(lines); - assertEquals(15, lines.size()); + assertEquals(16, lines.size()); assertEquals(1, fw.getSections().size()); } @@ -130,7 +130,7 @@ public void testMultiLineEdit() { fw = fw.replaceStep(step, "Then assert 2 == 2"); List lines = fw.getLines(); printLines(lines); - assertEquals(12, lines.size()); + assertEquals(13, lines.size()); assertEquals("# another comment", fw.getLines().get(9)); assertEquals("Then assert 2 == 2", fw.getLines().get(10)); assertEquals("Then match b == { foo: 'bar'}", fw.getLines().get(11));