gpii-universal
Version:
Cross platform, core components of the GPII personalization infrastructure.
214 lines (180 loc) • 9.09 kB
JavaScript
/*
A collection of functions to expand "test defs" and run them as tests. Test definitions are used throughout this
package to reuse test steps between different environments, for example, running the same tests against a local
development environment or the cloud. See the documentation for `gpii.test.runTestDefs` for more details on
supported options.
Copyright 2019 Raising the Floor International
Licensed under the New BSD license. You may not use this file except in
compliance with this License.
You may obtain a copy of the License at
https://github.com/gpii/universal/LICENSE.txt
*/
;
var fluid = require("infusion");
var gpii = fluid.registerNamespace("gpii");
var kettle = require("kettle");
var path = require("path");
/**
*
* A function that wraps test definitions in a test environment, case holder, and runs them. Uses
* sequence elements instead of manipulating arrays.
*
* The test definitions handled by this function support the following elements:
*
* 1. {String} `name` : The name of this block of tests, which will be reported in test output.
* 2. {Integer} `expect`: The number of tests to expect to be run.
* 3. {Array} `sequence`: The Fluid IoC test sequences to be run.
* 4. {Object} `config`: The configuration options, generally for the server used in these tests.
* 5. {String} `configType`: A namespaced "type" for the configuration, typically based on parsing of configuration
* files using Kettle.
*
* @param {Object} testDefs - A map of test definitions (see above).
* @param {Function} testDefExpander - A function that will be used to expand the test definition.
*
*/
gpii.test.runTestDefs = function (testDefs, testDefExpander) {
var testEnvironments = fluid.transform(testDefs, testDefExpander);
fluid.test.runTests(testEnvironments);
};
gpii.test.runServerTestDefs = function (testDefs) {
gpii.test.runTestDefs(testDefs, gpii.test.testDefToServerEnvironment);
};
gpii.test.runCouchTestDefs = function (testDefs) {
gpii.test.runTestDefs(testDefs, gpii.test.testDefToCouchEnvironment);
};
gpii.test.runSnapshotTestDefs = function (testDefs) {
gpii.test.runTestDefs(testDefs, gpii.test.testDefToSnapshotEnvironment);
};
gpii.test.runSnapshotWithInitialStateTestDefs = function (testDefs) {
gpii.test.runTestDefs(testDefs, gpii.test.testDefToSnapshotWithInitialStateEnvironment);
};
gpii.test.testDefToEnvironment = function (testDef, environmentType, sequenceGrade) {
var configurationName = testDef.configType || kettle.config.createDefaults(testDef.config);
//var caseHolderOptions = fluid.filterKeys(testDef, ["modules", "sequence", "config"], true);
var caseHolderOptions = fluid.filterKeys(testDef, ["modules", "sequence"], true);
caseHolderOptions.modules = [{
name: configurationName + " tests",
tests: [{
name: testDef.name,
expect: testDef.expect,
sequence: testDef.sequence,
sequenceGrade: testDef.sequenceGrade || sequenceGrade
}]
}];
return {
type: environmentType,
options: {
configurationName: configurationName,
components: {
tests: {
options: caseHolderOptions
}
}
}
};
};
gpii.test.testDefToServerEnvironment = function (testDef) {
return gpii.test.testDefToEnvironment(testDef, "gpii.test.serverEnvironment", "gpii.test.standardServerSequenceGrade");
};
gpii.test.testDefToCouchEnvironment = function (testDef) {
return gpii.test.testDefToEnvironment(testDef, "gpii.test.couchEnvironment", "gpii.test.couchSequenceGrade");
};
gpii.test.testDefToSnapshotEnvironment = function (testDef) {
return gpii.test.testDefToEnvironment(testDef, "gpii.test.couchEnvironment", "gpii.test.snapshotSequenceGrade");
};
gpii.test.testDefToSnapshotWithInitialStateEnvironment = function (testDef) {
return gpii.test.testDefToEnvironment(testDef, "gpii.test.couchEnvironment", "gpii.test.initialStateSequenceGrade");
};
/* Build a test fixture for integration/acceptance tests operating the stereotypical workflow -
* snapshot, login, expectConfigured, logout and expectRestored
*/
gpii.test.buildSingleTestFixture = function (testDef, rootGrades) {
testDef.gradeNames = fluid.makeArray(testDef.gradeNames).concat(fluid.makeArray(rootGrades));
testDef.sequenceGrade = testDef.initialState ? "gpii.test.initialStateSequenceGrade" : "gpii.test.snapshotSequenceGrade";
return testDef;
};
/** Build a fixture set suitable for sending to kettle.test.bootstrapServer that includes a non-flat
* array of sequences within the member `sequenceSegments` as well as a base record to be merged into each testDef
* @param {Array} fixtures - An array of proto-testDefs, each members `name` and `expect` with the same semantic
* as a standard testDef, and a member `sequenceSegments` whose elements may themselves be arrays of sequences.
* @param {Object} baseTestDef - A base test def record, holding members `configName`, `configPath` and other
* members appropriate for this options structure.
* @return {Object} - The "segmented" fixtures generated from the base fixtures and test def.
*/
gpii.test.buildSegmentedFixtures = function (fixtures, baseTestDef) {
return fluid.transform(fixtures, function (fixture) {
var overlay = {
name: fixture.name,
expect: fixture.expect,
sequence: fluid.flatten(fixture.sequenceSegments)
};
return fluid.extend(true, {}, baseTestDef, overlay);
});
};
// Convert a record as returned by a "portable test" into a full testDefs structure as accepted by gpii.test.buildTests
// TODO better docs and explanation
gpii.test.recordToTestDefs = function (record) {
var testDefs = fluid.copy(fluid.getGlobalValue(record.testDefs));
fluid.each(testDefs, function (testDef) {
testDef.config = {
configName: testDef.configName || record.configName,
configPath: testDef.configPath || record.configPath
};
});
return testDefs;
};
gpii.test.runTests = function (record, rootGrades, runFn) {
runFn = runFn || gpii.test.runCouchTestDefs;
var testDefs = gpii.test.recordToTestDefs(record);
var testDefs2 = gpii.test.buildTests(testDefs, rootGrades);
return runFn(testDefs2);
};
// Run from the base of every platform-specific acceptance/integration test fixture file as its per-file bootstrap.
// The long argument list is required in order to detect whether the file is being run as a top-level
// node executable - if it is, we default to running it as an integration test - otherwise, we just
// return its record.
gpii.test.bootstrap = function (record, rootGrades, foreignModule, foreignRequire, runFn) {
if (foreignRequire.main === foreignModule) { // the file was executed directly from the command line - run integration tests immediately
return gpii.test.runTests(record, rootGrades, runFn);
} else { // otherwise just return its record for processing
return record;
}
};
gpii.test.buildTests = function (testDefs, rootGrades) {
return fluid.transform(fluid.copy(testDefs), function (testDef) {
return gpii.test.buildSingleTestFixture(testDef, rootGrades);
});
};
/** Runs a suite of integration or acceptance tests as indexed by a platform-specific root
* such as "index-windows.js".
* @param {Array} files - The array of test fixture files, as returned by the index module.
* @param {String} baseDir - The base directory holding the index module.
* @param {Array} rootGrades - An array of grade names to be contributed to the component root
* (currently testCaseHolder) of the fixtures. This controls the variety of test which is executed -
* e.g. a mocked Windows environment integration test, or a full acceptance test (the default).
*/
gpii.test.runSuite = function (files, baseDir, rootGrades) {
fluid.log("Running test suites:\n", JSON.stringify(files, null, 2));
fluid.each(files, function (oneFile) {
var filePath = path.resolve(baseDir, "platform", oneFile);
var record = require(filePath);
gpii.test.runTests(record, rootGrades);
});
};
// simple utility to select a set of test suites to run based on presence of substrings in their names
// these are ANDed together - i.e. they must each appear in a selected suite's name
gpii.test.filterSuites = function (files, specs) {
fluid.each(specs, function (spec) {
files = fluid.remove_if(fluid.copy(files), function (file) {
return file.indexOf(spec) === -1;
});
});
return files;
};
gpii.test.filterSuitesByArgs = function (files, args) {
return args.length === 2 ? files : gpii.test.filterSuites(files, args.slice(2));
};
gpii.test.runSuitesWithFiltering = function (files, baseDir, rootGrades) {
var filtered = gpii.test.filterSuitesByArgs(files, process.argv);
gpii.test.runSuite(filtered, baseDir, rootGrades);
};