/*
* Copyright (C) 2013 salesforce.com, inc.
*
* 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 org.auraframework.test;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.message.BasicNameValuePair;
import org.auraframework.Aura;
import org.auraframework.def.DefDescriptor;
import org.auraframework.def.DefDescriptor.DefType;
import org.auraframework.def.DescriptorFilter;
import org.auraframework.def.TestCaseDef;
import org.auraframework.def.TestSuiteDef;
import org.auraframework.service.ContextService;
import org.auraframework.service.DefinitionService;
import org.auraframework.system.AuraContext.Authentication;
import org.auraframework.system.AuraContext.Format;
import org.auraframework.system.AuraContext.Mode;
import org.auraframework.test.WebDriverUtil.BrowserType;
import org.auraframework.test.annotation.UnAdaptableTest;
import org.auraframework.test.annotation.WebDriverTest;
import org.auraframework.throwable.quickfix.QuickFixException;
import org.auraframework.util.AuraTextUtil;
import org.auraframework.util.json.JsonReader;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
/**
* TODO(W-1386863): investigate why/fix the thread hostile nature of these tests.
*/
@UnAdaptableTest
@WebDriverTest
public class ComponentJSTestSuiteTest extends TestSuite {
public static TestSuite suite() throws Exception {
TestSuite suite = new NamespaceTestSuite("*");
suite.setName("JS component tests");
return suite;
}
private static class FailTestCase extends WebDriverTestCase {
private final Throwable cause;
private FailTestCase(DefDescriptor<TestSuiteDef> descriptor, Throwable cause) {
super(descriptor.getQualifiedName());
this.cause = cause;
}
@Override
public void runTest() throws Throwable {
throw cause;
}
}
@UnAdaptableTest
public static class NamespaceTestSuite extends TestSuite {
public NamespaceTestSuite(String namespace) throws Exception {
super(namespace);
ContextService contextService = Aura.getContextService();
DefinitionService definitionService = Aura.getDefinitionService();
boolean contextStarted = false;
if (!contextService.isEstablished()) {
contextStarted = true;
contextService.startContext(Mode.JSTEST, Format.JSON, Authentication.AUTHENTICATED);
}
Map<String, TestSuite> subSuites = new HashMap<>();
try {
DescriptorFilter filter = new DescriptorFilter("js://"+namespace, DefType.TESTSUITE.toString());
Set<DefDescriptor<?>> descriptors = definitionService.find(filter);
for (DefDescriptor<?> qd : descriptors) {
@SuppressWarnings("unchecked")
DefDescriptor<TestSuiteDef> descriptor = (DefDescriptor<TestSuiteDef>)qd;
Test test;
try {
test = new ComponentTestSuite(descriptor.getDef());
} catch (Throwable t) {
test = new FailTestCase(descriptor, t);
}
String testNamespace = descriptor.getNamespace();
if (namespace.equals(testNamespace)) {
addTest(test);
} else {
TestSuite subSuite = subSuites.get(testNamespace);
if (subSuite == null) {
subSuite = new TestSuite(testNamespace);
subSuites.put(testNamespace, subSuite);
addTest(subSuite);
}
subSuite.addTest(test);
}
}
} catch (Throwable t) {
System.err.println("Failed to load component tests for namespace: " + namespace);
t.printStackTrace();
} finally {
if (contextStarted) {
contextService.endContext();
}
}
}
}
private static class ComponentTestSuite extends TestSuite {
private final DefDescriptor<TestSuiteDef> descriptor;
private ComponentTestSuite(TestSuiteDef suiteDef) {
super(String.format("%s:%s", suiteDef.getDescriptor().getNamespace(), suiteDef.getDescriptor().getName()));
this.descriptor = suiteDef.getDescriptor();
for (TestCaseDef caseDef : suiteDef.getTestCaseDefs()) {
addTest(new ComponentTestCase(this, caseDef));
}
}
public String getUrl(DefType defType) {
return getUrl(defType, Mode.AUTOJSTEST);
}
public String getUrl(DefType defType, Mode mode) {
String ext = ".cmp";
if (defType == DefType.APPLICATION) {
ext = ".app";
}
return String.format("/%s/%s%s?aura.mode=%s&aura.testReset=true", descriptor.getNamespace(),
descriptor.getName(), ext, mode);
}
/**
* @return Returns the code.
* @throws QuickFixException
*/
public String getCode() throws QuickFixException {
ContextService contextService = Aura.getContextService();
boolean isEstablished = contextService.isEstablished();
if (!isEstablished) {
contextService.startContext(Mode.AUTOJSTEST, Format.JSON, Authentication.AUTHENTICATED);
}
try {
return descriptor.getDef().getCode();
} finally {
if (!isEstablished) {
contextService.endContext();
}
}
}
}
public static class ComponentTestCase extends WebDriverTestCase {
private ComponentTestCase(ComponentTestSuite suite, TestCaseDef caseDef) {
super("testRun");
this.name = String.format("%s$%s", suite.descriptor.getQualifiedName().replaceAll("://", "_"),
caseDef.getName());
this.suite = suite;
this.caseDef = caseDef;
for (String browser : caseDef.getBrowsers()) {
String token = browser.trim().toUpperCase();
Set<BrowserType> set;
if (token.charAt(0) == '-') {
token = token.substring(1);
set = excludedBrowsers;
} else {
set = targetBrowsers;
}
try {
set.add(BrowserType.valueOf(token));
} catch (IllegalArgumentException e) {
fail("Unknown BrowserType: " + browser);
}
}
}
private String getUrl() {
DefType defType = caseDef.getDefType();
String baseUrl = suite.getUrl(defType);
Set<Entry<String, Object>> attributes = caseDef.getAttributeValues().entrySet();
if (attributes.isEmpty()) {
return baseUrl;
} else {
String hash = "";
List<NameValuePair> newParams = Lists.newArrayList();
for (Entry<String, Object> entry : attributes) {
String key = entry.getKey();
String value = entry.getValue().toString();
if (key.equals("__layout")) {
hash = value;
} else {
newParams.add(new BasicNameValuePair(key, value));
}
}
return baseUrl + "&" + URLEncodedUtils.format(newParams, "UTF-8") + hash;
}
}
public String getTestUrlForManualRun() {
return suite.getUrl(caseDef.getDefType(), Mode.JSTEST) + "&test=" + caseDef.getName();
}
@Override
public String getName() {
return name;
}
@Override
public String getQualifiedName() {
String btype = getBrowserTypeString();
return caseDef.getDescriptor().getQualifiedName() + btype;
}
public void testRun() throws Throwable {
addMocksToTestContextLocalDef(caseDef.getLocalDefs());
open(getUrl(), Mode.AUTOJSTEST);
String ret = (String) auraUITestingUtil.getEval(String.format(
"return window.aura.test.run('%s', '%s', 30)",
AuraTextUtil.escapeForJavascriptString(caseDef.getName()),
AuraTextUtil.escapeForJavascriptString(suite.getCode())));
if (ret != null && !"null".equals(ret)) {
@SuppressWarnings("unchecked")
Map<String, Object> e = (Map<String, Object>) new JsonReader()
.read(ret);
fail((String) e.get("message"));
}
// Actions run on servers need special handling because their call
// back methods are called asynchronously.
// This check is to make sure all such calls were complete
waitForCondition("return window.aura.test.isComplete()", 30);
}
@Override
public Set<BrowserType> getTargetBrowsers() {
return targetBrowsers;
}
@Override
public Set<BrowserType> getExcludedBrowsers() {
return excludedBrowsers;
}
@Override
protected Set<String> getAuraErrorsExpectedDuringInit() {
return caseDef.getAuraErrorsExpectedDuringInit();
}
@Override
public Set<String> getTestLabels() {
return Sets.newHashSet(caseDef.getTestLabels());
}
private final ComponentTestSuite suite;
private final TestCaseDef caseDef;
private final String name;
private final Set<BrowserType> targetBrowsers = EnumSet.noneOf(BrowserType.class);
private final Set<BrowserType> excludedBrowsers = EnumSet.noneOf(BrowserType.class);
}
}