mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Test runner
This commit is contained in:
parent
d007b0c8ac
commit
d29339f0b8
|
@ -45,6 +45,11 @@
|
|||
<artifactId>hppc</artifactId>
|
||||
<version>0.6.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<name>TeaVM core</name>
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package org.teavm.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
|
@ -132,6 +134,7 @@ public class MethodReference {
|
|||
}
|
||||
|
||||
@Override
|
||||
@JsonValue
|
||||
public String toString() {
|
||||
if (reprCache == null) {
|
||||
reprCache = className + "." + name + signatureToString();
|
||||
|
@ -139,6 +142,7 @@ public class MethodReference {
|
|||
return reprCache;
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
public static MethodReference parse(String string) {
|
||||
MethodReference reference = parseIfPossible(string);
|
||||
if (reference == null) {
|
||||
|
|
|
@ -36,8 +36,6 @@
|
|||
<module>video</module>
|
||||
<module>async</module>
|
||||
<module>kotlin</module>
|
||||
<!--
|
||||
<module>scala</module>
|
||||
-->
|
||||
</modules>
|
||||
</project>
|
|
@ -27,6 +27,12 @@
|
|||
<name>TeaVM tests</name>
|
||||
<description>Project containing TeaVM tests, as it is impossible to test each module separately</description>
|
||||
|
||||
<properties>
|
||||
<teavm.test.incremental>false</teavm.test.incremental>
|
||||
<teavm.test.threads>1</teavm.test.threads>
|
||||
<teavm.test.selenium></teavm.test.selenium>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.teavm</groupId>
|
||||
|
@ -93,6 +99,10 @@
|
|||
<goals>
|
||||
<goal>test</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<numThreads>${teavm.test.threads}</numThreads>
|
||||
<seleniumURL>${teavm.test.selenium}</seleniumURL>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
<artifactId>teavm-tooling</artifactId>
|
||||
<packaging>bundle</packaging>
|
||||
|
||||
<name>TeaVM tooling core</name>
|
||||
<description>TeaVM API that helps to create tooling</description>
|
||||
|
@ -59,6 +60,17 @@
|
|||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Export-Package>org.teavm.*</Export-Package>
|
||||
<Bundle-SymbolicName>teavm-tooling</Bundle-SymbolicName>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -71,5 +71,6 @@ Export-Package: org.teavm.cache,
|
|||
org.teavm.resource,
|
||||
org.teavm.testing,
|
||||
org.teavm.tooling,
|
||||
org.teavm.tooling.sources,
|
||||
org.teavm.vm,
|
||||
org.teavm.vm.spi
|
||||
|
|
|
@ -4,7 +4,7 @@ Bundle-Name: TeaVM plugin for m2e
|
|||
Bundle-SymbolicName: teavm-eclipse-m2e-plugin;singleton:=true
|
||||
Bundle-Version: 0.4.0.qualifier
|
||||
Bundle-Vendor: Alexey Andreev <konsoletyper@gmail.com>
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Require-Bundle: teavm-eclipse-plugin;bundle-version="[0.4.0,0.5.0)",
|
||||
org.eclipse.m2e.core;bundle-version="[1.3,2)",
|
||||
org.eclipse.core.runtime;bundle-version="[3.8,4.0)",
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<artifactId>teavm-maven-plugin</artifactId>
|
||||
<versionRange>0.4.0-SNAPSHOT</versionRange>
|
||||
<goals>
|
||||
<goal>build-javascript</goal>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</pluginExecutionFilter>
|
||||
<action>
|
||||
|
|
|
@ -33,7 +33,7 @@ public class TeaVMProjectConfigurator extends AbstractProjectConfigurator {
|
|||
private static final String TOOL_ID = "teavm-eclipse-m2e-plugin.tool";
|
||||
private static final String TEAVM_ARTIFACT_ID = "teavm-maven-plugin";
|
||||
private static final String TEAVM_GROUP_ID = "org.teavm";
|
||||
private static final String TEAVM_MAIN_GOAL = "build-javascript";
|
||||
private static final String TEAVM_MAIN_GOAL = "compile";
|
||||
private int executionIdGenerator;
|
||||
private Set<String> usedExecutionIds = new HashSet<>();
|
||||
private IMaven maven;
|
||||
|
|
|
@ -4,7 +4,7 @@ Bundle-Name: TeaVM plugin for Eclipse
|
|||
Bundle-SymbolicName: teavm-eclipse-plugin;singleton:=true
|
||||
Bundle-Version: 0.4.0.qualifier
|
||||
Bundle-Vendor: Alexey Andreev <konsoletyper@gmail.com>
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Bundle-Activator: org.teavm.eclipse.TeaVMEclipsePlugin
|
||||
Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.8.0,4.0)",
|
||||
org.eclipse.debug.core;bundle-version="[3.7.0,4.0)",
|
||||
|
@ -48,5 +48,6 @@ Import-Package: org.teavm.cache,
|
|||
org.teavm.resource,
|
||||
org.teavm.testing,
|
||||
org.teavm.tooling,
|
||||
org.teavm.tooling.sources,
|
||||
org.teavm.vm,
|
||||
org.teavm.vm.spi
|
||||
|
|
|
@ -21,22 +21,62 @@ import java.lang.reflect.InvocationTargetException;
|
|||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.*;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.eclipse.core.resources.*;
|
||||
import org.eclipse.core.runtime.*;
|
||||
import org.eclipse.core.resources.IContainer;
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IMarker;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.resources.IResourceDelta;
|
||||
import org.eclipse.core.resources.IWorkspaceRoot;
|
||||
import org.eclipse.core.resources.IncrementalProjectBuilder;
|
||||
import org.eclipse.core.resources.ResourcesPlugin;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.core.runtime.SubProgressMonitor;
|
||||
import org.eclipse.core.runtime.jobs.Job;
|
||||
import org.eclipse.core.variables.IStringVariableManager;
|
||||
import org.eclipse.core.variables.VariablesPlugin;
|
||||
import org.eclipse.jdt.core.*;
|
||||
import org.eclipse.jdt.core.IClasspathEntry;
|
||||
import org.eclipse.jdt.core.ICompilationUnit;
|
||||
import org.eclipse.jdt.core.IJavaElement;
|
||||
import org.eclipse.jdt.core.IJavaProject;
|
||||
import org.eclipse.jdt.core.IMethod;
|
||||
import org.eclipse.jdt.core.IType;
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
import org.eclipse.jdt.core.Signature;
|
||||
import org.teavm.callgraph.CallGraph;
|
||||
import org.teavm.callgraph.CallGraphNode;
|
||||
import org.teavm.callgraph.CallSite;
|
||||
import org.teavm.diagnostics.Problem;
|
||||
import org.teavm.diagnostics.ProblemTextConsumer;
|
||||
import org.teavm.model.*;
|
||||
import org.teavm.tooling.*;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.InstructionLocation;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.tooling.ClassAlias;
|
||||
import org.teavm.tooling.RuntimeCopyOperation;
|
||||
import org.teavm.tooling.TeaVMTool;
|
||||
import org.teavm.tooling.TeaVMToolException;
|
||||
import org.teavm.tooling.sources.DirectorySourceFileProvider;
|
||||
import org.teavm.tooling.sources.JarSourceFileProvider;
|
||||
import org.teavm.tooling.sources.SourceFileProvider;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
|
@ -28,7 +28,6 @@ import java.util.Arrays;
|
|||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
@ -82,12 +81,6 @@ public class BuildJavascriptTestMojo extends AbstractJavascriptMojo {
|
|||
@Parameter
|
||||
private String[] additionalScripts;
|
||||
|
||||
@Parameter
|
||||
private Properties properties;
|
||||
|
||||
@Parameter
|
||||
private boolean incremental;
|
||||
|
||||
private TeaVMTestTool tool = new TeaVMTestTool();
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,13 +16,17 @@
|
|||
package org.teavm.maven;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import org.apache.maven.plugin.AbstractMojo;
|
||||
import org.apache.maven.plugin.MojoExecutionException;
|
||||
import org.apache.maven.plugin.MojoFailureException;
|
||||
|
@ -40,15 +44,37 @@ public class RunTestsMojo extends AbstractMojo {
|
|||
@Parameter(defaultValue = "${project.build.directory}/javascript-test")
|
||||
private File testDirectory;
|
||||
|
||||
@Parameter(defaultValue = "${project.build.directory}/teavm-test-report.json")
|
||||
private File reportFile;
|
||||
|
||||
@Parameter
|
||||
private URL seleniumURL;
|
||||
private String seleniumURL;
|
||||
|
||||
@Parameter
|
||||
private int numThreads = 1;
|
||||
|
||||
@Override
|
||||
public void execute() throws MojoExecutionException, MojoFailureException {
|
||||
if (seleniumURL == null || seleniumURL.isEmpty()) {
|
||||
getLog().info("Tests build skipped as selenium URL was not specified");
|
||||
return;
|
||||
}
|
||||
|
||||
if (System.getProperty("maven.test.skip", "false").equals("true") ||
|
||||
System.getProperty("skipTests") != null) {
|
||||
getLog().info("Tests build skipped as specified by system property");
|
||||
return;
|
||||
}
|
||||
|
||||
SeleniumTestRunner runner = new SeleniumTestRunner();
|
||||
runner.setLog(getLog());
|
||||
runner.setDirectory(testDirectory);
|
||||
runner.setUrl(seleniumURL);
|
||||
runner.setNumThreads(numThreads);
|
||||
try {
|
||||
runner.setUrl(new URL(seleniumURL));
|
||||
} catch (MalformedURLException e) {
|
||||
throw new MojoFailureException("Can't parse URL: " + seleniumURL, e);
|
||||
}
|
||||
|
||||
TestPlan plan;
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
|
@ -63,23 +89,31 @@ public class RunTestsMojo extends AbstractMojo {
|
|||
processReport(runner.getReport());
|
||||
}
|
||||
|
||||
private void processReport(List<TestResult> report) throws MojoExecutionException {
|
||||
if (report.isEmpty()) {
|
||||
private void processReport(TestReport report) throws MojoExecutionException, MojoFailureException {
|
||||
if (report.getResults().isEmpty()) {
|
||||
getLog().info("No tests ran");
|
||||
return;
|
||||
}
|
||||
|
||||
int failedTests = 0;
|
||||
for (TestResult result : report) {
|
||||
for (TestResult result : report.getResults()) {
|
||||
if (result.getStatus() != TestStatus.PASSED) {
|
||||
failedTests++;
|
||||
}
|
||||
}
|
||||
|
||||
if (failedTests > 0) {
|
||||
throw new MojoExecutionException(failedTests + " of " + report.size() + " test(s) failed");
|
||||
throw new MojoExecutionException(failedTests + " of " + report.getResults().size() + " test(s) failed");
|
||||
} else {
|
||||
getLog().info("All of " + report.size() + " tests successfully passed");
|
||||
getLog().info("All of " + report.getResults().size() + " tests successfully passed");
|
||||
}
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.enable(SerializationFeature.INDENT_OUTPUT);
|
||||
try (Writer writer = new OutputStreamWriter(new FileOutputStream(reportFile), "UTF-8")) {
|
||||
mapper.writeValue(writer, report);
|
||||
} catch (IOException e) {
|
||||
throw new MojoFailureException("Error writing test report", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,8 @@ import org.apache.maven.plugin.logging.Log;
|
|||
import org.openqa.selenium.JavascriptExecutor;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebDriverException;
|
||||
import org.openqa.selenium.chrome.ChromeDriver;
|
||||
import org.openqa.selenium.remote.DesiredCapabilities;
|
||||
import org.openqa.selenium.remote.RemoteWebDriver;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.tooling.testing.TestCase;
|
||||
import org.teavm.tooling.testing.TestGroup;
|
||||
|
@ -99,7 +100,7 @@ public class SeleniumTestRunner {
|
|||
latch = new CountDownLatch(numThreads);
|
||||
for (int i = 0; i < numThreads; ++i) {
|
||||
new Thread(() -> {
|
||||
ChromeDriver driver = new ChromeDriver();
|
||||
RemoteWebDriver driver = new RemoteWebDriver(DesiredCapabilities.chrome());
|
||||
webDriver.set(driver);
|
||||
localReport.set(new ArrayList<>());
|
||||
while (!seleniumStopped || !seleniumTaskQueue.isEmpty()) {
|
||||
|
@ -157,13 +158,24 @@ public class SeleniumTestRunner {
|
|||
MethodReference ref = MethodReference.parse(testCase.getTestMethod());
|
||||
switch (status) {
|
||||
case "ok":
|
||||
if (testCase.getExpectedExceptions().isEmpty()) {
|
||||
log.info("Test passed: " + testCase.getTestMethod());
|
||||
localReport.get().add(TestResult.passed(ref));
|
||||
} else {
|
||||
log.info("Test failed: " + testCase.getTestMethod());
|
||||
localReport.get().add(TestResult.exceptionNotThrown(ref));
|
||||
}
|
||||
break;
|
||||
case "exception": {
|
||||
String stack = resultObject.get("stack").asText();
|
||||
String exception = resultObject.get("exception").asText();
|
||||
if (!testCase.getExpectedExceptions().contains(exception)) {
|
||||
log.info("Test failed: " + testCase.getTestMethod());
|
||||
localReport.get().add(TestResult.error(ref, stack));
|
||||
localReport.get().add(TestResult.error(ref, exception, stack));
|
||||
} else {
|
||||
log.info("Test passed: " + testCase.getTestMethod());
|
||||
localReport.get().add(TestResult.passed(ref));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -194,7 +206,9 @@ public class SeleniumTestRunner {
|
|||
}
|
||||
}
|
||||
|
||||
public List<TestResult> getReport() {
|
||||
return new ArrayList<>(report);
|
||||
public TestReport getReport() {
|
||||
TestReport report = new TestReport();
|
||||
report.getResults().addAll(this.report);
|
||||
return report;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package org.teavm.maven;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class TestReport {
|
||||
private List<TestResult> results = new ArrayList<>();
|
||||
|
||||
public List<TestResult> getResults() {
|
||||
return results;
|
||||
}
|
||||
}
|
|
@ -15,6 +15,11 @@
|
|||
*/
|
||||
package org.teavm.maven;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
/**
|
||||
|
@ -24,30 +29,49 @@ import org.teavm.model.MethodReference;
|
|||
public class TestResult {
|
||||
private MethodReference method;
|
||||
private TestStatus status;
|
||||
private String exception;
|
||||
private String stack;
|
||||
|
||||
private TestResult(MethodReference method, TestStatus status, String stack) {
|
||||
@JsonCreator
|
||||
TestResult(
|
||||
@JsonProperty("method") MethodReference method,
|
||||
@JsonProperty("status") TestStatus status,
|
||||
@JsonInclude(Include.NON_NULL) @JsonProperty("exception") String exception,
|
||||
@JsonInclude(Include.NON_NULL) @JsonProperty("stack") String stack) {
|
||||
this.method = method;
|
||||
this.status = status;
|
||||
this.exception = exception;
|
||||
this.stack = stack;
|
||||
}
|
||||
|
||||
public static TestResult passed(MethodReference method) {
|
||||
return new TestResult(method, TestStatus.PASSED, null);
|
||||
return new TestResult(method, TestStatus.PASSED, null, null);
|
||||
}
|
||||
|
||||
public static TestResult error(MethodReference method, String stack) {
|
||||
return new TestResult(method, TestStatus.ERROR, stack);
|
||||
public static TestResult exceptionNotThrown(MethodReference method) {
|
||||
return new TestResult(method, TestStatus.EXCEPTION_NOT_THROWN, null, null);
|
||||
}
|
||||
|
||||
public static TestResult error(MethodReference method, String exception, String stack) {
|
||||
return new TestResult(method, TestStatus.ERROR, exception, stack);
|
||||
}
|
||||
|
||||
@JsonGetter
|
||||
public MethodReference getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
@JsonGetter
|
||||
public TestStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@JsonGetter
|
||||
public String getException() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
@JsonGetter
|
||||
public String getStack() {
|
||||
return stack;
|
||||
}
|
||||
|
|
|
@ -21,5 +21,6 @@ package org.teavm.maven;
|
|||
*/
|
||||
public enum TestStatus {
|
||||
PASSED,
|
||||
ERROR
|
||||
ERROR,
|
||||
EXCEPTION_NOT_THROWN
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<packaging>war</packaging>
|
||||
|
||||
<properties>
|
||||
<java.version>1.7</java.version>
|
||||
<java.version>1.8</java.version>
|
||||
<teavm.version>0.4.0-SNAPSHOT</teavm.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
@ -25,15 +25,7 @@
|
|||
<!-- JavaScriptObjects (JSO) - a JavaScript binding for TeaVM -->
|
||||
<dependency>
|
||||
<groupId>org.teavm</groupId>
|
||||
<artifactId>teavm-jso</artifactId>
|
||||
<version>${teavm.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Different browser APIs for TeaVM in terms of JSO -->
|
||||
<dependency>
|
||||
<groupId>org.teavm</groupId>
|
||||
<artifactId>teavm-dom</artifactId>
|
||||
<artifactId>teavm-jso-apis</artifactId>
|
||||
<version>${teavm.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
@ -80,9 +72,8 @@
|
|||
<executions>
|
||||
<execution>
|
||||
<id>web-client</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>build-javascript</goal>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<!-- Directory where TeaVM should put generated files. This configuration conforms to the settings
|
||||
|
@ -92,9 +83,6 @@
|
|||
<!-- Main class, containing static void main(String[]) -->
|
||||
<mainClass>${package}.Client</mainClass>
|
||||
|
||||
<!-- How to attach runtime.js. Possible values are: SEPARATE, MERGED and NONE -->
|
||||
<runtime>SEPARATE</runtime>
|
||||
|
||||
<!-- Whether TeaVM should produce minified JavaScript. Can reduce JavaScript file size more than
|
||||
two times -->
|
||||
<minifying>true</minifying>
|
||||
|
|
Loading…
Reference in New Issue
Block a user