diff --git a/.gitignore b/.gitignore index d831c00..4698d60 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,10 @@ /.project /node_modules /webpack.generated.js +/webpack.config.js /node /package.json /package-lock.json +/frontend/index.html +/frontend/generated +/error-screenshots diff --git a/pom.xml b/pom.xml index 8da6b5e..b47d09a 100644 --- a/pom.xml +++ b/pom.xml @@ -128,6 +128,34 @@ jar test + + com.vaadin + vaadin-testbench + test + + + com.vaadin + license-checker + test + + + org.hamcrest + hamcrest-library + 1.3 + test + + + io.github.bonigarcia + webdrivermanager + 5.9.1 + test + + + com.flowingcode.vaadin.test + testbench-rpc + 1.3.0 + test + @@ -223,6 +251,89 @@ + + it + + + + org.eclipse.jetty + jetty-maven-plugin + ${jetty.version} + + 0 + + jar + + ${project.artifactId} + 8081 + + + + start-jetty + pre-integration-test + + start + + + + stop-jetty + post-integration-test + + stop + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + 2.22.2 + + + + integration-test + verify + + + + + false + true + + + + ${webdriver.chrome.driver} + + + + + + maven-resources-plugin + 3.1.0 + + + + copy-test-to-classes + process-test-classes + + copy-resources + + + ${project.build.outputDirectory} + + + ${project.build.testOutputDirectory} + + + + + + + + + + directory diff --git a/src/test/java/com/flowingcode/addons/simpletimer/integration/AbstractViewTest.java b/src/test/java/com/flowingcode/addons/simpletimer/integration/AbstractViewTest.java new file mode 100644 index 0000000..fff79d5 --- /dev/null +++ b/src/test/java/com/flowingcode/addons/simpletimer/integration/AbstractViewTest.java @@ -0,0 +1,106 @@ +/*- + * #%L + * Template Add-on + * %% + * Copyright (C) 2024 Flowing Code + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +package com.flowingcode.addons.simpletimer.integration; + +import com.vaadin.testbench.ScreenshotOnFailureRule; +import com.vaadin.testbench.TestBench; +import com.vaadin.testbench.parallel.ParallelTest; +import io.github.bonigarcia.wdm.WebDriverManager; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.openqa.selenium.chrome.ChromeDriver; + +/** + * Base class for ITs + * + *

The tests use Chrome driver (see pom.xml for integration-tests profile) to run integration + * tests on a headless Chrome. If a property {@code test.use .hub} is set to true, {@code + * AbstractViewTest} will assume that the TestBench test is running in a CI environment. In order to + * keep the this class light, it makes certain assumptions about the CI environment (such as + * available environment variables). It is not advisable to use this class as a base class for you + * own TestBench tests. + * + *

To learn more about TestBench, visit Vaadin TestBench. + */ +public abstract class AbstractViewTest extends ParallelTest { + private static final int SERVER_PORT = 8080; + + private final String route; + + @Rule public ScreenshotOnFailureRule rule = new ScreenshotOnFailureRule(this, true); + + public AbstractViewTest() { + this(""); + } + + protected AbstractViewTest(String route) { + this.route = route; + } + + @BeforeClass + public static void setupClass() { + WebDriverManager.chromedriver().setup(); + } + + @Override + @Before + public void setup() throws Exception { + if (isUsingHub()) { + super.setup(); + } else { + setDriver(TestBench.createDriver(new ChromeDriver())); + } + getDriver().get(getURL(route)); + } + + /** + * Returns deployment host name concatenated with route. + * + * @return URL to route + */ + private static String getURL(String route) { + return String.format("http://%s:%d/%s", getDeploymentHostname(), SERVER_PORT, route); + } + + /** Property set to true when running on a test hub. */ + private static final String USE_HUB_PROPERTY = "test.use.hub"; + + /** + * Returns whether we are using a test hub. This means that the starter is running tests in + * Vaadin's CI environment, and uses TestBench to connect to the testing hub. + * + * @return whether we are using a test hub + */ + private static boolean isUsingHub() { + return Boolean.TRUE.toString().equals(System.getProperty(USE_HUB_PROPERTY)); + } + + /** + * If running on CI, get the host name from environment variable HOSTNAME + * + * @return the host name + */ + private static String getDeploymentHostname() { + return isUsingHub() ? System.getenv("HOSTNAME") : "localhost"; + } +} \ No newline at end of file diff --git a/src/test/java/com/flowingcode/addons/simpletimer/integration/IntegrationCallables.java b/src/test/java/com/flowingcode/addons/simpletimer/integration/IntegrationCallables.java new file mode 100644 index 0000000..c1122d0 --- /dev/null +++ b/src/test/java/com/flowingcode/addons/simpletimer/integration/IntegrationCallables.java @@ -0,0 +1,17 @@ +package com.flowingcode.addons.simpletimer.integration; + +public interface IntegrationCallables { + + void setStartTime(Integer startTime); + + void setEndTime(Integer endTime); + + void start(); + + void pause(); + + void reset(); + + boolean isRunning(); + +} diff --git a/src/test/java/com/flowingcode/addons/simpletimer/integration/IntegrationView.java b/src/test/java/com/flowingcode/addons/simpletimer/integration/IntegrationView.java new file mode 100644 index 0000000..26386ce --- /dev/null +++ b/src/test/java/com/flowingcode/addons/simpletimer/integration/IntegrationView.java @@ -0,0 +1,53 @@ +package com.flowingcode.addons.simpletimer.integration; + +import com.flowingcode.vaadin.addons.simpletimer.SimpleTimer; +import com.vaadin.flow.component.ClientCallable; +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.router.Route; + +@Route("/it") +public class IntegrationView extends Div implements IntegrationCallables { + + private SimpleTimer timer = new SimpleTimer(); + + public IntegrationView() { + add(timer); + } + + @Override + @ClientCallable + public void setStartTime(Integer startTime) { + timer.setStartTime(startTime); + } + + @Override + @ClientCallable + public void setEndTime(Integer endTime) { + timer.setEndTime(endTime); + } + + @Override + @ClientCallable + public void start() { + timer.start(); + } + + @Override + @ClientCallable + public void pause() { + timer.pause(); + } + + @Override + @ClientCallable + public void reset() { + timer.reset(); + } + + @Override + @ClientCallable + public boolean isRunning() { + return timer.isRunning(); + } + +} diff --git a/src/test/java/com/flowingcode/addons/simpletimer/integration/SimpleIT.java b/src/test/java/com/flowingcode/addons/simpletimer/integration/SimpleIT.java new file mode 100644 index 0000000..a18902b --- /dev/null +++ b/src/test/java/com/flowingcode/addons/simpletimer/integration/SimpleIT.java @@ -0,0 +1,57 @@ +package com.flowingcode.addons.simpletimer.integration; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.lessThan; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import com.flowingcode.vaadin.testbench.rpc.HasRpcSupport; +import org.junit.Test; + +public class SimpleIT extends AbstractViewTest implements HasRpcSupport { + + IntegrationCallables $server = createCallableProxy(IntegrationCallables.class); + + public SimpleIT() { + super("it"); + } + + private Double currentTime() { + return $(SimpleTimerElement.class).first().currentTime(); + } + + @Test + public void countDown() { + assertThat(currentTime(), nullValue()); + assertFalse($server.isRunning()); + + $server.setStartTime(1); + assertThat(currentTime(), equalTo(1.0)); + + $server.start(); + assertTrue($server.isRunning()); + double t0 = currentTime(); + double t1 = currentTime(); + assertThat(t0, lessThan(1.0)); + assertThat(t1, lessThan(t0)); + } + + @Test + public void countUp() { + assertThat(currentTime(), nullValue()); + assertFalse($server.isRunning()); + + $server.setEndTime(1); + assertThat(currentTime(), equalTo(0.0)); + + $server.start(); + assertTrue($server.isRunning()); + double t0 = currentTime(); + double t1 = currentTime(); + assertThat(t0, greaterThan(0.0)); + assertThat(t1, greaterThan(t0)); + } + +} diff --git a/src/test/java/com/flowingcode/addons/simpletimer/integration/SimpleTimerElement.java b/src/test/java/com/flowingcode/addons/simpletimer/integration/SimpleTimerElement.java new file mode 100644 index 0000000..a84b5b2 --- /dev/null +++ b/src/test/java/com/flowingcode/addons/simpletimer/integration/SimpleTimerElement.java @@ -0,0 +1,15 @@ +package com.flowingcode.addons.simpletimer.integration; + +import com.vaadin.testbench.TestBenchElement; +import com.vaadin.testbench.elementsbase.Element; +import java.util.Optional; + +@Element("simple-timer") +public class SimpleTimerElement extends TestBenchElement { + + Double currentTime() { + Number time = (Number) executeScript("return arguments[0].currentTime", this); + return Optional.ofNullable(time).map(Number::doubleValue).orElse(null); + } + +} diff --git a/webpack.config.js b/webpack.config.js deleted file mode 100644 index 4e575ad..0000000 --- a/webpack.config.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * This file has been autogenerated as it didn't exist or was made for an older incompatible version. - * This file can be used for manual configuration will not be modified if the flowDefaults constant exists. - */ -const merge = require('webpack-merge'); -const flowDefaults = require('./webpack.generated.js'); - -module.exports = merge(flowDefaults, { - -}); - -/** - * This file can be used to configure the flow plugin defaults. - * - * // Add a custom plugin - * flowDefaults.plugins.push(new MyPlugin()); - * - * // Update the rules to also transpile `.mjs` files - * if (!flowDefaults.module.rules[0].test) { - * throw "Unexpected structure in generated webpack config"; - * } - * flowDefaults.module.rules[0].test = /\.m?js$/ - * - * // Include a custom JS in the entry point in addition to generated-flow-imports.js - * if (typeof flowDefaults.entry.index != "string") { - * throw "Unexpected structure in generated webpack config"; - * } - * flowDefaults.entry.index = [flowDefaults.entry.index, "myCustomFile.js"]; - * - * or add new configuration in the merge block. - * - * module.exports = merge(flowDefaults, { - * mode: 'development', - * devtool: 'inline-source-map' - * }); - * - */