From 2654b66522258d3e3a294bda713b4ab42dc408a3 Mon Sep 17 00:00:00 2001 From: Krisjanis Seglins Date: Tue, 12 Nov 2024 09:33:24 +0200 Subject: [PATCH] Add IT test for service worker and runtime Fixes #2867 --- .../spring/runtime/frontend/about-view.ts | 9 ++ .../tests/spring/runtime/frontend/index.html | 25 +++++ .../tests/spring/runtime/frontend/index.ts | 10 ++ .../spring/runtime/frontend/test-view.ts | 9 ++ packages/java/tests/spring/runtime/pom.xml | 49 ++++++++++ .../com/vaadin/flow/connect/Application.java | 19 ++++ .../vaadin/flow/connect/SecurityConfig.java | 39 ++++++++ .../src/main/resources/application.properties | 3 + .../vaadin/flow/connect/ServiceWorkerIT.java | 91 +++++++++++++++++++ 9 files changed, 254 insertions(+) create mode 100644 packages/java/tests/spring/runtime/frontend/about-view.ts create mode 100644 packages/java/tests/spring/runtime/frontend/index.html create mode 100644 packages/java/tests/spring/runtime/frontend/index.ts create mode 100644 packages/java/tests/spring/runtime/frontend/test-view.ts create mode 100644 packages/java/tests/spring/runtime/pom.xml create mode 100644 packages/java/tests/spring/runtime/src/main/java/com/vaadin/flow/connect/Application.java create mode 100644 packages/java/tests/spring/runtime/src/main/java/com/vaadin/flow/connect/SecurityConfig.java create mode 100644 packages/java/tests/spring/runtime/src/main/resources/application.properties create mode 100644 packages/java/tests/spring/runtime/src/test/java/com/vaadin/flow/connect/ServiceWorkerIT.java diff --git a/packages/java/tests/spring/runtime/frontend/about-view.ts b/packages/java/tests/spring/runtime/frontend/about-view.ts new file mode 100644 index 0000000000..6d340d7ec1 --- /dev/null +++ b/packages/java/tests/spring/runtime/frontend/about-view.ts @@ -0,0 +1,9 @@ +import { LitElement, html } from 'lit'; +import { customElement } from 'lit/decorators.js'; + +@customElement('about-view') +export class AboutView extends LitElement { + render() { + return html`
About
`; + } +} diff --git a/packages/java/tests/spring/runtime/frontend/index.html b/packages/java/tests/spring/runtime/frontend/index.html new file mode 100644 index 0000000000..0796ee9db0 --- /dev/null +++ b/packages/java/tests/spring/runtime/frontend/index.html @@ -0,0 +1,25 @@ + + + + + + ITs for Endpoints + + + + + + + +
+ + diff --git a/packages/java/tests/spring/runtime/frontend/index.ts b/packages/java/tests/spring/runtime/frontend/index.ts new file mode 100644 index 0000000000..05fd6f8850 --- /dev/null +++ b/packages/java/tests/spring/runtime/frontend/index.ts @@ -0,0 +1,10 @@ +import './test-view.js'; +import './about-view.js'; +import { Route, Router } from '@vaadin/router'; + +const routes: Route[] = [{ path: '/', component: 'test-view' }, + { path: '/about-view', component: 'about-view' }]; + +// Vaadin router needs an outlet in the index.html page to display views +const router = new Router(document.querySelector('#outlet')); +router.setRoutes(routes); diff --git a/packages/java/tests/spring/runtime/frontend/test-view.ts b/packages/java/tests/spring/runtime/frontend/test-view.ts new file mode 100644 index 0000000000..1a297969e0 --- /dev/null +++ b/packages/java/tests/spring/runtime/frontend/test-view.ts @@ -0,0 +1,9 @@ +import { LitElement, html } from 'lit'; +import { customElement } from 'lit/decorators.js'; + +@customElement('test-view') +export class TestView extends LitElement { + render() { + return html`
Hello
`; + } +} diff --git a/packages/java/tests/spring/runtime/pom.xml b/packages/java/tests/spring/runtime/pom.xml new file mode 100644 index 0000000000..d3b68b89eb --- /dev/null +++ b/packages/java/tests/spring/runtime/pom.xml @@ -0,0 +1,49 @@ + + + 4.0.0 + + com.vaadin + tests-spring + 24.6-SNAPSHOT + + + tests-hilla-runtime + + + true + ${project.parent.parent.parent.basedir} + + + + + com.vaadin + hilla + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-test + test + + + + + spring-boot:run + + + com.vaadin + hilla-maven-plugin + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/packages/java/tests/spring/runtime/src/main/java/com/vaadin/flow/connect/Application.java b/packages/java/tests/spring/runtime/src/main/java/com/vaadin/flow/connect/Application.java new file mode 100644 index 0000000000..1032cec197 --- /dev/null +++ b/packages/java/tests/spring/runtime/src/main/java/com/vaadin/flow/connect/Application.java @@ -0,0 +1,19 @@ +package com.vaadin.flow.connect; + +import com.vaadin.flow.component.page.AppShellConfigurator; +import com.vaadin.flow.server.PWA; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * The entry point of the Spring Boot application. + */ +@SpringBootApplication +@PWA(name = "My App", shortName = "app") +public class Application implements AppShellConfigurator { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/packages/java/tests/spring/runtime/src/main/java/com/vaadin/flow/connect/SecurityConfig.java b/packages/java/tests/spring/runtime/src/main/java/com/vaadin/flow/connect/SecurityConfig.java new file mode 100644 index 0000000000..01f0abb7f7 --- /dev/null +++ b/packages/java/tests/spring/runtime/src/main/java/com/vaadin/flow/connect/SecurityConfig.java @@ -0,0 +1,39 @@ +package com.vaadin.flow.connect; + +import com.vaadin.flow.spring.security.VaadinWebSecurity; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; + +@EnableWebSecurity +@Configuration +public class SecurityConfig extends VaadinWebSecurity { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.csrf(h -> h.ignoringRequestMatchers(new AntPathRequestMatcher("/login"))); + http.authorizeHttpRequests( + h -> h.requestMatchers(new AntPathRequestMatcher("/")) + .permitAll()); + http.authorizeHttpRequests( + h -> h.requestMatchers(new AntPathRequestMatcher("/about-view")) + .permitAll()); + super.configure(http); + setLoginView(http, "/login"); + } + + @Bean + public InMemoryUserDetailsManager userDetailsService() { + // Configure users and roles in memory + UserDetails user = User.withUsername("user").password("{noop}user") + .roles("USER").build(); + UserDetails admin = User.withUsername("admin").password("{noop}admin") + .roles("ADMIN", "USER").build(); + return new InMemoryUserDetailsManager(user, admin); + } +} diff --git a/packages/java/tests/spring/runtime/src/main/resources/application.properties b/packages/java/tests/spring/runtime/src/main/resources/application.properties new file mode 100644 index 0000000000..5f948b8b75 --- /dev/null +++ b/packages/java/tests/spring/runtime/src/main/resources/application.properties @@ -0,0 +1,3 @@ +server.port=8888 +vaadin.devmode.liveReload.enabled=false +logging.level.org.atmosphere = warn diff --git a/packages/java/tests/spring/runtime/src/test/java/com/vaadin/flow/connect/ServiceWorkerIT.java b/packages/java/tests/spring/runtime/src/test/java/com/vaadin/flow/connect/ServiceWorkerIT.java new file mode 100644 index 0000000000..2d582e0e83 --- /dev/null +++ b/packages/java/tests/spring/runtime/src/test/java/com/vaadin/flow/connect/ServiceWorkerIT.java @@ -0,0 +1,91 @@ +/* + * Copyright 2000-2024 Vaadin Ltd. + * + * 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. + */ +package com.vaadin.flow.connect; + +import com.vaadin.flow.testutil.ChromeDeviceTest; +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.JavascriptExecutor; + +public class ServiceWorkerIT extends ChromeDeviceTest { + + @Test + public void onlineRoot_serviceWorkerInstalled_serviceWorkerActive() { + getDriver().get(getRootURL() + "/"); + waitForDevServer(); + waitForServiceWorkerReady(); + + boolean serviceWorkerActive = (boolean) ((JavascriptExecutor) getDriver()) + .executeAsyncScript( + "const resolve = arguments[arguments.length - 1];" + + "navigator.serviceWorker.ready.then( function(reg) { resolve(!!reg.active); });"); + Assert.assertTrue("service worker not installed", serviceWorkerActive); + } + + @Test + public void offlineRoot_reload_viewReloaded() { + openPageAndPreCacheWhenDevelopmentMode("/"); + + // Confirm that app shell is loaded + Assert.assertNotNull("Should have outlet when loaded online", + findElement(By.id("outlet"))); + + // Confirm that client side view is loaded + Assert.assertNotNull( + "Should have in DOM when loaded online", + findElement(By.tagName("test-view"))); + + // Reload the page in offline mode + executeScript("window.location.reload();"); + waitUntil(webDriver -> ((JavascriptExecutor) driver) + .executeScript("return document.readyState") + .equals("complete")); + } + + private void openPageAndPreCacheWhenDevelopmentMode(String targetView) { + openPageAndPreCacheWhenDevelopmentMode(targetView, () -> { + }); + } + + private void openPageAndPreCacheWhenDevelopmentMode(String targetView, + Runnable activateViews) { + getDriver().get(getRootURL() + targetView); + waitForDevServer(); + waitForServiceWorkerReady(); + + boolean isDevMode = Boolean.getBoolean("vaadin.test.developmentMode"); + if (isDevMode) { + // In production mode all views are supposed to be already in the + // bundle, but in dev mode they are loaded at runtime + // So, for dev mode, pre cache required views + activateViews.run(); + + // In addition not all external resources are cached when the page + // opens + // first time, so we need to reload the page even if there is no + // navigation to other views + getDriver().get(getRootURL() + targetView); + waitForDevServer(); + waitForServiceWorkerReady(); + } + } + + @Override + protected String getRootURL() { + return super.getRootURL(); + } +}