diff --git a/framework-platform/framework-platform.gradle b/framework-platform/framework-platform.gradle index 986fed60b25..8fce3ed3215 100644 --- a/framework-platform/framework-platform.gradle +++ b/framework-platform/framework-platform.gradle @@ -138,7 +138,6 @@ dependencies { api("org.seleniumhq.selenium:selenium-java:4.41.0") api("org.skyscreamer:jsonassert:1.5.3") api("org.testng:testng:7.12.0") - api("org.webjars:underscorejs:1.8.3") api("org.webjars:webjars-locator-lite:1.1.0") api("org.xmlunit:xmlunit-assertj:2.10.4") api("org.xmlunit:xmlunit-matchers:2.10.4") diff --git a/spring-webflux/spring-webflux.gradle b/spring-webflux/spring-webflux.gradle index 16c9be3a271..8de09951ee5 100644 --- a/spring-webflux/spring-webflux.gradle +++ b/spring-webflux/spring-webflux.gradle @@ -61,7 +61,7 @@ dependencies { testRuntimeOnly("org.glassfish:jakarta.el") testRuntimeOnly("org.jruby:jruby") testRuntimeOnly("org.python:jython-standalone") - testRuntimeOnly("org.webjars:underscorejs") + testRuntimeOnly("org.webjars:momentjs:2.29.4") } test { diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/LiteWebJarsResourceResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/LiteWebJarsResourceResolver.java index 6983d2fddfa..3fd7b40c402 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/LiteWebJarsResourceResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/LiteWebJarsResourceResolver.java @@ -90,7 +90,8 @@ public class LiteWebJarsResourceResolver extends AbstractResourceResolver { .switchIfEmpty(Mono.defer(() -> { String webJarResourcePath = findWebJarResourcePath(resourceUrlPath); if (webJarResourcePath != null) { - return chain.resolveUrlPath(webJarResourcePath, locations); + Mono fallback = (webJarResourcePath.endsWith("/")) ? Mono.just(webJarResourcePath) : Mono.empty(); + return chain.resolveUrlPath(webJarResourcePath, locations).switchIfEmpty(fallback); } else { return Mono.empty(); diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/resource/LiteWebJarsResourceResolverTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/resource/LiteWebJarsResourceResolverTests.java index e7a5ce16cff..fe8d35f87c9 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/resource/LiteWebJarsResourceResolverTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/resource/LiteWebJarsResourceResolverTests.java @@ -29,6 +29,8 @@ import org.springframework.web.testfixture.http.server.reactive.MockServerHttpRe import org.springframework.web.testfixture.server.MockServerWebExchange; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -80,8 +82,8 @@ class LiteWebJarsResourceResolverTests { @Test void resolveUrlWebJarResource() { - String file = "underscorejs/underscore.js"; - String expected = "underscorejs/1.8.3/underscore.js"; + String file = "momentjs/momentjs.js"; + String expected = "momentjs/2.29.4/momentjs.js"; given(this.chain.resolveUrlPath(file, this.locations)).willReturn(Mono.empty()); given(this.chain.resolveUrlPath(expected, this.locations)).willReturn(Mono.just(expected)); @@ -92,10 +94,24 @@ class LiteWebJarsResourceResolverTests { verify(this.chain, times(1)).resolveUrlPath(expected, this.locations); } + @Test + void resolveUrlWebJarDirectory() { + String folder = "momentjs/locale/"; + String expected = "momentjs/2.29.4/locale/"; + given(this.chain.resolveUrlPath(folder, this.locations)).willReturn(Mono.empty()); + given(this.chain.resolveUrlPath(expected, this.locations)).willReturn(Mono.empty()); + + String actual = this.resolver.resolveUrlPath(folder, this.locations, this.chain).block(TIMEOUT); + + assertThat(actual).isEqualTo(expected); + verify(this.chain, times(1)).resolveUrlPath(folder, this.locations); + verify(this.chain, times(1)).resolveUrlPath(expected, this.locations); + } + @Test void resolveUrlWebJarResourceNotFound() { - String file = "something/something.js"; - given(this.chain.resolveUrlPath(file, this.locations)).willReturn(Mono.empty()); + String file = "momentjs/locale/unknown.js"; + given(this.chain.resolveUrlPath(anyString(), eq(this.locations))).willReturn(Mono.empty()); String actual = this.resolver.resolveUrlPath(file, this.locations, this.chain).block(TIMEOUT); @@ -134,11 +150,11 @@ class LiteWebJarsResourceResolverTests { @Test void resolveResourceWebJar() { - String file = "underscorejs/underscore.js"; + String file = "momentjs/momentjs.js"; given(this.chain.resolveResource(this.exchange, file, this.locations)).willReturn(Mono.empty()); Resource expected = mock(); - String expectedPath = "underscorejs/1.8.3/underscore.js"; + String expectedPath = "momentjs/2.29.4/momentjs.js"; given(this.chain.resolveResource(this.exchange, expectedPath, this.locations)) .willReturn(Mono.just(expected)); diff --git a/spring-webmvc/spring-webmvc.gradle b/spring-webmvc/spring-webmvc.gradle index 760530e3c87..194278ad7c0 100644 --- a/spring-webmvc/spring-webmvc.gradle +++ b/spring-webmvc/spring-webmvc.gradle @@ -80,5 +80,5 @@ dependencies { testRuntimeOnly("org.glassfish:jakarta.el") testRuntimeOnly("org.jruby:jruby") testRuntimeOnly("org.python:jython-standalone") - testRuntimeOnly("org.webjars:underscorejs") + testRuntimeOnly("org.webjars:momentjs:2.29.4") } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/LiteWebJarsResourceResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/LiteWebJarsResourceResolver.java index 1486063b0e2..358927ab1e7 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/LiteWebJarsResourceResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/LiteWebJarsResourceResolver.java @@ -88,7 +88,10 @@ public class LiteWebJarsResourceResolver extends AbstractResourceResolver { if (path == null) { String webJarResourcePath = findWebJarResourcePath(resourceUrlPath); if (webJarResourcePath != null) { - return chain.resolveUrlPath(webJarResourcePath, locations); + path = chain.resolveUrlPath(webJarResourcePath, locations); + if (path == null && webJarResourcePath.endsWith("/")) { + path = webJarResourcePath; + } } } return path; diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/LiteWebJarsResourceResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/LiteWebJarsResourceResolverTests.java index 101c0630692..724ab79d87a 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/LiteWebJarsResourceResolverTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/LiteWebJarsResourceResolverTests.java @@ -26,6 +26,8 @@ import org.springframework.core.io.Resource; import org.springframework.web.testfixture.servlet.MockHttpServletRequest; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -74,8 +76,8 @@ class LiteWebJarsResourceResolverTests { @Test void resolveUrlWebJarResource() { - String file = "underscorejs/underscore.js"; - String expected = "underscorejs/1.8.3/underscore.js"; + String file = "momentjs/momentjs.js"; + String expected = "momentjs/2.29.4/momentjs.js"; given(this.chain.resolveUrlPath(file, this.locations)).willReturn(null); given(this.chain.resolveUrlPath(expected, this.locations)).willReturn(expected); @@ -86,10 +88,24 @@ class LiteWebJarsResourceResolverTests { verify(this.chain, times(1)).resolveUrlPath(expected, this.locations); } + @Test + void resolveUrlWebJarDirectory() { + String folder = "momentjs/locale/"; + String expected = "momentjs/2.29.4/locale/"; + given(this.chain.resolveUrlPath(folder, this.locations)).willReturn(null); + given(this.chain.resolveUrlPath(expected, this.locations)).willReturn(null); + + String actual = this.resolver.resolveUrlPath(folder, this.locations, this.chain); + + assertThat(actual).isEqualTo(expected); + verify(this.chain, times(1)).resolveUrlPath(folder, this.locations); + verify(this.chain, times(1)).resolveUrlPath(expected, this.locations); + } + @Test void resolveUrlWebJarResourceNotFound() { - String file = "something/something.js"; - given(this.chain.resolveUrlPath(file, this.locations)).willReturn(null); + String file = "momentjs/locale/unknown.js"; + given(this.chain.resolveUrlPath(anyString(), eq(this.locations))).willReturn(null); String actual = this.resolver.resolveUrlPath(file, this.locations, this.chain); @@ -125,8 +141,8 @@ class LiteWebJarsResourceResolverTests { @Test void resolveResourceWebJar() { Resource expected = mock(); - String file = "underscorejs/underscore.js"; - String expectedPath = "underscorejs/1.8.3/underscore.js"; + String file = "momentjs/momentjs.js"; + String expectedPath = "momentjs/2.29.4/momentjs.js"; given(this.chain.resolveResource(this.request, expectedPath, this.locations)).willReturn(expected); Resource actual = this.resolver.resolveResource(this.request, file, this.locations, this.chain);