├── automating
├── src
│ └── test
│ │ └── java
│ │ └── uk
│ │ └── co
│ │ └── compendiumdev
│ │ └── libraryexamples
│ │ ├── cucumber
│ │ ├── RunCukesTest.java
│ │ ├── people.feature
│ │ └── swapi
│ │ │ └── SwapiSteps.java
│ │ ├── restassured
│ │ ├── LoggingUsageTest.java
│ │ ├── SwapiAPIUsageTest.java
│ │ └── DownloadAFileExampleTest.java
│ │ ├── hamcrest
│ │ └── HamcrestAssertionOnSwapiDataUsageTest.java
│ │ ├── jsoup
│ │ └── SwapiApiFromJsoupUsageTest.java
│ │ ├── gson
│ │ └── GsonParsingSwapiApiData.java
│ │ └── webdriver
│ │ └── SwapiGUIFormUsageTest.java
└── pom.xml
├── .gitignore
├── pom.xml
├── README.md
├── LICENSE
└── charting
├── pom.xml
└── src
└── test
└── java
├── xchart
└── XchartExamplesTest.java
└── jfreechart
└── JFreeChartExamplesTest.java
/automating/src/test/java/uk/co/compendiumdev/libraryexamples/cucumber/RunCukesTest.java:
--------------------------------------------------------------------------------
1 | package uk.co.compendiumdev.libraryexamples.cucumber;
2 |
3 | import cucumber.api.CucumberOptions;
4 | import cucumber.api.junit.Cucumber;
5 | import org.junit.runner.RunWith;
6 |
7 | @RunWith(Cucumber.class)
8 | @CucumberOptions(plugin={"pretty", "html:target/cucumber"})
9 | public class RunCukesTest {
10 | }
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Log file
5 | *.log
6 |
7 | # BlueJ files
8 | *.ctxt
9 |
10 | # Mobile Tools for Java (J2ME)
11 | .mtj.tmp/
12 |
13 | # Package Files #
14 | *.jar
15 | *.war
16 | *.ear
17 | *.zip
18 | *.tar.gz
19 | *.rar
20 |
21 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
22 | hs_err_pid*
23 | /target
24 | /.idea
25 | /library-examples.iml
26 |
27 | downloads
28 | /charting/output/
29 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | uk.co.compendiumdev
8 | library-examples
9 | pom
10 | 1.0-SNAPSHOT
11 |
12 | charting
13 | automating
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # libraryexamples
2 |
3 | Simple examples of using Java Libraries.
4 |
5 | Used in "Java For Testers" classroom training.
6 |
7 | Examples include:
8 |
9 | - [WebDriver](http://www.seleniumhq.org/projects/webdriver/)
10 | - [Gson](https://github.com/google/gson)
11 | - [RestAssured](http://rest-assured.io/)
12 | - JsonPath
13 | - [on github](https://github.com/rest-assured/rest-assured)
14 | - [JSoup](https://jsoup.org/)
15 | - [Hamcrest](http://hamcrest.org/)
16 | - [Cucumber JVM](https://cucumber.io/docs/reference/jvm)
17 |
18 | Examples are using the [Swapi.co](https://swapi.co/) public API and JSON data from Swapi.co.
19 |
20 | For more information on Swapi.co visit:
21 |
22 | - https://swapi.co/
23 | - https://github.com/phalt/swapi
24 |
--------------------------------------------------------------------------------
/automating/src/test/java/uk/co/compendiumdev/libraryexamples/cucumber/people.feature:
--------------------------------------------------------------------------------
1 | Feature: Can find Star Wars People Using API
2 |
3 | Background: API allows us to access people
4 |
5 | We want the to make sure some common people are in the API
6 |
7 | Scenario: Get Luke from the API
8 | Given a user ID "1"
9 | When a call to the "people" api is made
10 | Then the name of the person is "Luke Skywalker"
11 |
12 |
13 | Scenario: Get C3PO from the API
14 | Given a user ID "2"
15 | When a call to the "people" api is made
16 | Then the name of the person is "C-3PO"
17 |
18 |
19 | Scenario Outline:
20 |
21 | * Users exist with the following "" and ""
22 |
23 | Examples:
24 | | userid | name |
25 | | 1 | Luke Skywalker |
26 | | 2 | C-3PO |
27 | | 3 | R2-D2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Alan Richardson
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/automating/src/test/java/uk/co/compendiumdev/libraryexamples/restassured/LoggingUsageTest.java:
--------------------------------------------------------------------------------
1 | package uk.co.compendiumdev.libraryexamples.restassured;
2 |
3 | import io.restassured.RestAssured;
4 | import io.restassured.filter.log.RequestLoggingFilter;
5 | import io.restassured.filter.log.ResponseLoggingFilter;
6 | import org.junit.Test;
7 |
8 | import java.io.*;
9 |
10 | import static org.hamcrest.core.IsEqual.equalTo;
11 |
12 | public class LoggingUsageTest {
13 |
14 | @Test
15 | public void canGetLukeLoggingToConsole(){
16 |
17 | // configuring logging to console out
18 | RestAssured.filters(new RequestLoggingFilter(),
19 | new ResponseLoggingFilter());
20 |
21 | RestAssured.get("https://swapi.dev/api/people/1/?format=json").
22 | then().
23 | assertThat().
24 | body("name",
25 | equalTo("Luke Skywalker"));
26 | }
27 |
28 | @Test
29 | public void canGetLukeLoggingToFile() throws IOException {
30 |
31 | final String currentDir = System.getProperty("user.dir");
32 | File outputFile = new File(currentDir,
33 | "restassured" + System.currentTimeMillis()+".log");
34 | System.out.println("log to file:" + outputFile.getAbsolutePath().toString());
35 | FileOutputStream fileOutput = new FileOutputStream(outputFile);
36 | PrintStream printToFile = new PrintStream(fileOutput);
37 |
38 | // configuring logging to console out
39 | RestAssured.filters(new RequestLoggingFilter(printToFile),
40 | new ResponseLoggingFilter(printToFile));
41 |
42 | RestAssured.get("https://swapi.dev/api/people/1/?format=json").
43 | then().
44 | assertThat().
45 | body("name",
46 | equalTo("Luke Skywalker"));
47 |
48 | fileOutput.close();
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/automating/src/test/java/uk/co/compendiumdev/libraryexamples/cucumber/swapi/SwapiSteps.java:
--------------------------------------------------------------------------------
1 | package uk.co.compendiumdev.libraryexamples.cucumber.swapi;
2 |
3 | import cucumber.api.java.en.Given;
4 | import cucumber.api.java.en.Then;
5 | import cucumber.api.java.en.When;
6 | import io.restassured.RestAssured;
7 | import io.restassured.path.json.JsonPath;
8 | import io.restassured.response.Response;
9 | import org.junit.Assert;
10 |
11 | import static org.hamcrest.core.IsEqual.equalTo;
12 |
13 |
14 | public class SwapiSteps {
15 |
16 | private String userId;
17 | private String endpoint;
18 | private Response response;
19 |
20 | @Given("^a user ID \"([^\"]*)\"$")
21 | public void aUserID(String userId) throws Throwable {
22 | this.userId = userId;
23 | }
24 |
25 | @When("^a call to the \"([^\"]*)\" api is made$")
26 | public void aCallToTheApiIsMade(String apiendpoint) throws Throwable {
27 | this.endpoint = apiendpoint;
28 | response = RestAssured.get(
29 | "https://swapi.dev/api/" + apiendpoint + "/" + this.userId + "/?format=json").
30 | andReturn();
31 | }
32 |
33 | @Then("^the name of the person is \"([^\"]*)\"$")
34 | public void theNameOfThePersonIs(String givenName) throws Throwable {
35 | String json = response.getBody().asString();
36 | JsonPath jsonPath = new JsonPath(json);
37 | Assert.assertEquals(
38 | givenName,
39 | jsonPath.getString("name"));
40 | }
41 |
42 |
43 | @Given("^Users exist with the following \\\"([^\\\"]*)\\\" and \\\"([^\\\"]*)\\\"$")
44 | public void users_exist_with_the_following(String anid, String name) throws Throwable {
45 | RestAssured.get(
46 | "https://swapi.dev/api/people/" + anid + "/?format=json").
47 | then().
48 | assertThat().
49 | body("name", equalTo(name));
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/automating/src/test/java/uk/co/compendiumdev/libraryexamples/hamcrest/HamcrestAssertionOnSwapiDataUsageTest.java:
--------------------------------------------------------------------------------
1 | package uk.co.compendiumdev.libraryexamples.hamcrest;
2 |
3 | import io.restassured.path.json.JsonPath;
4 | import org.junit.Test;
5 |
6 | import static org.hamcrest.CoreMatchers.not;
7 | import static org.hamcrest.MatcherAssert.assertThat;
8 | import static org.hamcrest.Matchers.greaterThan;
9 | import static org.hamcrest.core.Is.is;
10 | import static org.hamcrest.core.IsEqual.equalTo;
11 |
12 | public class HamcrestAssertionOnSwapiDataUsageTest {
13 |
14 | // JSON Data from Swapi.io
15 | String swapidata = "{\"name\":\"C-3PO\",\"height\":\"167\",\"mass\":\"75\",\"hair_color\":\"n/a\",\"skin_color\":\"gold\",\"eye_color\":\"yellow\",\"birth_year\":\"112BBY\",\"gender\":\"n/a\",\"homeworld\":\"https://swapi.dev/api/planets/1/\",\"films\":[\"https://swapi.dev/api/films/2/\",\"https://swapi.dev/api/films/5/\",\"https://swapi.dev/api/films/4/\",\"https://swapi.dev/api/films/6/\",\"https://swapi.dev/api/films/3/\",\"https://swapi.dev/api/films/1/\"],\"species\":[\"https://swapi.dev/api/species/2/\"],\"vehicles\":[],\"starships\":[],\"created\":\"2014-12-10T15:10:51.357000Z\",\"edited\":\"2014-12-20T21:17:50.309000Z\",\"url\":\"https://swapi.dev/api/people/2/\"}\n";
16 |
17 |
18 | // http://hamcrest.org/JavaHamcrest/
19 |
20 | @Test
21 | public void canAssertWithHamcrest(){
22 |
23 | // using RestAssured JsonPath to parse the Json Data
24 | JsonPath json = new JsonPath(swapidata);
25 |
26 | String name = json.getString("name");
27 | int mass = json.getInt("mass");
28 |
29 | // using C-3PO data
30 | assertThat(name, is(equalTo("C-3PO")));
31 | assertThat(name, is(not(equalTo("R2-D2"))));
32 | assertThat(mass, is(greaterThan(74)));
33 |
34 | }
35 |
36 |
37 | /*
38 |
39 | You can easily view the JSON used by visiting the Swapi.co site
40 | https://swapi.dev/api/people/2/?format=api
41 |
42 | Exercise:
43 | - Use Json path to extract more values from JSON
44 | - Experiment with Hamcrest matchers to assert on the values from Json
45 | - Expand Json Path to extract to an object and assert on object fields or 'get' methods
46 | - see `SwapoAPIUsageTest`
47 | */
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/charting/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | library-examples
7 | uk.co.compendiumdev
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | charting
13 |
14 |
15 | 5.6.2
16 |
17 |
18 |
19 |
20 |
21 | org.junit.jupiter
22 | junit-jupiter-api
23 | ${junit.jupiter.version}
24 | test
25 |
26 |
27 | org.junit.jupiter
28 | junit-jupiter-engine
29 | ${junit.jupiter.version}
30 | test
31 |
32 |
33 |
34 |
35 | org.jfree
36 | jfreechart
37 | 1.5.0
38 |
39 |
40 |
41 |
42 | org.jfree
43 | org.jfree.svg
44 | 4.1
45 |
46 |
47 |
48 |
49 |
50 | org.knowm.xchart
51 | xchart
52 | 3.6.5
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | org.apache.maven.plugins
61 | maven-compiler-plugin
62 | 3.8.0
63 |
64 | 12
65 | UTF-8
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/automating/src/test/java/uk/co/compendiumdev/libraryexamples/jsoup/SwapiApiFromJsoupUsageTest.java:
--------------------------------------------------------------------------------
1 | package uk.co.compendiumdev.libraryexamples.jsoup;
2 |
3 | import org.jsoup.Jsoup;
4 | import org.jsoup.nodes.Document;
5 | import org.junit.Assert;
6 | import org.junit.Test;
7 |
8 | import java.io.IOException;
9 |
10 |
11 | public class SwapiApiFromJsoupUsageTest {
12 |
13 | @Test
14 | public void canGetLuke() throws IOException {
15 |
16 | // have to ignore content type or it throws exception if not text/*, application/xml, or application/xhtml+xml
17 | Document doc = Jsoup.connect("https://swapi.dev/api/people/1/?format=json").ignoreContentType(true).get();
18 |
19 | String json = doc.text();
20 | System.out.println(json);
21 |
22 | // JSoup does not supply JSON parsing routines
23 | Assert.assertTrue(json.contains("Luke Skywalker"));
24 |
25 | }
26 |
27 | @Test
28 | public void canGetC3PO() throws IOException {
29 |
30 | Document doc = Jsoup.connect("https://swapi.dev/api/people/2/?format=json").ignoreContentType(true).get();
31 |
32 | String json = doc.text();
33 | System.out.println(json);
34 |
35 | // JSoup does not supply JSON parsing routines
36 | Assert.assertTrue(json.contains("C-3PO"));
37 | }
38 |
39 |
40 | /* JSOUP API Exercises
41 |
42 | - make sure your URL call is always upto date by getting the url from the root api
43 | - make a call to https://swapi.dev/api/?format=json and use the url for people in the returned array
44 |
45 | - automate more of the Swapi API e.g. planets etc.
46 |
47 | - Create an abstraction layer for the Swapi
48 | - e.g.
49 |
50 | ~~~~~~~~
51 | Swapi swapi = new Swapi();
52 | String json = swapi.getPersonJson(1);
53 | ~~~~~~~~
54 |
55 | - extend the abstraction layer to have a Person object which models the star wars person as an object
56 | - see Gson example of RestAssured JsonPath example for how to do this
57 |
58 | - e.g.
59 |
60 | ~~~~~~~~
61 | Person luke = swapi.getPerson(1);
62 | Assert.assertTrue("Luke Skywalker",luke.getName());
63 | ~~~~~~~~
64 |
65 | - add dependency injection to the Swapi so you have different implementations
66 |
67 | ~~~~~~~~
68 | Swapi swapi = new Swapi(new JsoupBackedSwapi());
69 | Swapi swapi = new Swapi(new RestAssuredBackedSwapi());
70 | Swapi swapi = new Swapi(new WebDriverBackedSwapi());
71 | ~~~~~~~~
72 |
73 |
74 | */
75 | }
76 |
--------------------------------------------------------------------------------
/automating/src/test/java/uk/co/compendiumdev/libraryexamples/gson/GsonParsingSwapiApiData.java:
--------------------------------------------------------------------------------
1 | package uk.co.compendiumdev.libraryexamples.gson;
2 |
3 |
4 | import com.google.gson.Gson;
5 | import org.junit.Assert;
6 | import org.junit.Test;
7 |
8 | import java.util.Map;
9 |
10 | public class GsonParsingSwapiApiData {
11 |
12 | // JSON Data from Swapi.io
13 | String swapidata = "{\"name\":\"C-3PO\",\"height\":\"167\",\"mass\":\"75\",\"hair_color\":\"n/a\",\"skin_color\":\"gold\",\"eye_color\":\"yellow\",\"birth_year\":\"112BBY\",\"gender\":\"n/a\",\"homeworld\":\"https://swapi.dev/api/planets/1/\",\"films\":[\"https://swapi.dev/api/films/2/\",\"https://swapi.dev/api/films/5/\",\"https://swapi.dev/api/films/4/\",\"https://swapi.dev/api/films/6/\",\"https://swapi.dev/api/films/3/\",\"https://swapi.dev/api/films/1/\"],\"species\":[\"https://swapi.dev/api/species/2/\"],\"vehicles\":[],\"starships\":[],\"created\":\"2014-12-10T15:10:51.357000Z\",\"edited\":\"2014-12-20T21:17:50.309000Z\",\"url\":\"https://swapi.dev/api/people/2/\"}\n";
14 |
15 |
16 | // https://github.com/google/gson
17 |
18 | // Simple and generic way to parse JSON quickly
19 | // - convert it to a Map
20 | // this can get your code up and running fast but
21 | // probably isn't best for long term maintenance
22 | @Test
23 | public void canParseWithGSon(){
24 |
25 | // generic parsing with Gson
26 | Gson gson = new Gson();
27 | Map m = gson.fromJson(swapidata, Map.class);
28 |
29 | String name = (String)m.get("name");
30 | int mass = Integer.parseInt((String)m.get("mass"));
31 |
32 | // using C-3PO data
33 | Assert.assertEquals("C-3PO", name);
34 | Assert.assertNotEquals("R2-D2", name);
35 | //Assert.assertTrue(mass > 74);
36 | Assert.assertTrue("expected " + mass + " to be greater than 74",
37 | mass > 74);
38 | }
39 |
40 |
41 | // longterm use is probably better to create a class to
42 | // represent the payload
43 | // and convert the JSON into the payload with fromJson
44 | private class Person{
45 | public String name;
46 | public int mass;
47 | }
48 |
49 | @Test
50 | public void canParseWithGSonAndObject(){
51 |
52 | // generic parsing with Gson
53 | Gson gson = new Gson();
54 | Person c3po = gson.fromJson(swapidata, Person.class);
55 |
56 | // using C-3PO data
57 | Assert.assertEquals("C-3PO", c3po.name);
58 | Assert.assertNotEquals("R2-D2", c3po.mass);
59 | //Assert.assertTrue(mass > 74);
60 | int mass = c3po.mass;
61 | Assert.assertTrue("expected " + mass + " to be greater than 74",
62 | mass > 74);
63 |
64 | }
65 |
66 | /* Exercises:
67 |
68 | You can easily view the JSON used by visiting the Swapi.co site
69 | https://swapi.dev/api/people/2/?format=api
70 |
71 | - Create a Person object that represents all the attributes of the Swapi.co format
72 | - and parse the string using GSON fromJson in the Person object
73 |
74 | - There are many ways to convert a Json string to objects
75 | - https://stackoverflow.com/questions/4110664/gson-directly-convert-string-to-jsonobject-no-pojo
76 | - Try parsing and using Json Objects the `new JsonParser().parse(swapidata).getAsJsonObject()`
77 |
78 | */
79 | }
80 |
--------------------------------------------------------------------------------
/automating/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | library-examples
7 | uk.co.compendiumdev
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | automating
13 |
14 |
15 |
16 |
19 |
20 | org.seleniumhq.selenium
21 | selenium-java
22 | 3.141.59
23 |
24 |
25 |
28 |
29 | org.seleniumhq.selenium
30 | htmlunit-driver
31 | 2.33.3
32 |
33 |
34 |
35 |
36 | io.rest-assured
37 | rest-assured
38 | 3.2.0
39 |
40 |
41 |
42 |
43 |
44 | org.jsoup
45 | jsoup
46 | 1.11.2
47 |
48 |
49 |
50 |
53 |
54 |
55 | org.hamcrest
56 | hamcrest-all
57 | 1.3
58 |
59 |
60 |
61 |
64 |
65 |
66 | com.google.code.gson
67 | gson
68 | 2.8.5
69 |
70 |
71 |
72 |
79 |
80 |
81 | io.cucumber
82 | cucumber-java
83 | 4.2.0
84 | test
85 |
86 |
87 | io.cucumber
88 | cucumber-junit
89 | 4.2.0
90 | test
91 |
92 |
93 |
94 | junit
95 | junit
96 | 4.12
97 | test
98 |
99 |
100 |
101 |
102 |
104 |
105 |
106 |
107 | org.apache.maven.plugins
108 | maven-compiler-plugin
109 | 3.1
110 |
111 | 1.8
112 | 1.8
113 | UTF-8
114 |
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/charting/src/test/java/xchart/XchartExamplesTest.java:
--------------------------------------------------------------------------------
1 | package xchart;
2 |
3 | import org.junit.jupiter.api.BeforeAll;
4 | import org.junit.jupiter.api.Test;
5 | import org.knowm.xchart.*;
6 | import org.knowm.xchart.style.lines.SeriesLines;
7 | import org.knowm.xchart.style.markers.SeriesMarkers;
8 |
9 | import java.io.File;
10 | import java.io.IOException;
11 | import java.time.LocalDate;
12 | import java.time.ZoneId;
13 | import java.util.ArrayList;
14 | import java.util.Date;
15 | import java.util.List;
16 |
17 | public class XchartExamplesTest {
18 |
19 | /*
20 | Useful Links:
21 |
22 | - https://knowm.org/open-source/xchart/
23 | - https://github.com/knowm/XChart
24 | - https://knowm.org/open-source/xchart/xchart-example-code/
25 |
26 | */
27 | static List xData = new ArrayList();
28 | static List yData = new ArrayList();
29 |
30 | static File outputPath;
31 |
32 | @BeforeAll
33 | static void dataSetup(){
34 | // JFreeChart will order dates, xChart displays data in the order provided
35 | for(int sample=1; sample < 10; sample++){
36 | LocalDate localDate = LocalDate.now().withDayOfMonth(sample);
37 | int year = localDate.getYear();
38 | int month = localDate.getMonthValue();
39 | int day = localDate.getDayOfMonth();
40 | int value = Long.valueOf(1000 + Math.round(Math.random()*100)).intValue();
41 |
42 | System.out.println(String.format("%d/%02d/%02d - %d", year, month, day, value));
43 |
44 | xData.add(Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()));
45 | yData.add(value);
46 | }
47 |
48 | outputPath = new File(System.getProperty("user.dir"), "/output/images");
49 | outputPath.mkdirs();
50 |
51 | }
52 |
53 | @Test
54 | void exampleXYGraph() throws IOException {
55 |
56 | XYChart xchart = new XYChartBuilder().
57 | width(600).height(400).
58 | title("Values at Date").
59 | xAxisTitle("Date").yAxisTitle("Values").
60 | build();
61 |
62 | xchart.getStyler().setLegendVisible(false);
63 | xchart.getStyler().setDatePattern("dd-MMM");
64 |
65 | XYSeries xseries = xchart.addSeries("Values", xData, yData);
66 | xseries.setMarker(SeriesMarkers.CIRCLE);
67 | xseries.setLineStyle(SeriesLines.SOLID);
68 |
69 | File svg = new File(outputPath, "xchartxychart.svg");
70 | VectorGraphicsEncoder.saveVectorGraphic(
71 | xchart,
72 | svg.getAbsolutePath(),
73 | VectorGraphicsEncoder.VectorGraphicsFormat.SVG);
74 |
75 | File png = new File(outputPath, "xchartxychart.png");
76 | BitmapEncoder.saveBitmap(
77 | xchart,
78 | png.getAbsolutePath(),
79 | BitmapEncoder.BitmapFormat.PNG);
80 |
81 | }
82 |
83 | @Test
84 | void exampleBarGraph() throws IOException {
85 |
86 | CategoryChart barchart = new CategoryChartBuilder().
87 | width(600).height(400).
88 | title("Values at Date")
89 | .xAxisTitle("Date").yAxisTitle("Values").
90 | build();
91 |
92 | // Customize Chart
93 | barchart.getStyler().setLegendVisible(false);
94 | barchart.getStyler().setHasAnnotations(true);
95 | barchart.getStyler().setDatePattern("dd-MMM");
96 |
97 | // Series
98 | barchart.addSeries("values",xData, yData);
99 |
100 | File svg = new File(outputPath, "xchartbarchart.svg");
101 | VectorGraphicsEncoder.saveVectorGraphic(
102 | barchart,
103 | svg.getAbsolutePath(),
104 | VectorGraphicsEncoder.VectorGraphicsFormat.SVG);
105 |
106 | File png = new File(outputPath, "xchartbarchart.png");
107 | BitmapEncoder.saveBitmap(
108 | barchart,
109 | png.getAbsolutePath(),
110 | BitmapEncoder.BitmapFormat.PNG);
111 | }
112 |
113 | }
114 |
--------------------------------------------------------------------------------
/charting/src/test/java/jfreechart/JFreeChartExamplesTest.java:
--------------------------------------------------------------------------------
1 | package jfreechart;
2 |
3 | import org.jfree.chart.ChartFactory;
4 | import org.jfree.chart.ChartUtils;
5 | import org.jfree.chart.JFreeChart;
6 | import org.jfree.data.category.CategoryDataset;
7 | import org.jfree.data.category.DefaultCategoryDataset;
8 | import org.jfree.data.time.Day;
9 | import org.jfree.data.time.TimeSeries;
10 | import org.jfree.data.time.TimeSeriesCollection;
11 | import org.jfree.svg.SVGGraphics2D;
12 | import org.jfree.svg.SVGUtils;
13 | import org.junit.jupiter.api.BeforeAll;
14 | import org.junit.jupiter.api.Test;
15 |
16 |
17 | import java.awt.*;
18 | import java.awt.image.BufferedImage;
19 | import java.io.File;
20 | import java.io.IOException;
21 | import java.time.LocalDate;
22 |
23 |
24 | public class JFreeChartExamplesTest {
25 |
26 | /*
27 | Useful links:
28 | - http://www.jfree.org/forum
29 | - http://www.jfree.org/jfreechart/
30 | - https://github.com/jfree/jfreechart
31 | - https://github.com/jfree/jfree-demos
32 | */
33 |
34 | static TimeSeries series = new TimeSeries("Values");
35 |
36 | static File outputPath;
37 |
38 | @BeforeAll
39 | static void dataSetup(){
40 | // JFreeChart will order dates, xChart displays data in the order provided
41 | for(int sample=1; sample < 10; sample++){
42 | LocalDate localDate = LocalDate.now().withDayOfMonth(sample);
43 | int year = localDate.getYear();
44 | int month = localDate.getMonthValue();
45 | int day = localDate.getDayOfMonth();
46 | int value = Long.valueOf(1000 + Math.round(Math.random()*100)).intValue();
47 |
48 | System.out.println(String.format("%d/%02d/%02d - %d", year, month, day, value));
49 |
50 | series.add(new Day(day, month, year),
51 | Integer.valueOf(value).doubleValue());
52 |
53 | }
54 |
55 | outputPath = new File(System.getProperty("user.dir"), "/output/images");
56 | outputPath.mkdirs();
57 |
58 | }
59 |
60 | @Test
61 | void exampleXYGraph() throws IOException {
62 |
63 | TimeSeriesCollection dataset = new TimeSeriesCollection();
64 |
65 | dataset.addSeries(series);
66 |
67 | JFreeChart chart = ChartFactory.createTimeSeriesChart(
68 | "Values at Date",
69 | "Date", "Values", dataset);
70 |
71 |
72 | SVGGraphics2D g2 = new SVGGraphics2D(600, 400);
73 | g2.setRenderingHint(JFreeChart.KEY_SUPPRESS_SHADOW_GENERATION, true);
74 | Rectangle r = new Rectangle(0, 0, 600, 400);
75 | chart.draw(g2, r);
76 |
77 | File svg = new File(outputPath, "jfreechartxychart.svg");
78 | SVGUtils.writeToSVG(svg, g2.getSVGElement());
79 |
80 | BufferedImage image = new BufferedImage(600, 400, BufferedImage.TYPE_INT_ARGB);
81 | Graphics2D bmp = image.createGraphics();
82 |
83 | bmp.setRenderingHint(JFreeChart.KEY_SUPPRESS_SHADOW_GENERATION, true);
84 | r = new Rectangle(0, 0, 600, 400);
85 | chart.draw(bmp, r);
86 |
87 | File png = new File(outputPath, "jfreechartxychart.png");
88 | ChartUtils.saveChartAsPNG(png, chart, 600, 400);
89 | }
90 |
91 | @Test
92 | void exampleBarGraph() throws IOException {
93 |
94 | TimeSeriesCollection dataset = new TimeSeriesCollection();
95 |
96 | dataset.addSeries(series);
97 |
98 | JFreeChart chart = ChartFactory.createXYBarChart(
99 | "Values at Date",
100 | "Date", true, "Values", dataset);
101 |
102 |
103 | SVGGraphics2D g2 = new SVGGraphics2D(600, 400);
104 | g2.setRenderingHint(JFreeChart.KEY_SUPPRESS_SHADOW_GENERATION, true);
105 | Rectangle r = new Rectangle(0, 0, 600, 400);
106 | chart.draw(g2, r);
107 |
108 | File svg = new File(outputPath, "jfreechartbarchart.svg");
109 | SVGUtils.writeToSVG(svg, g2.getSVGElement());
110 |
111 |
112 | BufferedImage image = new BufferedImage(600, 400, BufferedImage.TYPE_INT_ARGB);
113 | Graphics2D bmp = image.createGraphics();
114 |
115 | bmp.setRenderingHint(JFreeChart.KEY_SUPPRESS_SHADOW_GENERATION, true);
116 | r = new Rectangle(0, 0, 600, 400);
117 | chart.draw(bmp, r);
118 |
119 | File png = new File(outputPath, "jfreechartbarchart.png");
120 | ChartUtils.saveChartAsPNG(png, chart, 600, 400);
121 | }
122 |
123 | }
124 |
--------------------------------------------------------------------------------
/automating/src/test/java/uk/co/compendiumdev/libraryexamples/restassured/SwapiAPIUsageTest.java:
--------------------------------------------------------------------------------
1 | package uk.co.compendiumdev.libraryexamples.restassured;
2 |
3 |
4 | import io.restassured.RestAssured;
5 | import io.restassured.path.json.JsonPath;
6 | import io.restassured.response.Response;
7 | import org.junit.Assert;
8 | import org.junit.Test;
9 |
10 | import static org.hamcrest.core.IsEqual.equalTo;
11 |
12 | public class SwapiAPIUsageTest {
13 |
14 |
15 | /*
16 |
17 | - REST/HTTP abstraction layer
18 | - https://code.google.com/p/rest-assured/
19 | - Easier and more reliable than apache http client
20 | - https://hc.apache.org/
21 | - see also http://www.compendiumdev.co.uk/page/tracksrestapibook
22 |
23 |
24 |
25 | */
26 |
27 | // use basic RestAssured to get a JSON object form url
28 | // and parse with the in built assertions using Hamcrest matchers
29 | @Test
30 | public void canGetLuke(){
31 |
32 | RestAssured.get("https://swapi.dev/api/people/1/?format=json").
33 | then().
34 | assertThat().
35 | body("name",
36 | equalTo("Luke Skywalker"));
37 | }
38 |
39 | // use RestAssured to make call then return the Response
40 | // use JsonPath to parse the JSON in response
41 | // JsonPath can be imported and used separately from RestAssured if required
42 | @Test
43 | public void canGetC3POandParseWithJsonPath(){
44 |
45 | // use RestAssured to make an HTML Call
46 | Response response = RestAssured.get(
47 | "https://swapi.dev/api/people/2/?format=json").
48 | andReturn();
49 |
50 | String json = response.getBody().asString();
51 | System.out.println(json);
52 |
53 | // Use the JsonPath parsing library of RestAssured to Parse the JSON
54 |
55 | JsonPath jsonPath = new JsonPath(json);
56 | Assert.assertEquals(
57 | "C-3PO",
58 | jsonPath.getString("name"));
59 |
60 | }
61 |
62 |
63 | /*
64 |
65 | Assert that Luke Skywalker is male
66 | Assert that C-3PO gender is n/a
67 |
68 |
69 | Exercise
70 | - read the RestAssured documentation https://github.com/rest-assured/rest-assured/wiki/Usage
71 | - read the Swapi Documentation - https://swapi.co/documentation
72 | - Experiment with the API
73 |
74 | */
75 |
76 |
77 | private class Person{
78 | public String name;
79 | public int mass;
80 | }
81 |
82 |
83 | // JsonPath can parse Json directly into an object, like Gson can
84 | @Test
85 | public void canGetC3POandParseWithJsonPathIntoObject(){
86 |
87 | // use RestAssured to make an HTML Call
88 | Response response = RestAssured.get(
89 | "https://swapi.dev/api/people/2/?format=json").
90 | andReturn();
91 |
92 | String json = response.getBody().asString();
93 | System.out.println(json);
94 |
95 | // Use the JsonPath parsing library of RestAssured to Parse the JSON into an object
96 |
97 | Person c3po = new JsonPath(json).getObject("$", Person.class);
98 | Assert.assertEquals(
99 | "C-3PO",
100 | c3po.name);
101 |
102 | }
103 |
104 |
105 |
106 | /* Exercises:
107 |
108 | You can easily view the JSON used by visiting the Swapi.co site
109 | https://swapi.dev/api/people/2/?format=api
110 |
111 | - automate more of the Swapi API e.g. planets etc.
112 |
113 | - Create a Person object that represents all the attributes of the Swapi.co format
114 | - and parse the string using JsonPath into the Person object
115 |
116 | - Create an abstraction layer for the Swapi
117 | - e.g.
118 |
119 | ~~~~~~~~
120 | Swapi swapi = new Swapi();
121 | String json = swapi.getPersonJson(1);
122 | ~~~~~~~~
123 |
124 | - extend the abstraction layer to have a Person object which models the star wars person as an object
125 | - see Gson example of RestAssured JsonPath example for how to do this
126 |
127 | - e.g.
128 |
129 | ~~~~~~~~
130 | Person luke = swapi.getPerson(1);
131 | Assert.assertTrue("Luke Skywalker",luke.getName());
132 | ~~~~~~~~
133 |
134 | - add dependency injection to the Swapi so you have different implementations
135 | - create a `SwapiApi` Interface to support different implementations
136 |
137 | ~~~~~~~~
138 | Swapi swapi = new Swapi(new JsoupBackedSwapi());
139 | Swapi swapi = new Swapi(new RestAssuredBackedSwapi());
140 | Swapi swapi = new Swapi(new WebDriverBackedSwapi());
141 | ~~~~~~~~
142 |
143 | */
144 |
145 |
146 | }
147 |
--------------------------------------------------------------------------------
/automating/src/test/java/uk/co/compendiumdev/libraryexamples/restassured/DownloadAFileExampleTest.java:
--------------------------------------------------------------------------------
1 | package uk.co.compendiumdev.libraryexamples.restassured;
2 |
3 | import io.restassured.RestAssured;
4 | import io.restassured.filter.log.UrlDecoder;
5 | import io.restassured.response.Response;
6 | import org.junit.Assert;
7 | import org.junit.Test;
8 |
9 | import java.io.File;
10 | import java.io.FileOutputStream;
11 | import java.io.IOException;
12 | import java.io.OutputStream;
13 | import java.nio.charset.Charset;
14 | import java.util.HashMap;
15 | import java.util.Map;
16 | import java.util.UUID;
17 |
18 | public class DownloadAFileExampleTest {
19 |
20 | /**
21 | This example shows how to download a file with RESTAssured
22 |
23 | This will download the file as binary so can be used for images, zip files etc.
24 |
25 | The example is coded to download a source file from github, but you can download anything.
26 |
27 | I sometimes use this during GUI automating to download files.
28 |
29 | Also I use this when interacting with sites that don't provide an API.
30 | */
31 | @Test
32 | public void canDownloadFilesWithRestAssured() throws IOException {
33 |
34 | // By default this is going to be a subfolder in your project, you can change this to an
35 | // absolute path or resources if you want to. I kept it simple for the example
36 | String downloadFolder = "downloads";
37 | File outputPath = new File(downloadFolder);
38 |
39 | // create the folder structure if it does not exist
40 | outputPath.mkdirs();
41 |
42 | // sometimes we might be bypassing login or need login credentials created by cookies
43 | // we can create a hashmap of cookies if we need to
44 | Map cookies = new HashMap();
45 | // e.g. if I needed to inject a session cookie
46 | //cookies.put("session_id", Secret.SESSION_ID);
47 |
48 | // sometimes our access controls might be via headers so I might need to set those up
49 | // we can create a hashmap of headers if we need to
50 | Map headers = new HashMap();
51 | //cookies.put("X-AUTH-CODE", Secret.AUTH_CODE_HEADER);
52 |
53 | // if your url was extracted from a json respose in another message then you might need to decode it first
54 | // to make sure it is a completely valid URL e.g. doesn't have any \u0026 type values
55 | String urlToDownload="https://avatars3.githubusercontent.com/u/2621217?s=40&v=4";
56 | //String urlToDownload="https://raw.githubusercontent.com/eviltester/libraryexamples/master/src/test/java/uk/co/compendiumdev/libraryexamples/restassured/SwapiAPIUsageTest.java";
57 | urlToDownload = UrlDecoder.urlDecode(urlToDownload, Charset.defaultCharset(), false);
58 |
59 |
60 | // Sometimes I add a timestamp to the file e.g.
61 | //String downloadFileName = "downloadedFile_" + System.currentTimeMillis() + "_.txt";
62 |
63 | // Sometimes I add a GUID to the file e.g.
64 | //String downloadFileName = "downloadedFile_" + UUID.randomUUID() + "_.txt";
65 |
66 | // the point is, control the filename so you know what you are downloading
67 | String downloadFileName = "downloadedFile.png";
68 |
69 |
70 | // For the purpose of the test, if the file already exists then I will delete it
71 |
72 | File checkDownloaded = new File(outputPath.getPath(), downloadFileName);
73 | if(checkDownloaded.exists()) {
74 | checkDownloaded.delete();
75 | }
76 |
77 | // get image using RestAssured
78 | downloadUrlAsFile(cookies, headers, urlToDownload, outputPath, downloadFileName);
79 |
80 |
81 | // Added an assert to check if file exists
82 | checkDownloaded = new File(outputPath.getPath(), downloadFileName);
83 | Assert.assertTrue(checkDownloaded.exists());
84 |
85 |
86 | }
87 |
88 | private void downloadUrlAsFile(final Map cookies, final Map headers, final String urlToDownload, final File outputPath, final String filename) throws IOException {
89 |
90 | File outputFile = new File(outputPath.getPath(), filename);
91 |
92 |
93 | final Response response = RestAssured.given().headers(headers).cookies(cookies).when().get(urlToDownload).andReturn();
94 |
95 | // check if the URL actually exists
96 | if(response.getStatusCode() == 200){
97 |
98 | // I am choosing to delete the file if it already exists and write it again
99 | // if it already exists you might choose to return and not overwrite it
100 | if (outputFile.exists()) {
101 | outputFile.delete();
102 | }
103 |
104 | // I might choose to use the mime type of the file to control the file extension
105 | // here I am just outputting it to the console to demonstrate how to get the type
106 | System.out.println("Downloaded an " + response.getHeader("Content-Type"));
107 |
108 | // get the contents of the file
109 | byte[] fileContents = response.getBody().asByteArray();
110 |
111 | // output contents to file
112 | OutputStream outStream=null;
113 |
114 | try {
115 |
116 | outStream = new FileOutputStream(outputFile);
117 | outStream.write(fileContents);
118 |
119 | }catch(Exception e){
120 |
121 | System.out.println("Error writing file " + outputFile.getAbsolutePath());
122 |
123 | }finally {
124 |
125 | if(outStream!=null){
126 | outStream.close();
127 | }
128 | }
129 | }
130 | }
131 |
132 | }
133 |
--------------------------------------------------------------------------------
/automating/src/test/java/uk/co/compendiumdev/libraryexamples/webdriver/SwapiGUIFormUsageTest.java:
--------------------------------------------------------------------------------
1 | package uk.co.compendiumdev.libraryexamples.webdriver;
2 |
3 |
4 | import com.gargoylesoftware.htmlunit.BrowserVersion;
5 | import org.junit.Assert;
6 | import org.junit.Ignore;
7 | import org.junit.Test;
8 | import org.openqa.selenium.By;
9 | import org.openqa.selenium.WebDriver;
10 | import org.openqa.selenium.WebElement;
11 | import org.openqa.selenium.chrome.ChromeDriver;
12 | import org.openqa.selenium.htmlunit.HtmlUnitDriver;
13 | import org.openqa.selenium.safari.SafariDriver;
14 | import org.openqa.selenium.support.ui.ExpectedConditions;
15 | import org.openqa.selenium.support.ui.WebDriverWait;
16 |
17 | import java.util.List;
18 |
19 | @Ignore("You need to amend for your setup and operating system to use this e.g. did you install a ChromeDriver? Are you on Mac for SafariDriver?")
20 | public class SwapiGUIFormUsageTest {
21 |
22 | /*
23 | This example uses WebDriver from http://www.seleniumhq.org/
24 | see also http://SeleniumSimplified.com
25 |
26 | // mac:
27 | // brew install chromedriver
28 |
29 | // mac: to use SafariDriver remember to configure safari to allow remote execution - follow instructions in the error message if you have not
30 |
31 | // windows:
32 | // choco install chromedriver
33 |
34 | */
35 |
36 | private WebDriver getDefaultDriver(){
37 |
38 | WebDriver driver;
39 |
40 | // comment out the lines you want to get the driver you need
41 |
42 | //driver = new HtmlUnitDriver(BrowserVersion.BEST_SUPPORTED, true);
43 | //((HtmlUnitDriver)driver).setJavascriptEnabled(true);
44 |
45 | //driver = new ChromeDriver();
46 |
47 | driver = new SafariDriver();
48 |
49 | return driver;
50 | }
51 |
52 | @Test
53 | public void canGetSwapiGUIPage(){
54 |
55 | WebDriver driver;
56 | driver = getDefaultDriver();
57 |
58 | //driver = new HtmlUnitDriver(BrowserVersion.BEST_SUPPORTED, true);
59 | //driver = new ChromeDriver();
60 |
61 | // On mac SafariDriver is built in
62 | //driver = new SafariDriver();
63 |
64 | driver.get("https://swapi.co/");
65 |
66 | Assert.assertTrue(driver.getTitle().contains("Star Wars"));
67 |
68 | driver.quit();
69 |
70 | }
71 |
72 |
73 |
74 | @Test
75 | public void usePersonApiGUIForLuke(){
76 |
77 | WebDriver driver;
78 |
79 | driver = getDefaultDriver();
80 |
81 | // this does actually work on HtmlUnitDriver
82 | //driver = new HtmlUnitDriver(BrowserVersion.CHROME, true);
83 | //((HtmlUnitDriver)driver).setJavascriptEnabled(true);
84 | //driver = new SafariDriver();
85 | //driver = new ChromeDriver();
86 |
87 | driver.get("https://swapi.dev/api/people/1/?format=api");
88 |
89 | WebElement response = driver.findElement(By.cssSelector("div.response-info > pre"));
90 |
91 | String json = response.getText();
92 |
93 | System.out.println(json);
94 | Assert.assertTrue(json.contains("Luke Skywalker"));
95 |
96 | driver.quit();
97 |
98 | }
99 |
100 | @Test
101 | public void usePersonApiGUIForC3PO(){
102 |
103 | WebDriver driver;
104 |
105 | driver = getDefaultDriver();
106 |
107 | // this does actually work on HtmlUnitDriver
108 | //driver = new HtmlUnitDriver(BrowserVersion.CHROME, true);
109 | //((HtmlUnitDriver)driver).setJavascriptEnabled(true);
110 | //driver = new SafariDriver();
111 |
112 | driver.get("https://swapi.dev/api/people/2/?format=api");
113 |
114 | WebElement response = driver.findElement(By.cssSelector("div.response-info > pre"));
115 |
116 | String json = response.getText();
117 |
118 | System.out.println(json);
119 | Assert.assertTrue(json.contains("C-3PO"));
120 |
121 | driver.quit();
122 |
123 | }
124 |
125 |
126 |
127 | @Test
128 | public void canSubmitFormFromGUIPage(){
129 |
130 | // this doesn't actually work on HtmlUnitDriver since the JavaScript is not interpreted properly
131 | WebDriver driver;
132 | driver = getDefaultDriver();
133 |
134 | //driver = new HtmlUnitDriver(BrowserVersion.CHROME, true);
135 | //((HtmlUnitDriver)driver).setJavascriptEnabled(true);
136 |
137 | //driver = new SafariDriver();
138 | //driver = new ChromeDriver();
139 |
140 | driver.get("https://swapi.co/");
141 |
142 | WebElement inputfield = driver.findElement(By.id("interactive"));
143 |
144 | inputfield.sendKeys("people/2/");
145 |
146 | driver.findElement(By.className("btn-primary")).click();
147 |
148 | new WebDriverWait(driver, 10).
149 | until(
150 | ExpectedConditions.
151 | textToBePresentInElementLocated(By.id("interactive_output"), "C-3PO"));
152 |
153 | WebElement output = driver.findElement(By.id("interactive_output"));
154 | String json = output.getText();
155 |
156 | Assert.assertTrue(json.contains("C-3PO"));
157 |
158 | driver.quit();
159 |
160 | }
161 |
162 |
163 | /* Exercise search for C-3PO instead of Luke Skywalker:
164 | If you are on Mac then you can uncomment out the SafariDriver lines and run the tests in Safari
165 | - you don't need to download ChromeDriver - although you can if you want to
166 |
167 | On windows you will need to download a Driver - currently recommend ChromeDriver
168 |
169 | Download ChromeDriver from https://sites.google.com/a/chromium.org/chromedriver/
170 | And add the chromedriver executable to your path
171 | comment out the usage of HtmlUnitDriver and enable use of ChromeDriver
172 | Search for "people/2/"
173 | Assert on the name "C-3PO"
174 | */
175 |
176 |
177 | @Test
178 | public void canClickSearchLink(){
179 |
180 | // this doesn't actually work on HtmlUnitDriver since the JavaScript is not interpreted properly
181 | WebDriver driver;
182 | driver = getDefaultDriver();
183 |
184 | //driver = new HtmlUnitDriver(BrowserVersion.CHROME, true);
185 | //((HtmlUnitDriver)driver).setJavascriptEnabled(true);
186 | //driver = new SafariDriver();
187 | //driver = new ChromeDriver();
188 |
189 | driver.get("https://swapi.co/");
190 |
191 | List links = driver.findElements(By.cssSelector("small > a"));
192 |
193 | links.get(0).click();
194 |
195 | WebElement output = driver.findElement(By.id("interactive_output"));
196 | String json = output.getText();
197 |
198 | System.out.println(json);
199 | Assert.assertTrue(json.contains("Luke Skywalker"));
200 |
201 | driver.quit();
202 |
203 | }
204 |
205 | /* Exercise search for C-3PO instead of Luke Skywalker:
206 | Download ChromeDriver from https://sites.google.com/a/chromium.org/chromedriver/
207 | And add the chromedriver executable to your path
208 | comment out the usage of HtmlUnitDriver and enable use of ChromeDriver
209 | click on the second link (1)
210 | Assert on the name "Yavin IV"
211 | */
212 |
213 | /* for a real 'hack'
214 |
215 | create a SwapiApi implementation that uses WebDriver and parses the Json from the GUI
216 |
217 | - ugh it is horrible but you will learn how to parse Strings
218 | - and there will a come a time when you have to automate like this
219 | */
220 |
221 | }
222 |
--------------------------------------------------------------------------------