Testing

Tests are written using Cmobilecom AF testing API which is platform independent testing automation API to simulate user interaction with applications. See javadoc for API references.

Directory Structure

Add test under application root directory.
application-root-dir/
                    web/
                        settings.gradle
                        build.gradle
                    test/
                        src/test/java/mypackage/MyAppTest.java
                        build.gradle                  

Build

Add test project to application multi-project build.

settings.gradle under root project directory (e.g. web):

include 'test'
project(':test').projectDir = new File('../test')
test project build.gradle
apply from : "${rootProject.cmobilecomAFBuildCommonDir}/test.gradle"

test {
	// webdriver executable directory
	systemProperty "webdriverDir", "/path/webdriver"
}

Run tests:
gradlew :test:test [options]
options:
-PkeepPageLoader
	Keep page loader(browser for web) after each test.
	
-Plocales
	Limit tests to specified locales(separated by comma). e.g., en,zh_CN
	
-PdriverTypes
	Limit tests to specified drivers(separated by comma). e.g., chrome,firefox

-PwaitTime=[milliseconds]
	Wait time before partial behaviors start to be handled. default is 200 milliseconds.
For example,
gradlew :test:test -PkeepPageLoader -Plocales=en -PdriverTypes=chrome

TestConfig

TestConfig is a parameter for JUnit parameterized testing, which configures testing for different combinations of locales and underlying driver types. See command line options to control the list of testConfigs.

Driver types and corresponding driver executable names on windows:

driver types     executable names
-----------------------------------------------------------------
chrome           chromedriver.exe
firefox          geckodriver.exe
edge             edgedriver.exe
ie               iedriver.exe 
sarafi           safaridriver.exe
opera            operadriver.exe  
Driver executable directory is specified in build.gradle as a system property.

To override or extend default testConfigs, initialize static member variable testConfigs.


public class MyAppTest extends TestBase {

	static {
		testConfigs = new ArrayList<>();
	
		// or extend default testConfigs
		testConfigs = TestBase.testConfigs();
		...
	}

}

Application Tests

Run module tests from application tests. For example,

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import com.cmobilecom.af.test.api.ContainerBean;
import com.cmobilecom.af.test.api.ManageCenter;
import com.cmobilecom.af.test.api.TestBase;
import com.cmobilecom.af.test.api.TestConfig;

@RunWith(Parameterized.class)
public class MyAppTest extends TestBase {
	
	public MyAppTest(TestConfig testConfig) {
		super(testConfig);
	}
	
	@Test
	public void test() {
		ContainerBean containerBean = this.pageResource.getPage("http", "localhost", "80", "manage");
		ManageCenter manageCenter = ManageCenter.loginToManageCenter(containerBean, "system", "123456");

		// run module tests, e.g., ModuleFoo
		ModuleFooTest moduleATest = new ModuleFooTest(manageCenter);
		moduleFooTest.run();
	}
}

Module Tests

Add test sourceSet directory under module root directory:
src/main
    test/
        java
        resources/bundle/messages.properties
                         messages_zh.properties
The module test jars will be built and added to the compile and runtime classpath of application tests.

If one module test(say Module "A") has dependency on another module test(say Module "B"), add the following dependencies in the build.gradle of module A.

dependencies {

	testImplementation project(path: ':module-b', configuration: 'moduleTestJar')
}
Module main jar will be added to the runtime classpath of applicagtion tests, which include module resource bundles.

Resouce Bundles

To find menu nodes by visible text, select options by visiblt text or verify visible text value on UI, resource bundles will be loaded for current locale.

Base name for resource bundle in module main jar:

[moduleName].bundle.messages
Base name for resource bundle in module test jar:
[moduleName].test.bundle.messages
Default base name for application(module assembly) resource bundle:
[rootProject.softwareName].bundle.messages

Example 1

Example code is excerpted from the tests of AF examples.
  1. Login into manage center and create an Instance.
  2. Login into the new instance.
  3. Set locale and time zone, and create Business Parameters.

package com.cmobilecom.af.example.test;

import java.util.Locale;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import com.cmobilecom.af.test.api.ContainerBean;
import com.cmobilecom.af.test.api.EntityBean;
import com.cmobilecom.af.test.api.EntityProperty;
import com.cmobilecom.af.test.api.ManageCenter;
import com.cmobilecom.af.test.api.MenuBean;
import com.cmobilecom.af.test.api.MenuNode;
import com.cmobilecom.af.test.api.Region;
import com.cmobilecom.af.test.api.TestBase;
import com.cmobilecom.af.test.api.TestConfig;

@RunWith(Parameterized.class)
public class ExamplesTest extends TestBase {

	public ExamplesTest(TestConfig testConfig) {
		super(testConfig);
	}

	@Test
	public void test() {
		// load page for manage center
		ContainerBean containerBean = this.pageResource.getPage("http", "localhost", "8088",
				true, "manage");
		// login into manage center
		ManageCenter manageCenter = ManageCenter.loginToManageCenter(containerBean, "system", "123456");
		// create a new instance, and then login into the instance
		ManageCenter manageCenterForNewInstance = (ManageCenter) manageCenter.createInstance("C.AFEX.SS", true);

		// set locale and time zone in system module
		// testConfig.locale is for messages, parameters.locale is for number, date and currency
		Locale locale = Locale.US;
		setLocaleAndTimeZone(manageCenterForNewInstance, locale, "America/New_York");
		createBusinessParams(manageCenterForNewInstance,"USD", "1/1/19");

		// run module tests
		ExampleHRTest exampleHRTest = new ExampleHRTest(manageCenterForNewInstance);
		exampleHRTest.run();
	}

	/**
	 * Set locale and time zone.
	 *
	 * @param locale locale
	 * @param timeZoneId time zone id
	 */
	private void setLocaleAndTimeZone(ManageCenter manageCenter, Locale locale, String timeZoneId) {
		// default content region
		Region defaultContentRegion = manageCenter.getDefaultContentRegion(null);

		// click menu node: System > Setup > Parameters
		MenuBean containerMenuBean = manageCenter.getContainerMenuBean();
		MenuNode parametersMenuNode = containerMenuBean.getMenuNode("System", "Setup", "Parameters");
		parametersMenuNode.click();

		// wait region partial update to complete
		EntityBean parametersBean = (EntityBean) defaultContentRegion.waitUntilStale(true).getBean(0);
		// edit
		parametersBean.edit();

		// set property values
		EntityProperty localeEP = parametersBean.getEntityProperty("locale");
		localeEP.waitUntilEditable(true).selectOption(locale.toString(), true, false);
		parametersBean.getEntityProperty("timeZone").selectOption(timeZoneId, true, false);

		// apply changes to persistence
		parametersBean.applyChange();

		// cache the locale in manage center, which can be used by module tests
		manageCenter.setEffectiveLocale(locale);
	}

	/**
	 * Create Business Parameters.
	 *
	 * @param yearStartDate date format in the locale set in Parameters
	 */
	private void createBusinessParams(ManageCenter manageCenter, String currencyCode, String yearStartDate) {
		// default content region
		Region defaultContentRegion = manageCenter.getDefaultContentRegion(null);

		// click menu node: System > Setup > BusinessParams
		MenuBean containerMenuBean = manageCenter.getContainerMenuBean();
		MenuNode createUomGroupMenuNode = containerMenuBean.getMenuNode("System", "Setup", "BusinessParams");
		createUomGroupMenuNode.click();

		// wait region partial update to complete
		EntityBean businessParamsBean = (EntityBean) defaultContentRegion.waitUntilStale(true).getBean(0);

		// set property values
		EntityProperty currencyEP = businessParamsBean.getEntityProperty("currencyCode");
		currencyEP.selectOption(currencyCode, true, false);
		businessParamsBean.getEntityProperty("yearStartDate").setValue(yearStartDate);

		// create entity in persistence
		businessParamsBean.create();

		// cache the currency code in manage center, which can be used by module tests
		manageCenter.setCurrencyCode(currencyCode);
	}
}

Example 2

Example code is excerpted from the tests of AF examples module ExampleHR.
  1. Create expense claim entity.
  2. Access enclosed form bean (expense claim item list bean).
  3. Access entity list properties.
  4. Verify statistics property values.

public class ExampleHRText {

	private ManageCenter manageCenter;

	public ExampleHRTest(ManageCenter manageCenter) {
		this.manageCenter = manageCenter;
	}

	/**
	 * Create expense claim.
	 *
	 * @param createIdRule  whether to create IdRule
	 * @param importFormDesigns whether to import form designs
	 * @return the nid of the created expense claim
	 */
	private String createExpenseClaim(boolean createIdRule, boolean importFormDesigns) {
		ResourceBundle bundle = manageCenter.getContainerBean().getResourceBundle();
		Region defaultContentRegion = this.manageCenter.getDefaultContentRegion(null);

		// click menu node: ExampleHR > ExpenseClaims > Create
		MenuBean containerMenuBean = manageCenter.getContainerMenuBean();
		MenuNode createExpenseClaimMenuNode = containerMenuBean.getMenuNode("ExampleHR", "ExpenseClaims", "Create");
		createExpenseClaimMenuNode.click();

		ContainerBean containerBean = manageCenter.getContainerBean();

		// create IdRule if it has not been created
		if (createIdRule) {
			// dialog is opened to create IdRule
			containerBean.waitUntilDialogOpen();

			DialogBean dialogBean = containerBean.getDialogBean();
			createIdRule(dialogBean, "ExpenseClaim", 10, 100);
			dialogBean.close();
		}

		// import form designs if they have not been imported
		if (importFormDesigns) {
			// dialog is opened to import form designs
			containerBean.waitUntilDialogOpen();

			DialogBean dialogBean = containerBean.getDialogBean();
			manageCenter.importEntities(dialogBean, 2, true);
			dialogBean.close();
		}

		// wait region partial update to complete (showing ExpenseClaimBean)
		EntityBean expenseClaimBean = (EntityBean) defaultContentRegion.waitUntilStale(true).getBean(0);

		// select formDesign 1002
		expenseClaimBean = expenseClaimBean.selectFormDesign("1002");
		expenseClaimBean.getEntityProperty("employee").autoComplete("001", 0);
		expenseClaimBean.getEntityProperty("summary").setValue(bundle.getString("BusinessTrip"));

		// get enclosed form bean
		EntityListBean expenseClaimItemsBean = (EntityListBean) expenseClaimBean.getFormBean("expenseClaimItems");

		Locale effectiveLocale = this.manageCenter.getEffectiveLocale();
		LocalDate currentDate = LocalDate.now();
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern("M/d/yyyy", effectiveLocale);
		String currentDateStr = formatter.format(currentDate);

		// Row 1
		expenseClaimItemsBean.getEntityProperty(0,"date").setValue(currentDateStr);
		expenseClaimItemsBean.getEntityProperty(0,"code").setValue("001");
		expenseClaimItemsBean.getEntityProperty(0,"description").setValue(bundle.getString("Hotel"));
		expenseClaimItemsBean.getEntityProperty(0,"expense").setValue("200\t");

		EntityProperty totalExpenseEP = expenseClaimItemsBean.getStatisticsEntityProperty("expense");
		totalExpenseEP.waitUntilValueToBe("$200.00",true);

		// add a new row
		expenseClaimItemsBean.addRows();

		// Row 2
		expenseClaimItemsBean.waitUntilPropertyVisible(1,"date").setValue(currentDateStr);
		expenseClaimItemsBean.getEntityProperty(1,"code").setValue("002");
		expenseClaimItemsBean.getEntityProperty(1,"description").setValue(bundle.getString("Transportation"));
		expenseClaimItemsBean.getEntityProperty(1,"expense").setValue("600\t");

		totalExpenseEP.waitUntilValueToBe("$800.00",true);

		// create entity in persistence
		expenseClaimBean.create();

		// return the nid of the created expense claim
		return expenseClaimBean.getEntityProperty("nid").getValue();
	}
	
	public void run() {
		...
		
		createExpenseClaim(true, true);
	}
}

Dialog


ContainerBean containerBean = manageCenter.getContainerBean();
// wait dialog to open
containerBean.waitUntilDialogOpen();

// get the opened dialog
DialogBean dialogBean = containerBean.getDialogBean();
// access content in dialog, e.g. create a User
EntityBean userBean = (EntityBean) dialogBean.getDefaultContentBean();
// set User entity property values
...
userBean.create();

// close dialog		
dialogBean.close();
DialogBean is a ContainerBean, so a child dialog can be opened from its parent DialogBean.

Upload Files

EntityProperty property = employeeBean.getEntityProperty("photos");

// Upload a list of files.
property.uploadFiles(List<String> files);
	
// Upload files under the directory.
property.uploadFiles(File dir, FilenameFilter filter);

Assertions