alsatian-test/alsatian

View on GitHub
packages/alsatian/core/test-loader.ts

Summary

Maintainability
A
3 hrs
Test Coverage
import { FileRequirer, TestFixture } from "./";
import { ITest, ITestCase, ITestFixture } from "./_interfaces";
import { METADATA_KEYS } from "./alsatian-core";
import { deprecate } from "./maintenance/deprecate";

export class TestLoader {
    public constructor(private fileRequirer: FileRequirer) {}

    public loadTestFixture(filePath: string): Array<ITestFixture> {
        try {
            const testFixtureModule = this.fileRequirer.require(filePath);
            const testFixtures: Array<ITestFixture> = [];

            const loadFixture = (constructor: any, description: string) => {
                const testFixture = this.loadTestFixtureInstance(
                    constructor,
                    description,
                    filePath
                );
                if (testFixture !== null) {
                    testFixtures.push(testFixture);
                }
            };

            if (typeof testFixtureModule === "function") {
                // if the default export is class constructor
                loadFixture(testFixtureModule, testFixtureModule.name);
            } else {
                // otherwise there are multiple exports and we must handle all of them
                Object.keys(testFixtureModule)
                    .filter(key => typeof testFixtureModule[key] === "function")
                    .forEach(key => loadFixture(testFixtureModule[key], key));
            }

            return testFixtures;
        } catch (e) {
            process.stderr.write(`ERROR LOADING FILE: ${filePath}\n${e.stack}`);
            process.exit(1);
        }
    }

    private loadTestFixtureInstance(
        testFixtureConstructor: new () => object,
        defaultFixtureDescription: string,
        filePath: string
    ): ITestFixture | null {
        // get test fixture metadata or create new metadata
        // to support not requiring the TestFixture decorator.
        // TODO: This functionality will be removed in 5.0.0 where
        // TestFixture decorator will become mandatory
        const testFixture =
            Reflect.getMetadata(
                METADATA_KEYS.TEST_FIXTURE,
                testFixtureConstructor
            ) || new TestFixture(defaultFixtureDescription);

        testFixture.ignored = false;

        testFixture.filePath = filePath;

        if (Reflect.getMetadata(METADATA_KEYS.IGNORE, testFixtureConstructor)) {
            // fixture should be ignored
            testFixture.ignored = true;

            testFixture.ignoreReason = Reflect.getMetadata(
                METADATA_KEYS.IGNORE_REASON,
                testFixtureConstructor
            );
        }

        // create an instance of the test fixture
        testFixture.fixture = new testFixtureConstructor();

        // find all the tests on this test fixture
        const tests = Reflect.getMetadata(
            METADATA_KEYS.TESTS,
            testFixture.fixture
        );

        testFixture.focussed = false;

        if (Reflect.getMetadata(METADATA_KEYS.FOCUS, testFixtureConstructor)) {
            testFixture.focussed = true;
        }

        if (tests === undefined) {
            // no tests on the fixture
            return null;
        }

        if (!Reflect.getMetadata(METADATA_KEYS.TEST_FIXTURE, testFixtureConstructor)) {
            deprecate(
                "Writing tests without the TestFixture decorator",
                "5.0.0",
                "Please add a TestFixture decorator to all classes containing tests."
            );
        }

        tests.forEach((test: ITest) => {
            // the test is ignored if the fixture is, or if it's specifically ignored
            test.ignored = false;
            if (
                testFixture.ignored ||
                Reflect.getMetadata(
                    METADATA_KEYS.IGNORE,
                    testFixture.fixture,
                    test.key
                )
            ) {
                test.ignored = true;

                // individual test ignore reasons take precedence over test fixture ignore reasons
                test.ignoreReason =
                    Reflect.getMetadata(
                        METADATA_KEYS.IGNORE_REASON,
                        testFixture.fixture,
                        test.key
                    ) || testFixture.ignoreReason;
            }

            test.focussed = false;

            if (
                Reflect.getMetadata(
                    METADATA_KEYS.FOCUS,
                    testFixture.fixture,
                    test.key
                )
            ) {
                test.focussed = true;
            }

            test.timeout =
                Reflect.getMetadata(
                    METADATA_KEYS.TIMEOUT,
                    testFixture.fixture,
                    test.key
                ) || null;

            testFixture.addTest(test);

            if (!test.description) {
                test.description = test.key;
            }

            const testCases = Reflect.getMetadata(
                METADATA_KEYS.TEST_CASES,
                testFixture.fixture,
                test.key
            );
            test.testCases = [];

            if (!testCases) {
                test.testCases.push({ caseArguments: [] });
            } else {
                testCases.forEach((testCase: ITestCase) => {
                    test.testCases.push(testCase);
                });
            }
        });

        return testFixture;
    }
}