Selenium IDEのcommand、valueをcsvで取り込んで設定ファイルのフォーマットで出力する

## 概要
タイトルの通りです。
別記事で作ったSelenium IDEのcommandとvalueのcsvを取り込んで
Selenium IDEでの設定ファイル(拡張子sideのjson)を
Jacksonで出力してみました。

## この記事を作成した経緯
Selenium IDEのcommandとvalueを別ファイルにして
sideファイルを作ろうとした備忘録です。

## 開発環境の各バージョン
Java : 18
IDE : STS 4.10.0
Spring Boot : 2.6.5

### sideファイルの例

<details><summary>Googleを開くだけのsideファイル</summary>

```json:ウィンドウを開くだけのsideファイル
{
  "id": "bf97d162-8455-469e-b827-2dc9ce77c2a9",
  "version": "2.0",
  "name": "blank",
  "url": "https://www.google.com/",
  "tests": [{
    "id": "8c4010ed-fa49-41c5-ba15-c0a3580f3516" ,
    "name": "template",
    "commands": [{
      "id": "ed4ba57d-3498-4af5-ab13-6253abf83e69",
      "comment": "",
      "command": "open",
      "target": "",
      "targets": [],
      "value": ""
    }]
  }],
  "suites": [{
    "id": "fbc33c96-414b-425e-932e-96da7372c9e3",
    "name": "Default Suite",
    "persistSession": false,
    "parallel": false,
    "timeout": 300,
    "tests": ["8c4010ed-fa49-41c5-ba15-c0a3580f3516"]
  }],
  "urls": [],
  "plugins": []
}
```

</details>

## side化させるcsv

<details><summary>result.csv</summary>

```text:result.csv
open,https://www.yahoo.co.jp/
store text:name,p
store text:tag,button
click
store text:css,Hits__item
```

</details>

csvの1列目、2列目はそれぞれ以下の通りに設定して
tests,suitesを作成するようにします。
1列目 : (ルート) -> commands -> tests -> command
2列目 : (ルート) -> commands -> tests -> value

### sideファイルを作る際の注意点
suitesのtestsの値は
ルートにあるtestsのidの値の配列にすることです。

## sideファイルを作るJavaプロジェクト
Javaプロジェクトはgradleで作成しました。
フォルダ構成とbuild.gradleは次の通りです。

<details><summary>フォルダ構成(一部省略)</summary>

```bash:フォルダ構成(一部省略)
project root
|--src
|  |--main
|  |  |--java
|  |  |  |--createSide
|  |  |  |  |--App.java
|  |  |  |  |--json
|  |  |  |  |  |--Command.java
|  |  |  |  |  |--CsvController.java
|  |  |  |  |  |--CsvValue.java
|  |  |  |  |  |--RootNode.java
|  |  |  |  |  |--Suite.java
|  |  |  |  |  |--Test.java
|--build.gradle
|--result.csv
```

</details>

<details><summary>build.gradle</summary>

```gradle:build.gradle
plugins {
    // Apply the application plugin to add support for building a CLI application in Java.
    id 'application'
}

repositories {
    // Use Maven Central for resolving dependencies.
    mavenCentral()
}

dependencies {
    // Use JUnit Jupiter for testing.
    testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1'

    // This dependency is used by the application.
    implementation 'com.google.guava:guava:30.1.1-jre'

    // Jackson
 	implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.13.2'
	// Jackson Dataformat Csv
	implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-csv', version: '2.13.2'	

	compileOnly 'org.projectlombok:lombok:1.18.22'
	annotationProcessor 'org.projectlombok:lombok:1.18.22'

}

application {
    // Define the main class for the application.
    mainClass = 'createSide.App'
}

tasks.named('test') {
    // Use JUnit Platform for unit tests.
    useJUnitPlatform()
}
```

</details>

<details><summary>App.java(json出力)</summary>

```java:App.java (json出力)
// (include文省略)
public class App {

	public static void main(String[] args) throws Exception {
		CsvController csvController 
			= new CsvController(Path.of("./result.csv"));
		RootNode rootNode = csvController.createRootNode();
		DefaultPrettyPrinter printer = new DefaultPrettyPrinter();
		printer.indentArraysWith(DefaultIndenter.SYSTEM_LINEFEED_INSTANCE);
		String json = new ObjectMapper().writer(printer).writeValueAsString(rootNode);
		System.out.println(json);
	}
}
```

</details>

<details><summary>CsvController.java (csv->java)</summary>

```java:CsvController.java (csv->java)
// (import文等、省略)
@AllArgsConstructor
public class CsvController {

    private Path path;

    @SuppressWarnings("rawtypes")
    private List<CsvValue> readCsv() throws IOException {
        CsvMapper csvMapper = new CsvMapper();
        Class clazz = CsvValue.class;
        CsvSchema csvSchema = csvMapper.schemaFor(clazz);
        List<CsvValue> result = new ArrayList<>(0);
        MappingIterator<CsvValue> objectMappingIterator 
            = csvMapper.readerFor(clazz).with(csvSchema)
                .readValues(path.toFile());
        while (objectMappingIterator.hasNext()) {
            result.add(objectMappingIterator.next());
        }
        return result;
    }

    public RootNode createRootNode() throws IOException {
        RootNode rootNode = new RootNode();
        Test test = new Test();
        List<CsvValue> list = this.readCsv();
        List<Command> commands = new ArrayList<>(0);
        for (CsvValue element : list) {
            Command command = new Command();
            command.setCommand(element.getCommand());
            command.setValue(element.getValue());
            commands.add(command);
        }
        test.setCommands(commands);
        rootNode.getTests().add(test);
        Suite suite = new Suite();
        suite.getTests().add(test.getId());
        rootNode.getSuites().add(suite);
        return rootNode;
    }
}
```

</details>

<details><summary>CsvValue.java (csv1行分のデータ->java)</summary>

```java:CsvValue.java (csv1行分のデータ->java)
package createSide.json;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

import lombok.Data;

@Data
@JsonPropertyOrder({ "command", "value" })
public class CsvValue {
	@JsonProperty("command")
	private String command;
	@JsonProperty("value")
	private String value;
}
```

</details>

RootNode.java, Command.java, Suite.java, Test.javaはjson出力用のエンティティで
RootNodeの中にあるcommands, tests, suitesの
内容に対応するクラスがCommand.java, Suite.java, Test.javaです。

<details><summary>Suite.java</summary>

```java:Suite.java
// (import文等、省略)
@Data
public class Suite {

	private String id;
	private String name;
	private boolean persistSession;
	private boolean parallel;
	private long timeout;
	private List<String> tests;
	
	public Suite() {
		this.id = UUID.randomUUID().toString();
		this.name = "no name";
		this.persistSession = false;
		this.parallel = false;
		this.timeout = 300;
		this.tests = new ArrayList<String>(0);
	}
}
```

</details>

<details><summary>Test.java</summary>

```java:Test.java
// (import文等、省略)
@Data
public class Test {

	private String id;
	private String comment;
	private List<Command> commands;
	
	public Test() {
		this.id = UUID.randomUUID().toString();
		this.comment = "";
		this.commands = new ArrayList<Command>(0);
	}

}
```

</details>

<details><summary>Command.java</summary>

```java:Command.java
// (import文等、省略)
@Data
public class Command {
	private String id;
	private String comment;
	private String command;
	private String target;
	private List<List<String>> targets;
	private String value;
	
	public Command() {
		this.id = UUID.randomUUID().toString();
		this.comment = "";
		this.command = "";
		this.target = "";
		// targetsは文字列の2次元配列
		this.targets = new ArrayList<List<String>>(0);
		this.value = "";
	}
}
```

</details>

<details><summary>出力されたjson</summary>

```json:出力されたjson
{
  "id" : "c462be82-6000-417c-9cdb-5fb2707ad887",
  "version" : "2.0",
  "name" : "",
  "url" : "",
  "tests" : [
    {
      "id" : "36cd8244-7a5c-47c7-8a80-a9344418f52f",
      "comment" : "",
      "commands" : [
        {
          "id" : "2f7b0829-1c2a-4e0e-bdba-1e0a83c0e562",
          "comment" : "",
          "command" : "open",
          "target" : "",
          "targets" : [ ],
          "value" : "https://www.yahoo.co.jp/"
        },
        {
          "id" : "4de0018d-1e55-4750-991f-8db3dcb7751b",
          "comment" : "",
          "command" : "store text:name",
          "target" : "",
          "targets" : [ ],
          "value" : "p"
        },
        {
          "id" : "f5b049be-333d-4004-8de4-866af0799efe",
          "comment" : "",
          "command" : "store text:tag",
          "target" : "",
          "targets" : [ ],
          "value" : "button"
        },
        {
          "id" : "7d9f7c1d-181a-4edb-bd26-cccdd258d0c7",
          "comment" : "",
          "command" : "click",
          "target" : "",
          "targets" : [ ],
          "value" : null
        },
        {
          "id" : "30b373fd-1b12-440d-bd21-2ffad7f50ee2",
          "comment" : "",
          "command" : "store text:css",
          "target" : "",
          "targets" : [ ],
          "value" : "Hits__item"
        }
      ]
    }
  ],
  "suites" : [
    {
      "id" : "971de2bd-01ec-4819-94d0-51030d2f250e",
      "name" : "no name",
      "persistSession" : false,
      "parallel" : false,
      "timeout" : 300,
      "tests" : [
        "36cd8244-7a5c-47c7-8a80-a9344418f52f"
      ]
    }
  ]
}
```

</details>

この状態ではそのまま実行はできませんが、
多少の訂正で済む程度にまではできたと思います。

### 最後に
お読みいただき、ありがとうございました。