<commit_before>/*
 * Copyright (c) 2015 Juniper Networks, Inc. All rights reserved.
 */
define([
    'co-test-runner',
    'ct-test-utils',
    'ct-test-messages',
    'monitor/networking/test/ui/views/InstanceView.mock.data',
    'co-tabs-view-test-suite',
    'co-details-view-test-suite',
    'co-grid-contrail-list-model-test-suite',
    'co-grid-view-test-suite',
    'co-chart-view-line-bar-test-suite',
    'co-chart-view-line-test-suite',
], function (cotr, cttu, cttm, TestMockdata, TabsViewTestSuite, DetailsViewTestSuite,
             GridListModelTestSuite, GridViewTestSuite, LineBarChartViewTestSuite, LineWithFocusChartViewTestSuite) {

    var moduleId = cttm.INSTANCE_VIEW_COMMON_TEST_MODULE;

    var testType = cotc.VIEW_TEST;

    var fakeServerConfig = cotr.getDefaultFakeServerConfig();

    var fakeServerResponsesConfig = function() {
        /**
         URIs
         /api/tenants/config/domains [done]
         /api/tenants/config/projects [done
         /api/tenants/projects/default-domain [done]
         /api/tenants/networks/default-domain:demo [done]
         /api/tenant/networking/virtual-machine [done]
         /api/tenant/monitoring/instance-connected-graph [done]
         /api/tenant/networking/virtual-machine-interfaces/summary [done]
         /api/tenant/networking/flow-series/vm [done]
         /api/tenant/networking/network/stats/top [done]
         /api/tenant/networking/stats [done]
         */

        var responses = [];
        /**
         *  /api/tenants/config/domains
         */
        responses.push(cotr.createFakeServerResponse( {
            url: cttu.getRegExForUrl(ctwc.URL_ALL_DOMAINS),
            body: JSON.stringify(TestMockdata.domainsMockData)
        }));
        responses.push(cotr.createFakeServerResponse( {
            url: cttu.getRegExForUrl(ctwc.URL_ALL_PROJECTS),
            body: JSON.stringify(TestMockdata.projectsMockData)
        }));
        responses.push(cotr.createFakeServerResponse( {
            url: /\/api\/tenants\/projects\/default-domain.*$/,
            body: JSON.stringify(TestMockdata.projectsMockData)
        }));
        responses.push(cotr.createFakeServerResponse( {
            url: /\/api\/tenants\/networks\/default\-domain\:demo.*$/,
            body: JSON.stringify(TestMockdata.demoProjectMockData)
        }));
        responses.push(cotr.createFakeServerResponse({
            url: /\/api\/tenant\/networking\/virtual\-machines\/details.*$/,
            body: JSON.stringify(TestMockdata.virtualMachineMockData)
        }));
        responses.push(cotr.createFakeServerResponse({
            url: /\/api\/tenant\/networking\/virtual\-machine.*$/,
            body: JSON.stringify(TestMockdata.virtualMachineStatsMockData)
        }));
        responses.push(cotr.createFakeServerResponse({
            url: /\/api\/tenant\/monitoring\/instance\-connected\-graph.*$/,
            body: JSON.stringify(TestMockdata.virtualMachineConnectedGraphMockData)
        }));
        responses.push(cotr.createFakeServerResponse({
            method: "POST",
            url: cttu.getRegExForUrl(ctwc.URL_QUERY),
            body: JSON.stringify(TestMockdata.reportsQueryMockData)
        }));
        responses.push(cotr.createFakeServerResponse({
            url: /\/api\/tenant\/networking\/flow\-series\/vm.*$/,
            body: JSON.stringify(TestMockdata.virtualMachineFlowSeriesMockData)
        }));
        responses.push(cotr.createFakeServerResponse({
            method: "POST",
            url: cttu.getRegExForUrl(ctwc.URL_VM_INTERFACES),
            body: JSON.stringify(TestMockdata.virtualMachineInterfacesMockData)
        }));
        responses.push(cotr.createFakeServerResponse({
            url: /\/api\/tenant\/networking\/network\/stats\/top.*$/,
            body: JSON.stringify(TestMockdata.networkingStatsTopMockData)
        }));
        responses.push(cotr.createFakeServerResponse({
            method: "POST",
            url: cttu.getRegExForUrl(ctwc.URL_VM_VN_STATS),
            body: JSON.stringify(TestMockdata.networkingStatsMockData)
        }));
        return responses;
    };
    fakeServerConfig.getResponsesConfig = fakeServerResponsesConfig;

    var pageConfig = cotr.getDefaultPageConfig();
    pageConfig.hashParams = {
        p: 'mon_networking_instances',
        q: {
            view: 'details',
            type: 'instance',
            "focusedElement": {
                "fqName": "default-domain:demo:st_vn101",
                "type": "virtual-machine",
                "uuid": "0275be58-4e5f-440e-81fa-07aac3fb1623",
                "vmName": "st_vn101_vm21"
            }
        }
    };
    pageConfig.loadTimeout = cotc.PAGE_LOAD_TIMEOUT * 5;

    var getTestConfig = function() {
        return {
            rootView: mnPageLoader.mnView,
            tests: [
                 {
                    viewId: ctwl.INSTANCE_TABS_ID,
                    suites: [
                        {
                            class: TabsViewTestSuite,
                            groups: ['all']
                        }
                    ]
                },
                {
                    viewId: ctwl.INSTANCE_DETAILS_ID,
                    suites: [
                        {
                            class: DetailsViewTestSuite,
                            groups: ['all'],
                            modelConfig: {
                                dataGenerator: cttu.commonDetailsDataGenerator
                            }
                        }
                    ]
                },
                {
                    viewId: ctwl.INSTANCE_CPU_MEM_STATS_ID,
                    suites: [
                        {
                            class: LineBarChartViewTestSuite,
                            groups: ['all']
                        }
                    ]
                },
                {
                    viewId: ctwl.INSTANCE_TRAFFIC_STATS_CHART_ID,
                    suites: [
                        {
                            class: LineWithFocusChartViewTestSuite,
                            groups: ['all']
                        }
                    ]
                },
                {
                    viewId: ctwl.INSTANCE_INTERFACE_GRID_ID,
                    suites: [
                        {
                            class: GridViewTestSuite,
                            groups: ['all']
                        },
                        {
                            class: GridListModelTestSuite,
                            groups: ['all'],
                            modelConfig: {
                                dataGenerator: cttu.commonGridDataGenerator,
                                dataParsers: {
                                    mockDataParseFn: cttu.deleteSizeField,
                                    gridDataParseFn: cttu.deleteSizeField
                                }
                            }
                        },
                    ]
                },
            ]
        } ;

    };

    var testInitFn = function(defObj) {
        //simulate click on all the tabs
        var instanceTabsViewObj = mnPageLoader.mnView.viewMap[ctwl.INSTANCE_TABS_ID],
            instanceTabs = instanceTabsViewObj.attributes.viewConfig.tabs;

        _.each(instanceTabs, function(tab) {
            $("#" + tab.elementId + "-tab-link").trigger("click");
        });

        setTimeout(function() {
                defObj.resolve();
            },
            // Add necessary timeout for the tab elements to load properly and resolve the promise
            cotc.PAGE_INIT_TIMEOUT - 50
        );

        return;
    };

    var pageTestConfig = cotr.createPageTestConfig(moduleId, testType, fakeServerConfig, pageConfig, getTestConfig, testInitFn);

    cotr.startTestRunner(pageTestConfig);

});<commit_msg>Related-Bug: #1553413, Related-Bug: #1539243 Fix random timeout issue with UT<commit_after>/*
 * Copyright (c) 2015 Juniper Networks, Inc. All rights reserved.
 */
define([
    'co-test-runner',
    'ct-test-utils',
    'ct-test-messages',
    'monitor/networking/test/ui/views/InstanceView.mock.data',
    'co-tabs-view-test-suite',
    'co-details-view-test-suite',
    'co-grid-contrail-list-model-test-suite',
    'co-grid-view-test-suite',
    'co-chart-view-line-bar-test-suite',
    'co-chart-view-line-test-suite',
], function (cotr, cttu, cttm, TestMockdata, TabsViewTestSuite, DetailsViewTestSuite,
             GridListModelTestSuite, GridViewTestSuite, LineBarChartViewTestSuite, LineWithFocusChartViewTestSuite) {

    var moduleId = cttm.INSTANCE_VIEW_COMMON_TEST_MODULE;

    var testType = cotc.VIEW_TEST;

    var fakeServerConfig = cotr.getDefaultFakeServerConfig();

    var fakeServerResponsesConfig = function() {
        /**
         URIs
         /api/tenants/config/domains [done]
         /api/tenants/config/projects [done
         /api/tenants/projects/default-domain [done]
         /api/tenants/networks/default-domain:demo [done]
         /api/tenant/networking/virtual-machine [done]
         /api/tenant/monitoring/instance-connected-graph [done]
         /api/tenant/networking/virtual-machine-interfaces/summary [done]
         /api/tenant/networking/flow-series/vm [done]
         /api/tenant/networking/network/stats/top [done]
         /api/tenant/networking/stats [done]
         */

        var responses = [];
        /**
         *  /api/tenants/config/domains
         */
        responses.push(cotr.createFakeServerResponse( {
            url: cttu.getRegExForUrl(ctwc.URL_ALL_DOMAINS),
            body: JSON.stringify(TestMockdata.domainsMockData)
        }));
        responses.push(cotr.createFakeServerResponse( {
            url: cttu.getRegExForUrl(ctwc.URL_ALL_PROJECTS),
            body: JSON.stringify(TestMockdata.projectsMockData)
        }));
        responses.push(cotr.createFakeServerResponse( {
            url: /\/api\/tenants\/projects\/default-domain.*$/,
            body: JSON.stringify(TestMockdata.projectsMockData)
        }));
        responses.push(cotr.createFakeServerResponse( {
            url: /\/api\/tenants\/networks\/default\-domain\:demo.*$/,
            body: JSON.stringify(TestMockdata.demoProjectMockData)
        }));
        responses.push(cotr.createFakeServerResponse({
            url: /\/api\/tenant\/networking\/virtual\-machines\/details.*$/,
            body: JSON.stringify(TestMockdata.virtualMachineMockData)
        }));
        responses.push(cotr.createFakeServerResponse({
            url: /\/api\/tenant\/networking\/virtual\-machine.*$/,
            body: JSON.stringify(TestMockdata.virtualMachineStatsMockData)
        }));
        responses.push(cotr.createFakeServerResponse({
            url: /\/api\/tenant\/monitoring\/instance\-connected\-graph.*$/,
            body: JSON.stringify(TestMockdata.virtualMachineConnectedGraphMockData)
        }));
        responses.push(cotr.createFakeServerResponse({
            method: "POST",
            url: cttu.getRegExForUrl(ctwc.URL_QUERY),
            body: JSON.stringify(TestMockdata.reportsQueryMockData)
        }));
        responses.push(cotr.createFakeServerResponse({
            url: /\/api\/tenant\/networking\/flow\-series\/vm.*$/,
            body: JSON.stringify(TestMockdata.virtualMachineFlowSeriesMockData)
        }));
        responses.push(cotr.createFakeServerResponse({
            method: "POST",
            url: cttu.getRegExForUrl(ctwc.URL_VM_INTERFACES),
            body: JSON.stringify(TestMockdata.virtualMachineInterfacesMockData)
        }));
        responses.push(cotr.createFakeServerResponse({
            url: /\/api\/tenant\/networking\/network\/stats\/top.*$/,
            body: JSON.stringify(TestMockdata.networkingStatsTopMockData)
        }));
        responses.push(cotr.createFakeServerResponse({
            method: "POST",
            url: cttu.getRegExForUrl(ctwc.URL_VM_VN_STATS),
            body: JSON.stringify(TestMockdata.networkingStatsMockData)
        }));
        return responses;
    };
    fakeServerConfig.getResponsesConfig = fakeServerResponsesConfig;

    var pageConfig = cotr.getDefaultPageConfig();
    pageConfig.hashParams = {
        p: 'mon_networking_instances',
        q: {
            view: 'details',
            type: 'instance',
            "focusedElement": {
                "fqName": "default-domain:demo:st_vn101",
                "type": "virtual-machine",
                "uuid": "0275be58-4e5f-440e-81fa-07aac3fb1623",
                "vmName": "st_vn101_vm21"
            }
        }
    };
    pageConfig.loadTimeout = cotc.PAGE_LOAD_TIMEOUT * 5;

    var getTestConfig = function() {
        return {
            rootView: mnPageLoader.mnView,
            tests: [
                 {
                    viewId: ctwl.INSTANCE_TABS_ID,
                    suites: [
                        {
                            class: TabsViewTestSuite,
                            groups: ['all']
                        }
                    ]
                },
                {
                    viewId: ctwl.INSTANCE_DETAILS_ID,
                    suites: [
                        {
                            class: DetailsViewTestSuite,
                            groups: ['all'],
                            modelConfig: {
                                dataGenerator: cttu.commonDetailsDataGenerator
                            }
                        }
                    ]
                },
                {
                    viewId: ctwl.INSTANCE_CPU_MEM_STATS_ID,
                    suites: [
                        {
                            class: LineBarChartViewTestSuite,
                            groups: ['all']
                        }
                    ]
                },
                {
                    viewId: ctwl.INSTANCE_TRAFFIC_STATS_CHART_ID,
                    suites: [
                        {
                            class: LineWithFocusChartViewTestSuite,
                            groups: ['all']
                        }
                    ]
                },
                {
                    viewId: ctwl.INSTANCE_INTERFACE_GRID_ID,
                    suites: [
                        {
                            class: GridViewTestSuite,
                            groups: ['all']
                        },
                        {
                            class: GridListModelTestSuite,
                            groups: ['all'],
                            modelConfig: {
                                dataGenerator: cttu.commonGridDataGenerator,
                                dataParsers: {
                                    mockDataParseFn: cttu.deleteSizeField,
                                    gridDataParseFn: cttu.deleteSizeField
                                }
                            }
                        },
                    ]
                },
            ]
        } ;

    };

    var testInitFn = function(defObj, onAllViewsRenderComplete) {
        //simulate click on all the tabs
        var instanceTabsViewObj = mnPageLoader.mnView.viewMap[ctwl.INSTANCE_TABS_ID],
            instanceTabs = instanceTabsViewObj.attributes.viewConfig.tabs;

        _.each(instanceTabs, function(tab) {
            $("#" + tab.elementId + "-tab-link").trigger("click");
        });

        setTimeout(function() {
                /**
                 * Tabs are already rendered so by default the event will not get fired.
                 * call the notify once tabs are activated.
                 */
                onAllViewsRenderComplete.notify();
                defObj.resolve();
            },
            // Add necessary timeout for the tab elements to load properly and resolve the promise
            cotc.PAGE_INIT_TIMEOUT * 2
        );

        return;
    };

    var pageTestConfig = cotr.createPageTestConfig(moduleId, testType, fakeServerConfig, pageConfig, getTestConfig, testInitFn);

    cotr.startTestRunner(pageTestConfig);

});<|endoftext|>


<commit_before>Application.Directives.directive('projectTabs', projectTabsDirective);

function projectTabsDirective() {
    return {
        scope: {
            project: "="
        },
        controller: 'projectTabsDirectiveController',
        restrict: "AE",
        templateUrl: "application/core/projects/project/project-tabs.html"
    };
}

Application.Controllers.controller('projectTabsDirectiveController',
                                  ["$scope", "$state","projectColors", "pubsub", "model.projects", "$stateParams", "$filter", projectTabsDirectiveController]);

function projectTabsDirectiveController($scope, $state, projectColors, pubsub, Projects,$stateParams, $filter) {

    pubsub.waitOn($scope, "update-tab-count.change", function () {
            Projects.get($stateParams.id).then(function (project) {
                $scope.project.reviews = project.reviews;
                $scope.project.samples = project.samples;
                $scope.project.notes = project.notes;
                $scope.updateTabCounts();
            });
    });
    var activeTab = "overview";

    if ($state.current.name == 'projects.project.reviews.view'){
        activeTab = "reviews";
    }
    $scope.colors = projectColors;
    $scope.inactiveColor = projectColors.getInactiveColor();

    $scope.setActiveTab = function(tabID) {
        activeTab = tabID;
    };

    $scope.isActiveTab = function (tabID) {
        return tabID == activeTab;
    };

    $scope.getActiveStyle = function() {
        var color = projectColors.getCurrentProjectColor();
        return {
            background: color,
            'border-bottom-style': 'solid',
            'border-bottom-color': color,
            color: 'white',
            'font-weight': 'bold',
            outline: 0
        };
    };

    $scope.getInactiveStyle = function() {
        var color = projectColors.getCurrentProjectColorLight();
        var fontColor = projectColors.getInactiveColor();
        return {
            color: fontColor,
            background: color
        };
    };

    $scope.updateTabCounts = function(){
        $scope.tabs = [
            {
                id: "overview",
                icon: "fa-list",
                name: "Overview",
                hasCount: false,
                defaulttab: "view"
            },

            {
                id: "files",
                icon: "fa-files-o",
                name: "Files",
                hasCount: false,
                defaulttab: "view"
            },

            {
                id: "samples",
                icon: "fa-cubes",
                name: "Samples",
                hasCount: true,
                count: $scope.project.samples.length,
                defaulttab: "view"
            },

            {
                id: "provenance",
                icon: "fa-code-fork",
                name: "Provenance",
                hasCount: true,
                count: $scope.project.processes.length,
                defaulttab: "view"
            },

            {
                id: "reviews",
                icon: "fa-comment",
                name: "Reviews",
                hasCount: true,
                count: ($filter('byKey')($scope.project.reviews, 'status', 'open')).length ,
                defaulttab: "view"
            },

            {
                id: "notes",
                icon: "fa-edit",
                name: "Notes",
                hasCount: true,
                count: $scope.project.notes.length,
                defaulttab: "view"
            }
        ];
    }
    function init(){
        console.dir($scope.project);
        $scope.updateTabCounts();
    }
    init();

}
<commit_msg>removed console from project tabs<commit_after>Application.Directives.directive('projectTabs', projectTabsDirective);

function projectTabsDirective() {
    return {
        scope: {
            project: "="
        },
        controller: 'projectTabsDirectiveController',
        restrict: "AE",
        templateUrl: "application/core/projects/project/project-tabs.html"
    };
}

Application.Controllers.controller('projectTabsDirectiveController',
                                  ["$scope", "$state","projectColors", "pubsub", "model.projects", "$stateParams", "$filter", projectTabsDirectiveController]);

function projectTabsDirectiveController($scope, $state, projectColors, pubsub, Projects,$stateParams, $filter) {

    pubsub.waitOn($scope, "update-tab-count.change", function () {
            Projects.get($stateParams.id).then(function (project) {
                $scope.project.reviews = project.reviews;
                $scope.project.samples = project.samples;
                $scope.project.notes = project.notes;
                $scope.updateTabCounts();
            });
    });
    var activeTab = "overview";

    if ($state.current.name == 'projects.project.reviews.view'){
        activeTab = "reviews";
    }
    $scope.colors = projectColors;
    $scope.inactiveColor = projectColors.getInactiveColor();

    $scope.setActiveTab = function(tabID) {
        activeTab = tabID;
    };

    $scope.isActiveTab = function (tabID) {
        return tabID == activeTab;
    };

    $scope.getActiveStyle = function() {
        var color = projectColors.getCurrentProjectColor();
        return {
            background: color,
            'border-bottom-style': 'solid',
            'border-bottom-color': color,
            color: 'white',
            'font-weight': 'bold',
            outline: 0
        };
    };

    $scope.getInactiveStyle = function() {
        var color = projectColors.getCurrentProjectColorLight();
        var fontColor = projectColors.getInactiveColor();
        return {
            color: fontColor,
            background: color
        };
    };

    $scope.updateTabCounts = function(){
        $scope.tabs = [
            {
                id: "overview",
                icon: "fa-list",
                name: "Overview",
                hasCount: false,
                defaulttab: "view"
            },

            {
                id: "files",
                icon: "fa-files-o",
                name: "Files",
                hasCount: false,
                defaulttab: "view"
            },

            {
                id: "samples",
                icon: "fa-cubes",
                name: "Samples",
                hasCount: true,
                count: $scope.project.samples.length,
                defaulttab: "view"
            },

            {
                id: "provenance",
                icon: "fa-code-fork",
                name: "Provenance",
                hasCount: true,
                count: $scope.project.processes.length,
                defaulttab: "view"
            },

            {
                id: "reviews",
                icon: "fa-comment",
                name: "Reviews",
                hasCount: true,
                count: ($filter('byKey')($scope.project.reviews, 'status', 'open')).length ,
                defaulttab: "view"
            },

            {
                id: "notes",
                icon: "fa-edit",
                name: "Notes",
                hasCount: true,
                count: $scope.project.notes.length,
                defaulttab: "view"
            }
        ];
    }
    function init(){
        $scope.updateTabCounts();
    }
    init();

}
<|endoftext|>


<commit_before>var U = require('../util'),
    Q = require('q'),
    PATH = require('path');

exports.baseTechPath = require.resolve('./level.js');

exports.techMixin = {

    createByDecl: function(item, level, opts) {

        var _this = this,
            projectName = item.block;

        return this.createLevel({
                outputDir: level.dir,
                force: opts.force,
                level: 'project'
            }, [projectName])
            .then(function() {
                return _this.createProject(PATH.join(level.dir, projectName), opts);
            });

    },

    /**
     * Create the following project structure
     *
     * .bem/
     *     levels/
     *         blocks.js
     *         bundles.js
     *     techs/
     *     level.js
     *
     * @param {String} path  Absolute path to the project directory
     * @param {Object} opts  Options to the `bem create` command
     * @return {Promise * Undefined}
     */
    createProject: function(path, opts) {

        var bemDir = PATH.join(path, '.bem'),
            levels = PATH.join(bemDir, 'levels'),

            // .bem/levels/create blocks.js level prototype
            blocks = this.createLevel({
                forceTech: ['examples'],
                outputDir: levels,
                force: opts.force
            }, ['blocks.js']),

            // create .bem/levels/bundles.js level prototype
            bundles = this.createLevel({
                outputDir: levels,
                force: opts.force
            }, ['bundles.js']),

            // create .bem/levels/examples.js level prototype
            examples = bundles.then(function() {
                return this.createLevel({
                    level: PATH.resolve(levels, 'bundles.js'),
                    outputDir: levels,
                    force: opts.force
                }, ['examples.js'])
            }.bind(this)),

            // create .bem/techs directory
            techs = U.mkdirp(PATH.join(bemDir, 'techs')),

            // create .bem/techs and node_modules/ directories
            dirs = Q.all([
                U.mkdirp(PATH.join(bemDir, 'techs')),
                U.mkdirp(PATH.join(path, 'node_modules'))
            ]),

            // run `npm link bem` command
            linkBem = U.exec('npm link bem', { cwd: path, env: process.env });

        return Q.all([blocks, bundles, examples, dirs, linkBem])
            .then(function() {});

    }

};
<commit_msg>project tech: Create docs level prototype on bem create<commit_after>var U = require('../util'),
    Q = require('q'),
    PATH = require('path');

exports.baseTechPath = require.resolve('./level.js');

exports.techMixin = {

    createByDecl: function(item, level, opts) {

        var _this = this,
            projectName = item.block;

        return this.createLevel({
                outputDir: level.dir,
                force: opts.force,
                level: 'project'
            }, [projectName])
            .then(function() {
                return _this.createProject(PATH.join(level.dir, projectName), opts);
            });

    },

    /**
     * Create the following project structure
     *
     * .bem/
     *     levels/
     *         blocks.js
     *         bundles.js
     *     techs/
     *     level.js
     *
     * @param {String} path  Absolute path to the project directory
     * @param {Object} opts  Options to the `bem create` command
     * @return {Promise * Undefined}
     */
    createProject: function(path, opts) {

        var bemDir = PATH.join(path, '.bem'),
            levels = PATH.join(bemDir, 'levels'),

            // .bem/levels/create blocks.js level prototype
            blocks = this.createLevel({
                forceTech: ['examples'],
                outputDir: levels,
                force: opts.force
            }, ['blocks.js']),

            // create .bem/levels/bundles.js level prototype
            bundles = this.createLevel({
                outputDir: levels,
                force: opts.force
            }, ['bundles.js']),

            // create .bem/levels/examples.js level prototype
            examples = bundles.then(function() {
                return this.createLevel({
                    level: PATH.resolve(levels, 'bundles.js'),
                    outputDir: levels,
                    force: opts.force
                }, ['examples.js'])
            }.bind(this)),

            // create .bem/levels/docs.js level prototype
            docs = bundles.then(function() {
                return this.createLevel({
                    forceTech: ['md'],
                    outputDir: levels,
                    force: opts.force
                }, ['docs.js'])
            }.bind(this)),

            // create .bem/techs and node_modules/ directories
            dirs = Q.all([
                U.mkdirp(PATH.join(bemDir, 'techs')),
                U.mkdirp(PATH.join(path, 'node_modules'))
            ]),

            // run `npm link bem` command
            linkBem = U.exec('npm link bem', { cwd: path, env: process.env });

        return Q.all([blocks, bundles, examples, docs, dirs, linkBem])
            .then(function() {});

    }

};
<|endoftext|>


<commit_before>const { v4: uuidv4 } = require('uuid');
let VError = require('verror');
let util = require('util');

class TestSteps {
  constructor(cb) {
    this.cb = cb;
    this.finish();
  }

  get currentSuite() {
    return this.suites.length > 0 ? this.suites[this.suites.length - 1] : null;
  }

  get currentTest() {
    const currentSuite = this.currentSuite;
    if ( !currentSuite || currentSuite.tests.length === 0 )
      return null;
    return currentSuite.tests[currentSuite.tests.length - 1];
  }

  async finish() {
    if ( this.suites )
      for ( const suite of this.suites ) {
        await this._onSuiteFinish(suite);
      }

    this.suites = [];
    this._before = null;
    this._beforeEach = null;
    this._after = null;
    this._afterEach = null;
  }

  async describe(suiteName, fn) {
    const suite = this._createSuite(this._getSuitePrefix() + suiteName, false);
    fn();
    await this._executeSuite(suite);
  }

  async it(testName, fn) {
    let suite = this.currentSuite;
    if ( !suite ) {
      suite = this._createSuite('Default Suite', true);
      this._onSuiteStart(suite);
    }
    let test = this._createTest(suite, testName, fn);
    if ( suite.executeTestsImmediately ) {
      await this._executeTest(suite, test);
      this._onSuiteFinish(suite);
    } else
      suite.tests.push(test);
  }

  before(fn) {
    this._register('_before', fn);
  }

  beforeEach(fn) {
    this._register('_beforeEach', fn);
  }

  after(fn) {
    this._register('_after', fn);
  }

  afterEach(fn) {
    this._register('_afterEach', fn);
  }

  _getSuitePrefix() {
    if ( this.suites.length === 0 )
      return '';
    let answer = '';
    for ( const suite of this.suites )
      answer += `${suite.name} => `;
    return answer;
  }

  _register(hook, fn) {
    const suite = this.currentSuite;
    if ( suite )
      suite[hook] = fn;
    else
      this[hook] = fn;
  }

  _createSuite(suiteName, isDefault) {
    const suite = {
      uuid: uuidv4(),
      name: suiteName,
      tests: [],
      started: Date.now(),
      hasError: false,
      executeTestsImmediately: isDefault
    };
    this.suites.push(suite);
    return suite;
  }

  _createTest(suite, name, fn) {
    return {
      suiteUuid: suite.uuid,
      suiteName: suite.name,
      uuid: uuidv4(),
      name: name,
      fn: fn,
      started: Date.now()
    };
  }

  async _executeSuite(suite) {
    this._onSuiteStart(suite);

    if ( suite.tests )
      for ( const test of suite.tests ) {
        await this._executeTest(suite, test);
      }

    this._onSuiteFinish(suite);
  }

  async _onSuiteStart(suite) {
    const before = suite._before || this._before;
    if ( before )
      await this._executeFn(before);

    if ( this.cb.startSuite )
      this.cb.startSuite(suite);
  }

  async _onSuiteFinish(suite) {
    suite.finished = Date.now();
    suite.duration = Date.now() - suite.started;

    const after = suite._after || this._after;
    if ( after )
      await this._executeFn(after);

    this.suites.pop();
    if ( this.cb.finishSuite )
      this.cb.finishSuite(suite);
  }

  async _executeTest(suite, test) {
    const beforeEach = suite._beforeEach || this._beforeEach;
    if ( beforeEach )
      await this._executeFn(beforeEach);

    await this._executeStep(test.fn, suite, test);

    const afterEach = suite._afterEach || this._afterEach;
    if ( afterEach )
      await this._executeFn(afterEach);
  }

  async _executeStep(fn, suite, test) {
    if ( this.cb.startTest )
      this.cb.startTest(test, suite);

    try {
      await this._executeFn(fn);
      test.state = 'passed';
    } catch (err) {
      suite.hasError = true;
      test.state = 'failed';
      test.errorType = err.name;
      if ( err.message )
        test.error = err.message;
      if ( err.stack )
        test.errorTrace = this.toErrorContent(err);
    }
    test.finished = Date.now();
    test.duration = Date.now() - test.started;

    if ( this.cb.finishTest )
      this.cb.finishTest(test, suite);
  }

  async _executeFn(fn) {
    const isAsync = fn.constructor.name === 'AsyncFunction';
    if ( isAsync )
      await fn();
    else if ( fn.length === 1 )
      await new Promise(function (resolve, reject) {
        fn(function (err) {
          if ( err )
            reject(new Error(err));
          else
            resolve();
        });
      });
    else
      fn();
  }

  filterStackTrace(stack) {
    const lines = stack.split('\n');
    var answer = '';
    var hitScriptLine = false;
    _.forEach(lines, function (line) {
      var trimmedLine = line.trim();
      if (!hitScriptLine)
        answer += line + '\n';
      if (trimmedLine.indexOf('at script.js') === 0 || trimmedLine.indexOf('(script.js') > 0)
        hitScriptLine = true;
    });
    return answer;
  }

  toErrorContent(error, msgOnly) {
    if (_.isUndefined(error) || error === null)
      return "";
    if (_.isString(error))
      return error;
    if (!_.isError(error) && _.isBuffer(error))
      return error.toString();
    if (!_.isError(error) && _.isObject(error))
      return util.inspect(error);
    if (!_.isError(error))
      return '' + error;
    if (_.isUndefined(error.message))
      return "Unknown error";
    switch (error.message) {
      case "Error: read ECONNRESET":
      case "Error: socket hang up":
        return "Connection closed unexpectedly";
      case "connect ETIMEDOUT":
        return "Connect timed out";
      default:
        return msgOnly ? error.message : this.filterStackTrace(VError.fullStack(error));
    }
  }

}

module.exports = TestSteps;

/*

const webdriver = require('selenium-webdriver');
const util = require('util');
const fs = require('fs');
const writeFile = util.promisify(fs.writeFile);
const path = require('path');
const { expect } = require('chai');

async function takeScreenshot(driver, file){
  let image = await driver.takeScreenshot();
  await writeFile(file, image, 'base64');
}

describe('DefaultTest', () => {
  let driver = new webdriver.Builder()
    .withCapabilities(webdriver.Capabilities.chrome())
    .build();

  it('should go to google.com and check title', async () => {
    await driver.get('https://www.google.com');
    await takeScreenshot(driver, path.join(process.env.OUTPUT_DIR || '.', 'HomePage.png'));
    const title = await driver.getTitle();
    expect(title).to.equal('Google');
  });

  after(async () => driver.quit());
});

{
  "name": "webdriver-testable-demo",
  "version": "0.0.1",
  "description": "Simple Webdriver Javascript Example",
  "author": "Testable",
  "devDependencies": {
  "testable-utils": "0.5.9",
    "selenium-webdriver": "3.6.0",
    "mocha": "8.1.3",
    "chai": "4.2.0"
}
}*/
<commit_msg>callback functions added<commit_after>const { v4: uuidv4 } = require('uuid');
let VError = require('verror');
let util = require('util');

const EarlyTerminationMsg = 'Connection closed early when test was stopped';

class TestSteps {

  requestsInProgress = 0;
  scriptRunning = false;

  constructor() {
    this.cb = this.callback;
    this.finish();
  }

  get currentSuite() {
    return this.suites.length > 0 ? this.suites[this.suites.length - 1] : null;
  }

  get currentTest() {
    const currentSuite = this.currentSuite;
    if ( !currentSuite || currentSuite.tests.length === 0 )
      return null;
    return currentSuite.tests[currentSuite.tests.length - 1];
  }

  async finish() {
    if ( this.suites )
      for ( const suite of this.suites ) {
        await this._onSuiteFinish(suite);
      }

    this.suites = [];
    this._before = null;
    this._beforeEach = null;
    this._after = null;
    this._afterEach = null;
  }

  async describe(suiteName, fn) {
    const suite = this._createSuite(this._getSuitePrefix() + suiteName, false);
    fn();
    await this._executeSuite(suite);
  }

  async it(testName, fn) {
    let suite = this.currentSuite;
    if ( !suite ) {
      suite = this._createSuite('Default Suite', true);
      this._onSuiteStart(suite);
    }
    let test = this._createTest(suite, testName, fn);
    if ( suite.executeTestsImmediately ) {
      await this._executeTest(suite, test);
      this._onSuiteFinish(suite);
    } else
      suite.tests.push(test);
  }

  before(fn) {
    this._register('_before', fn);
  }

  beforeEach(fn) {
    this._register('_beforeEach', fn);
  }

  after(fn) {
    this._register('_after', fn);
  }

  afterEach(fn) {
    this._register('_afterEach', fn);
  }

  _getSuitePrefix() {
    if ( this.suites.length === 0 )
      return '';
    let answer = '';
    for ( const suite of this.suites )
      answer += `${suite.name} => `;
    return answer;
  }

  _register(hook, fn) {
    const suite = this.currentSuite;
    if ( suite )
      suite[hook] = fn;
    else
      this[hook] = fn;
  }

  _createSuite(suiteName, isDefault) {
    const suite = {
      uuid: uuidv4(),
      name: suiteName,
      tests: [],
      started: Date.now(),
      hasError: false,
      executeTestsImmediately: isDefault
    };
    this.suites.push(suite);
    return suite;
  }

  _createTest(suite, name, fn) {
    return {
      suiteUuid: suite.uuid,
      suiteName: suite.name,
      uuid: uuidv4(),
      name: name,
      fn: fn,
      started: Date.now()
    };
  }

  async _executeSuite(suite) {
    this._onSuiteStart(suite);

    if ( suite.tests )
      for ( const test of suite.tests ) {
        await this._executeTest(suite, test);
      }

    this._onSuiteFinish(suite);
  }

  async _onSuiteStart(suite) {
    const before = suite._before || this._before;
    if ( before )
      await this._executeFn(before);

    if ( this.cb.startSuite )
      this.cb.startSuite(suite);
  }

  async _onSuiteFinish(suite) {
    suite.finished = Date.now();
    suite.duration = Date.now() - suite.started;

    const after = suite._after || this._after;
    if ( after )
      await this._executeFn(after);

    this.suites.pop();
    if ( this.cb.finishSuite )
      this.cb.finishSuite(suite);
  }

  async _executeTest(suite, test) {
    const beforeEach = suite._beforeEach || this._beforeEach;
    if ( beforeEach )
      await this._executeFn(beforeEach);

    await this._executeStep(test.fn, suite, test);

    const afterEach = suite._afterEach || this._afterEach;
    if ( afterEach )
      await this._executeFn(afterEach);
  }

  async _executeStep(fn, suite, test) {
    if ( this.cb.startTest )
      this.cb.startTest(test, suite);

    try {
      await this._executeFn(fn);
      test.state = 'passed';
    } catch (err) {
      suite.hasError = true;
      test.state = 'failed';
      test.errorType = err.name;
      if ( err.message )
        test.error = err.message;
      if ( err.stack )
        test.errorTrace = this.toErrorContent(err);
    }
    test.finished = Date.now();
    test.duration = Date.now() - test.started;

    if ( this.cb.finishTest )
      this.cb.finishTest(test, suite);
  }

  async _executeFn(fn) {
    const isAsync = fn.constructor.name === 'AsyncFunction';
    if ( isAsync )
      await fn();
    else if ( fn.length === 1 )
      await new Promise(function (resolve, reject) {
        fn(function (err) {
          if ( err )
            reject(new Error(err));
          else
            resolve();
        });
      });
    else
      fn();
  }

  filterStackTrace(stack) {
    const lines = stack.split('\n');
    var answer = '';
    var hitScriptLine = false;
    _.forEach(lines, function (line) {
      var trimmedLine = line.trim();
      if (!hitScriptLine)
        answer += line + '\n';
      if (trimmedLine.indexOf('at script.js') === 0 || trimmedLine.indexOf('(script.js') > 0)
        hitScriptLine = true;
    });
    return answer;
  }

  toErrorContent(error, msgOnly) {
    if (_.isUndefined(error) || error === null)
      return "";
    if (_.isString(error))
      return error;
    if (!_.isError(error) && _.isBuffer(error))
      return error.toString();
    if (!_.isError(error) && _.isObject(error))
      return util.inspect(error);
    if (!_.isError(error))
      return '' + error;
    if (_.isUndefined(error.message))
      return "Unknown error";
    switch (error.message) {
      case "Error: read ECONNRESET":
      case "Error: socket hang up":
        return "Connection closed unexpectedly";
      case "connect ETIMEDOUT":
        return "Connect timed out";
      default:
        return msgOnly ? error.message : this.filterStackTrace(VError.fullStack(error));
    }
  }

  callback (){
    const self = this;
    return {
      startSuite: function(suite) {
        self.reportTestSuiteActions([ { type: 'StartSuite', data: suite }]);
        // self._requestStarted();
      },
      finishSuite: function(suite) {
        self.reportTestSuiteActions([ { type: 'FinishSuite', data: suite }]);
        // self._requestFinished();
      },
      startTest: function(test) {
        self.reportTestSuiteActions([ { type: 'StartSuiteTest', data: test }]);
        // self._requestStarted();
      },
      finishTest: function(test) {
        self.reportTestSuiteActions([ { type: 'FinishSuiteTest', data: test }]);
        // self._requestFinished();
      }
    }
  }

  _requestStarted(){
    this.requestsInProgress = this.requestsInProgress + 1;
  }
  reportTestSuiteActions(suiteActions){
    // this.sendMsg({
    //   type: "TestSuiteActions",
    //   data: suiteActions
    // });
  }
  _requestFinished(result){
    this.requestsInProgress = this.requestsInProgress - 1;
    this.resultFinished(result);
    if (this.requestsInProgress === 0 && !this.scriptRunning) {
      // this._done();
    }
  }

  resultFinished(result, earlyTermination) {
    if (_.isObject(result) && !result.isFinished) {
      if (earlyTermination && result.data.url) {
        result.setTraceStatus('Terminated Early', EarlyTerminationMsg);
        result.addTrace('Error', {}, EarlyTerminationMsg);
      }
      result.finished();
      if (!result.isEmpty()) {
        this.sendMsg({
          type: "Result",
          data: result.data
        });
      }
      let index = this.activeResults.indexOf(result);
      if (index !== -1)
        this.activeResults.splice(index, 1);
      if (result.id)
        this.resultsById.delete(result.id);
    }
  }

}

module.exports = TestSteps;

/*

const webdriver = require('selenium-webdriver');
const util = require('util');
const fs = require('fs');
const writeFile = util.promisify(fs.writeFile);
const path = require('path');
const { expect } = require('chai');

async function takeScreenshot(driver, file){
  let image = await driver.takeScreenshot();
  await writeFile(file, image, 'base64');
}

describe('DefaultTest', () => {
  let driver = new webdriver.Builder()
    .withCapabilities(webdriver.Capabilities.chrome())
    .build();

  it('should go to google.com and check title', async () => {
    await driver.get('https://www.google.com');
    await takeScreenshot(driver, path.join(process.env.OUTPUT_DIR || '.', 'HomePage.png'));
    const title = await driver.getTitle();
    expect(title).to.equal('Google');
  });

  after(async () => driver.quit());
});

{
  "name": "webdriver-testable-demo",
  "version": "0.0.1",
  "description": "Simple Webdriver Javascript Example",
  "author": "Testable",
  "devDependencies": {
  "testable-utils": "0.5.9",
    "selenium-webdriver": "3.6.0",
    "mocha": "8.1.3",
    "chai": "4.2.0"
}
}*/
<|endoftext|>


<commit_before>/*
 * Copyright 2013 Laurent Bovet <laurent.bovet@windmaster.ch>
 *
 * 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.
 */

var TypeScript, _, traverse; // from global scope
(function (definition) {
    // CommonJS
    if (typeof exports === "object") {
        module.exports = definition(require("underscore"), require("q"), require("traverse"), require("./typson.js"));
        // RequireJS
    } else if (typeof define === "function" && define.amd) {
        define(["vendor/underscore", "vendor/q", "vendor/traverse", "./typson"], definition);
    }
})
(function (underscore, Q, _traverse, typson) {
    if (underscore) {
        _ = underscore;
    }
    if( _traverse) {
        traverse = _traverse;
    }
    var api = {};
    var primitiveTypes = [ "string", "number", "boolean", "any" ];
    var validationKeywords = [ "type", "minimum", "exclusiveMinimum", "maximum", "exclusiveMaximum", "multipleOf", "minLength", "maxLength", "format", "pattern", "minItems", "maxItems", "uniqueItems", "default", "additionalProperties" ];
    var annotedValidationKeywordPattern = /@[a-z.]+\s*[^@\s]+/gi;
    var TypescriptASTFlags = { "optionalName" : 4, "arrayType" : 8 };
    var defaultProperties = { additionalProperties: false};

    /**
     * Creates json-schema type definitions from a type script.
     *
     * @param script {string} URI pointing to the source type script or the script itself.
     * @param refPath {string} A path to prepend to $ref references.
     */
    api.definitions = function (script, refPath) {
        return Q.promise(function (resolve) {
            typson.tree(script).done(function (tree) {
                TypeScript = typson.TypeScript;
                var definitions = {
                        interfaces: {},
                        enums: {}
                    };
                _.each(tree, function (script) {
                    _.each(script.moduleElements.members, function (decl) {
                        handleDeclaration(decl, definitions, refPath, "");
                    });
                });

                traverse(definitions.interfaces).forEach(function(item) {
                    if(this.key === "$ref") {
                        var currentSchema = this.path[0];
                        var lastDot = currentSchema.lastIndexOf(".");
                        if(lastDot > -1) {
                            this.update(fullModulePath(currentSchema.substring(0, lastDot), item, definitions.interfaces));
                        }
                    }
                });

                resolve(definitions.interfaces);
            });
        });
    };

    /**
     * Generates a schema from a type script.
     *
     * @param script {string} URI pointing to the source type script or the script itself.
     * @param type {string} The type to generate the schema from.
     * @param [id] {string} Schema id.
     */
    api.schema = function (script, type, id) {
        return Q.promise(function (resolve) {
            api.definitions(script, "#/definitions").done(function(definitions) {
                var schema = {};
                schema.$schema = "http://json-schema.org/draft-04/schema#";
                _.extend(schema, definitions[type]);
                delete definitions[type];
                if(id) {
                    schema.id = id;
                }
                resolve(schema);
                schema.definitions = definitions;
            });
        });
    };

    function handleDeclaration(decl, definitions, refPath, modulePath) {
        if (decl.nodeType() === TypeScript.NodeType.InterfaceDeclaration ||
                decl.nodeType() === TypeScript.NodeType.ClassDeclaration) {
            handleInterfaceDeclaration(decl, definitions, refPath, modulePath);
        }
        else if (decl.nodeType() === TypeScript.NodeType.ModuleDeclaration) {
            if (decl.getModuleFlags() & TypeScript.ModuleFlags.IsEnum) {
                handleEnumDeclaration(decl, definitions, modulePath);
            } else {
                _.each(decl.members.members, function (subDecl) {
                    var subModulePath = modulePath;
                    if(!(decl.getModuleFlags() & TypeScript.ModuleFlags.IsWholeFile)) {
                        subModulePath+=(decl.name.actualText+".");
                    }
                    handleDeclaration(subDecl, definitions, refPath, subModulePath);
                });
            }
        }
    }

    /**
     * Handles interface declaration as new definition and registers it in the global set of interface definitions
     *
     * @param type {object} the TypeScript AST node associated to the interface declaration
     * @param definitions {object} the set of handled interface and enum definitions
     */
    function handleInterfaceDeclaration(type, definitions, refPath, modulePath) {
        var name = (modulePath.length > 0 ? modulePath : "") + type.name.actualText;
        var definition = definitions.interfaces[name] = {};
        definition.id = name;
        definition.type = "object";
        copyComment(type, definition);

        definition.properties = {};
        mergeInheritedProperties(type, definition, definitions);
        handlePropertyDeclaration(type, definition, definitions, refPath);
        _.defaults(definition, defaultProperties);
    }

    /**
     * Handles property or interface declarations and adds them to the interface definition.
     * Recursive, if the property has child properties, i.e. inline object declarations,
     * this function will recurse down to get all the definitions in the schema.
     *
     * @param type {object} the TypeScript AST node associated to the property declaration
     * @param definition {object} the property definition
     * @param definitions {object} the set of handled interface and enum definitions
     */
    function handlePropertyDeclaration(type, definition, definitions, refPath) {
        _.each(type.members.members, function (variable) {
            var property = definition.properties[variable.id.actualText] = {};
            copyComment(variable, property);
            var overridenType = property.type;
            var variableType = extractFullTypeName(variable.typeExpr.term);
            var propertyType = null;

            //required
            if (!(variable.id.getFlags() & TypescriptASTFlags.optionalName)) {
                if (!definition.required) {
                    definition.required = [];
                }
                definition.required.push(variable.id.actualText);
            }
            //arrays
            if (variable.typeExpr.getFlags() & TypescriptASTFlags.arrayType) {
                property.type = "array";
                propertyType = property.items = property.items || {};
            }
            //maps
            else if (variable.typeExpr.term.getFlags() & TypescriptASTFlags.arrayType) {
                property.type = "object";

                var members = variable.typeExpr.term.members.members;

                // Map (arbitrary properties)
                if (members[0].returnTypeAnnotation) {
                    propertyType = property.additionalProperties = {};
                    variableType = variable.typeExpr.term.members.members[0].returnTypeAnnotation.term.actualText;

                    if (!variableType) {
                        // Map where value is an inline property declaration
                        _.defaults(property, defaultProperties);
                        _.defaults(property.additionalProperties, defaultProperties);
                        property.additionalProperties.type = "object";
                        property.additionalProperties.properties = {};
                        handlePropertyDeclaration(variable.typeExpr.term.members.members[0].returnTypeAnnotation.term, property.additionalProperties, definitions);
                        variableType = "any";
                    }
                }
                // Object (inline declaration)
                else {
                    _.defaults(property, defaultProperties);
                    property.properties = {};
                    handlePropertyDeclaration(variable.typeExpr.term, property, definitions);
                    variableType = "any";
                }
            }
            //other
            else {
                propertyType = property;
            }

            var fullTypeName = variableType;

            //enums
            if (definitions.enums[fullTypeName]) {
                propertyType.id = definitions.enums[fullTypeName].id;
                propertyType.enum = _.keys(definitions.enums[fullTypeName].enumeration);
                addEnumDescription(definitions.enums[fullTypeName].enumeration, property);
            }
            //other
            else if (primitiveTypes.indexOf(variableType) === -1) {
                propertyType.$ref = refPath? refPath+"/"+fullTypeName: fullTypeName;
            } else {
                if(variableType !== "any") {
                    propertyType.type = overridenType || variableType;
                }
            }
        });
    }

    function extractFullTypeName(term) {

        if(term.actualText) {
            return term.actualText;
        } else {
            if(term.nodeType() === TypeScript.NodeType.MemberAccessExpression) {
                return extractFullTypeName(term.operand1)+"."+extractFullTypeName(term.operand2);
            }
        }
    }

    /**
     * Visits every super type extended by the given type recursively and provisions the given definition with the properties of the associated super definitions.
     *
     * @param type {object} the TypeScript AST node associated to the interface declaration
     * @param definition {object} the definition to be provisionned
     * @param definitions {object} the set of handled interface and enum definitions
     */
    function mergeInheritedProperties(type, definition, definitions) {
        if (type.extendsList) {
            _.each(type.extendsList.members, function (superType) {
                var superDefinition = definitions.interfaces[superType.actualText];
                // does the provisionning if a definition exists for the current super type
                if (superDefinition) {
                    // recursive call
                    mergeInheritedProperties(superType, definition, definitions);
                    // merges properties
                    for(var superKey in superDefinition.properties) {
                        definition.properties[superKey] = superDefinition.properties[superKey];
                    }
                    // merges required
                    if (superDefinition.required) {
                        _.each(superDefinition.required, function (requiredSuperPropertyName) {
                            if (!definition.required) {
                                definition.required = [];
                            }
                            definition.required.push(requiredSuperPropertyName);
                        });
                    }
                }
            });
        }
    }

    /**
     * Handles enum declaration as new definition and registers it in the global set of enum definitions
     *
     * @param type {object} the TypeScript AST node associated to the enum declaration
     * @param definitions {object} the set of handled interface and enum definitions
     */
    function handleEnumDeclaration(type, definitions, modulePath) {
        var name = (modulePath.length > 0 ? modulePath : "") + type.name.actualText;
        var definition = definitions.enums[name] = {};
        definition.enumeration = {};
        definition.id = type.name.actualText;
        _.each(type.members.members, function (declaration) {
            var comment = declaration.declaration.declarators.members[0].docComments().slice(-1)[0];
            var commentText = comment ? comment.getDocCommentTextValue() : "";
            var declarationText = declaration.declaration.declarators.members[0].id.actualText;
            declarationText = declarationText.replace(/^["'](.+)["']$/, "$1");
            definition.enumeration[declarationText] = commentText;
        });
    }

    /**
     * Extracts the description and the validation keywords from a comment
     *
     * @param from {object} the TypeScript AST node
     * @param to {object} the destination variable or definition.
     */
    function copyComment(from, to) {
        var comments = from.docComments();
        if (comments.length > 0) {
            var commentContent = comments.slice(-1)[0].getDocCommentTextValue();
            copyValidationKeywords(copyDescription(commentContent, to), to);
        }
    }

    /**
     * Extracts the description part of a comment and register it in the description property.
     * The description is supposed to start at first position and may be delimited by @.
     *
     * @param comment {string} the full comment.
     * @param to {object} the destination variable or definition.
     * @returns {string} the full comment minus the beginning description part.
     */
    function copyDescription(comment, to) {
        var delimiter = "@";
        var delimiterIndex = comment.indexOf(delimiter);
        var description = comment.slice(0, delimiterIndex < 0 ? comment.length : delimiterIndex);
        if (description.length > 0) {
            to.description = description.replace(/\s+$/g, "");
        }
        return delimiterIndex < 0 ? "" : comment.slice(delimiterIndex);
    }

    /**
     * Extracts the schema validation keywords stored in a comment and register them as properties.
     * A validation keyword starts by a @. It has a name and a value. Several keywords may occur.
     *
     * @param comment {string} the full comment.
     * @param to {object} the destination variable.
     */
    function copyValidationKeywords(comment, to) {
        annotedValidationKeywordPattern.lastIndex = 0;
        // TODO: to improve the use of the exec method: it could make the tokenization
        var annotation;
        while ((annotation = annotedValidationKeywordPattern.exec(comment))) {
            var annotationTokens = annotation[0].split(" ");
            var keyword = annotationTokens[0].slice(1);
            var path = keyword.split(".");
            var context = null;
            if (path.length > 1) {
                context = path[0];
                keyword = path[1];
            }
            // case sensitive check inside the dictionary
            if (validationKeywords.indexOf(keyword) >= 0) {
                var value = annotationTokens.length > 1 ? annotationTokens[1] : "";
                try {
                    value = JSON.parse(value);
                } catch (e) {
                }
                if (context) {
                    if (!to[context]) {
                        to[context] = {};
                    }
                    to[context][keyword] = value;
                }
                else {
                    to[keyword] = value;
                }
            }
        }
    }

    function addEnumDescription(enumeration, property) {
        if(enumeration && _.values(enumeration).join("")) {
            var values = ["Value"];
            values = values.concat(_.keys(enumeration));
            var max = _.max(values, function(value) { return value.length; }).length;
            var table = "\n\n| "+values[0] + new Array(max+3-values[0].length).join(" ") + "| Description";
            table += "\n|-";
            _.each(enumeration, function(comment, value) {
                table += "\n| `"+value+"`"+ new Array(max+1-value.length).join(" ") + "|" + (comment ? " "+comment : "|");
            });
            property.description = property.description || "";
            property.description += table;
        }
    }

    /**
     * Compute an absolute module path to the target type given a reference module, by looking up it in
     * a type dictionary.
     *
     * @param refModule {string}
     * @param target {string}
     * @param types {object} A dictionary whose keys are the known types
     * @returns {string} the full path to the target
     */
    function fullModulePath(refModule, target, types) {
        var path = target.split("/");
        var prefix = path.splice(0, path.length-1).join("/");
        target = path.splice(-1)[0];
        var refPath = refModule.split(".");
        while(refPath.length > 0) {
            var candidate = refPath.join(".")+"."+target;
            if(candidate in types) {
                target = candidate;
                break;
            }
            refPath = refPath.slice(0, refPath.length-1);
        }
        return prefix.length > 0 ? prefix + "/" + target : target;
    }

    api.exec = function(script, type) {
        var sys = require("sys");
        if(type) {
            api.schema(script, type).done(function(schema) {
                sys.print(JSON.stringify(schema, null, 2));
            });
        } else {
            api.definitions(script).done(function(definitions) {
                sys.print(JSON.stringify(definitions, null, 2));
            });
        }
    };

    if (typeof window === "undefined" && require.main === module) {
        if (process.argv[2]) {
            api.exec(process.argv[2], process.argv[3]);
        } else {
            require("sys").print("Usage: node typson-schema.js <url-or-path-to-type-script-file> [type]\n");
        }
    } else {
        return api;
    }
});
<commit_msg>Some bug fix and remove constraint on validation/property keywords<commit_after>/*
 * Copyright 2013 Laurent Bovet <laurent.bovet@windmaster.ch>
 *
 * 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.
 */

var TypeScript, _, traverse; // from global scope
(function(definition) {
    // CommonJS
    if (typeof exports === "object") {
        module.exports = definition(require("underscore"), require("q"), require("traverse"), require("./typson.js"));
        // RequireJS
    } else if (typeof define === "function" && define.amd) {
        define(["vendor/underscore", "vendor/q", "vendor/traverse", "./typson"], definition);
    }
})
(function(underscore, Q, _traverse, typson) {
    if (underscore) {
        _ = underscore;
    }
    if (_traverse) {
        traverse = _traverse;
    }
    var api = {};
    var primitiveTypes = ["string", "number", "boolean", "any"];
    //var validationKeywords = ["type", "minimum", "exclusiveMinimum", "maximum", "exclusiveMaximum", "multipleOf",
    //    "minLength", "maxLength", "format", "pattern", "minItems", "maxItems", "uniqueItems", "default",
    //    "additionalProperties", "title"];
    var annotedValidationKeywordPattern = /@[a-z.]+\s*[^@\n\*]+/gi;
    var TypescriptASTFlags = {"optionalName": 4, "arrayType": 8};
    var defaultProperties = {additionalProperties: false};

    /**
     * Creates json-schema type definitions from a type script.
     *
     * @param script {string} URI pointing to the source type script or the script itself.
     * @param refPath {string} A path to prepend to $ref references.
     */
    api.definitions = function(script, refPath) {
        return Q.promise(function(resolve) {
            typson.tree(script).done(function(tree) {
                TypeScript = typson.TypeScript;
                var definitions = {
                    interfaces: {},
                    enums: {}
                };
                _.each(tree, function(script) {
                    _.each(script.moduleElements.members, function(decl) {
                        handleDeclaration(decl, definitions, refPath, "");
                    });
                });

                traverse(definitions.interfaces).forEach(function(item) {
                    if (this.key === "$ref") {
                        var currentSchema = this.path[0];
                        var lastDot = currentSchema.lastIndexOf(".");
                        if (lastDot > -1) {
                            this.update(fullModulePath(currentSchema.substring(0, lastDot), item, definitions.interfaces));
                        }
                    }
                });

                resolve(definitions.interfaces);
            });
        });
    };

    /**
     * Generates a schema from a type script.
     *
     * @param script {string} URI pointing to the source type script or the script itself.
     * @param type {string} The type to generate the schema from.
     * @param [id] {string} Schema id.
     */
    api.schema = function(script, type, id) {
        return Q.promise(function(resolve) {
            api.definitions(script, "#/definitions").done(function(definitions) {
                var schema = {};
                schema.$schema = "http://json-schema.org/draft-04/schema#";
                _.extend(schema, definitions[type]);
                delete definitions[type];
                if (id) {
                    schema.id = id;
                }
                resolve(schema);
                schema.definitions = definitions;
            });
        });
    };

    function handleDeclaration(decl, definitions, refPath, modulePath) {
        if (decl.nodeType() === TypeScript.NodeType.InterfaceDeclaration ||
            decl.nodeType() === TypeScript.NodeType.ClassDeclaration) {
            handleInterfaceDeclaration(decl, definitions, refPath, modulePath);
        }
        else if (decl.nodeType() === TypeScript.NodeType.ModuleDeclaration) {
            if (decl.getModuleFlags() & TypeScript.ModuleFlags.IsEnum) {
                handleEnumDeclaration(decl, definitions, modulePath);
            } else {
                _.each(decl.members.members, function(subDecl) {
                    var subModulePath = modulePath;
                    if (!(decl.getModuleFlags() & TypeScript.ModuleFlags.IsWholeFile)) {
                        subModulePath += (decl.name.actualText + ".");
                    }
                    handleDeclaration(subDecl, definitions, refPath, subModulePath);
                });
            }
        }
    }

    /**
     * Handles interface declaration as new definition and registers it in the global set of interface definitions
     *
     * @param type {object} the TypeScript AST node associated to the interface declaration
     * @param definitions {object} the set of handled interface and enum definitions
     */
    function handleInterfaceDeclaration(type, definitions, refPath, modulePath) {
        var name = (modulePath.length > 0 ? modulePath : "") + type.name.actualText;
        var definition = definitions.interfaces[name] = {};
        definition.id = name;
        definition.type = "object";
        copyComment(type, definition);

        definition.properties = {};
        mergeInheritedProperties(type, definition, definitions);
        handlePropertyDeclaration(type, definition, definitions, refPath);
        //copyComment(type, definition);
        _.defaults(definition, defaultProperties);
    }

    /**
     * Handles property or interface declarations and adds them to the interface definition.
     * Recursive, if the property has child properties, i.e. inline object declarations,
     * this function will recurse down to get all the definitions in the schema.
     *
     * @param type {object} the TypeScript AST node associated to the property declaration
     * @param definition {object} the property definition
     * @param definitions {object} the set of handled interface and enum definitions
     */
    function handlePropertyDeclaration(type, definition, definitions, refPath) {
        _.each(type.members.members, function(variable) {
            var property = definition.properties[variable.id.actualText] = {};
            copyComment(variable, property);
            var overridenType = property.type;
            var variableType = extractFullTypeName(variable.typeExpr.term);
            var propertyType = null;

            //required
            if (!(variable.id.getFlags() & TypescriptASTFlags.optionalName)) {
                if (!definition.required) {
                    definition.required = [];
                }
                definition.required.push(variable.id.actualText);
            }
            //arrays
            if (variable.typeExpr.getFlags() & TypescriptASTFlags.arrayType) {
                property.type = "array";
                propertyType = property.items = property.items || {};
            }
            //maps
            else if (variable.typeExpr.term.getFlags() & TypescriptASTFlags.arrayType) {
                property.type = "object";

                var members = variable.typeExpr.term.members.members;

                // Map (arbitrary properties)
                if (members[0].returnTypeAnnotation) {
                    propertyType = property.additionalProperties = {};
                    variableType = variable.typeExpr.term.members.members[0].returnTypeAnnotation.term.actualText;

                    if (!variableType) {
                        // Map where value is an inline property declaration
                        _.defaults(property, defaultProperties);
                        _.defaults(property.additionalProperties, defaultProperties);
                        property.additionalProperties.type = "object";
                        property.additionalProperties.properties = {};
                        handlePropertyDeclaration(variable.typeExpr.term.members.members[0].returnTypeAnnotation.term, property.additionalProperties, definitions, refPath);
                        variableType = "any";
                    }
                }
                // Object (inline declaration)
                else {
                    _.defaults(property, defaultProperties);
                    property.properties = {};
                    handlePropertyDeclaration(variable.typeExpr.term, property, definitions, refPath);
                    variableType = "any";
                }
            }
            //other
            else {
                propertyType = property;
            }

            var fullTypeName = variableType;

            //enums
            if (definitions.enums[fullTypeName]) {
                propertyType.id = definitions.enums[fullTypeName].id;
                propertyType.enum = _.keys(definitions.enums[fullTypeName].enumeration);
                addEnumDescription(definitions.enums[fullTypeName].enumeration, property);
            }
            //other
            else if (primitiveTypes.indexOf(variableType) === -1) {
                propertyType.$ref = refPath ? refPath + "/" + fullTypeName : fullTypeName;
            } else {
                if (variableType !== "any") {
                    propertyType.type = overridenType || variableType;
                }
            }
        });
    }

    function extractFullTypeName(term) {

        if (term.actualText) {
            return term.actualText;
        } else {
            if (term.nodeType() === TypeScript.NodeType.MemberAccessExpression) {
                return extractFullTypeName(term.operand1) + "." + extractFullTypeName(term.operand2);
            }
        }
    }

    /**
     * Visits every super type extended by the given type recursively and provisions the given definition with the properties of the associated super definitions.
     *
     * @param type {object} the TypeScript AST node associated to the interface declaration
     * @param definition {object} the definition to be provisionned
     * @param definitions {object} the set of handled interface and enum definitions
     */
    function mergeInheritedProperties(type, definition, definitions) {
        if (type.extendsList) {
            _.each(type.extendsList.members, function(superType) {
                var superDefinition = definitions.interfaces[superType.actualText];
                // does the provisionning if a definition exists for the current super type
                if (superDefinition) {
                    // recursive call
                    mergeInheritedProperties(superType, definition, definitions);
                    // merges properties
                    for (var superKey in superDefinition.properties) {
                        definition.properties[superKey] = superDefinition.properties[superKey];
                    }
                    // merges required
                    if (superDefinition.required) {
                        _.each(superDefinition.required, function(requiredSuperPropertyName) {
                            if (!definition.required) {
                                definition.required = [];
                            }
                            definition.required.push(requiredSuperPropertyName);
                        });
                    }
                }
            });
        }
    }

    /**
     * Handles enum declaration as new definition and registers it in the global set of enum definitions
     *
     * @param type {object} the TypeScript AST node associated to the enum declaration
     * @param definitions {object} the set of handled interface and enum definitions
     */
    function handleEnumDeclaration(type, definitions, modulePath) {
        var name = (modulePath.length > 0 ? modulePath : "") + type.name.actualText;
        var definition = definitions.enums[name] = {};
        definition.enumeration = {};
        definition.id = type.name.actualText;
        _.each(type.members.members, function(declaration) {
            var comment = declaration.declaration.declarators.members[0].docComments().slice(-1)[0];
            var commentText = comment ? comment.getDocCommentTextValue() : "";
            var declarationText = declaration.declaration.declarators.members[0].id.actualText;
            declarationText = declarationText.replace(/^["'](.+)["']$/, "$1");
            definition.enumeration[declarationText] = commentText;
        });
    }

    /**
     * Extracts the description and the validation keywords from a comment
     *
     * @param from {object} the TypeScript AST node
     * @param to {object} the destination variable or definition.
     */
    function copyComment(from, to) {
        var comments = from.docComments();
        if (comments.length > 0) {
            var commentContent = comments.slice(-1)[0].getDocCommentTextValue();
            copyValidationKeywords(copyDescription(commentContent, to), to);
        }
    }

    /**
     * Extracts the description part of a comment and register it in the description property.
     * The description is supposed to start at first position and may be delimited by @.
     *
     * @param comment {string} the full comment.
     * @param to {object} the destination variable or definition.
     * @returns {string} the full comment minus the beginning description part.
     */
    function copyDescription(comment, to) {
        var delimiter = "@";
        var delimiterIndex = comment.indexOf(delimiter);
        var description = comment.slice(0, delimiterIndex < 0 ? comment.length : delimiterIndex);
        if (description.length > 0) {
            to.description = description.replace(/\s+$/g, "");
        }
        return delimiterIndex < 0 ? "" : comment.slice(delimiterIndex);
    }

    /**
     * Extracts the schema validation keywords stored in a comment and register them as properties.
     * A validation keyword starts by a @. It has a name and a value. Several keywords may occur.
     *
     * @param comment {string} the full comment.
     * @param to {object} the destination variable.
     */
    function copyValidationKeywords(comment, to) {
        annotedValidationKeywordPattern.lastIndex = 0;
        // TODO: to improve the use of the exec method: it could make the tokenization
        var annotation;
        while ((annotation = annotedValidationKeywordPattern.exec(comment))) {
            var annotationTokens = annotation[0].split(" ");
            var keyword = annotationTokens[0].slice(1);
            var path = keyword.split(".");
            var context = null;
            if (path.length > 1) {
                context = path[0];
                keyword = path[1];
            }
            // case sensitive check inside the dictionary
            //if (validationKeywords.indexOf(keyword) >= 0) {
            var value = annotationTokens.length > 1 ? annotationTokens.slice(1).join(' ') : "";
            try {
                value = JSON.parse(value);
            } catch (e) {
            }
            if (context) {
                if (!to[context]) {
                    to[context] = {};
                }
                to[context][keyword] = value;
            }
            else {
                to[keyword] = value;
            }
            //}
        }
    }

    function addEnumDescription(enumeration, property) {
        if (enumeration && _.values(enumeration).join("")) {
            var values = ["Value"];
            values = values.concat(_.keys(enumeration));
            var max = _.max(values, function(value) {
                return value.length;
            }).length;
            var table = "\n\n| " + values[0] + new Array(max + 3 - values[0].length).join(" ") + "| Description";
            table += "\n|-";
            _.each(enumeration, function(comment, value) {
                table += "\n| `" + value + "`" + new Array(max + 1 - value.length).join(" ") + "|" +
                (comment ? " " + comment : "|");
            });
            property.description = property.description || "";
            property.description += table;
        }
    }

    /**
     * Compute an absolute module path to the target type given a reference module, by looking up it in
     * a type dictionary.
     *
     * @param refModule {string}
     * @param target {string}
     * @param types {object} A dictionary whose keys are the known types
     * @returns {string} the full path to the target
     */
    function fullModulePath(refModule, target, types) {
        var path = target.split("/");
        var prefix = path.splice(0, path.length - 1).join("/");
        target = path.splice(-1)[0];
        var refPath = refModule.split(".");
        while (refPath.length > 0) {
            var candidate = refPath.join(".") + "." + target;
            if (candidate in types) {
                target = candidate;
                break;
            }
            refPath = refPath.slice(0, refPath.length - 1);
        }
        return prefix.length > 0 ? prefix + "/" + target : target;
    }

    api.exec = function(script, type) {
        var sys = require("sys");
        if (type) {
            api.schema(script, type).done(function(schema) {
                sys.print(JSON.stringify(schema, null, 2));
            });
        } else {
            api.definitions(script).done(function(definitions) {
                sys.print(JSON.stringify(definitions, null, 2));
            });
        }
    };

    if (typeof window === "undefined" && require.main === module) {
        if (process.argv[2]) {
            api.exec(process.argv[2], process.argv[3]);
        } else {
            require("sys").print("Usage: node typson-schema.js <url-or-path-to-type-script-file> [type]\n");
        }
    } else {
        return api;
    }
});
<|endoftext|>