UNPKG

test-scribe

Version:

Your sidekick for analog machine development. Run machines interactively and generate automated test scripts based on the outcome.

395 lines (329 loc) 14.4 kB
/** * Homepage UI logic * * ng-app="homepage" * ng-controller="afterRender" */ wireUp('homepage', { // Other angular modules we depend on. dependencies: ['negotiateKeyboardEvent', 'ui.bootstrap.tooltip'], // The providers to pass in as `imports` in the functions below. imports: ['$scope', '$timeout', '$http', 'getInputValsFromScope'], // /$$ // | $$ // /$$$$$$ /$$$$$$$ /$$$$$$ /$$$$$$ /$$$$$$$ /$$$$$$$ /$$$$$$ /$$$$$$ // /$$__ $$| $$__ $$ /$$__ $$ /$$__ $$| $$__ $$ /$$__ $$ /$$__ $$ /$$__ $$ // | $$ \ $$| $$ \ $$ | $$ \__/| $$$$$$$$| $$ \ $$| $$ | $$| $$$$$$$$| $$ \__/ // | $$ | $$| $$ | $$ | $$ | $$_____/| $$ | $$| $$ | $$| $$_____/| $$ // | $$$$$$/| $$ | $$ | $$ | $$$$$$$| $$ | $$| $$$$$$$| $$$$$$$| $$ // \______/ |__/ |__/ |__/ \_______/|__/ |__/ \_______/ \_______/|__/ // Run when the page renders. afterRender: function (imports) { // Grab data that was bootstrapped on the page as view locals, // then turned into global constants in an inline script tag // in `homepage.ejs`. imports.$scope.packIdentity = PACK_IDENTITY; imports.$scope.pathToPack = PATH_TO_PACK; imports.$scope.machines = _.values(MACHINE_DEFS); // By default, reload pack every time a machine is run. imports.$scope.currentPack.willReloadOnRun = true; // A "control object" used for communicating with our dropdown directive. // (http://stackoverflow.com/a/18487942) imports.$scope.dropdownActions = { // Directive will add a function: // set: function (index) { ... } }; }, // /$$ // | $$ // /$$$$$$ /$$ /$$ /$$$$$$ /$$$$$$/$$$$ /$$$$$$ | $$ /$$$$$$ // /$$__ $$| $$ /$$/ |____ $$| $$_ $$_ $$ /$$__ $$| $$ /$$__ $$ // | $$$$$$$$ \ $$$$/ /$$$$$$$| $$ \ $$ \ $$| $$ \ $$| $$| $$$$$$$$ // | $$_____/ >$$ $$ /$$__ $$| $$ | $$ | $$| $$ | $$| $$| $$_____/ // | $$$$$$$ /$$/\ $$| $$$$$$$| $$ | $$ | $$| $$$$$$$/| $$| $$$$$$$ // \_______/|__/ \__/ \_______/|__/ |__/ |__/| $$____/ |__/ \_______/ // | $$ // | $$ // |__/ // /$$ /$$ /$$ // |__/ | $$ | $$ // /$$ /$$ /$$$$$$ /$$$$$$ /$$ /$$$$$$ | $$$$$$$ | $$ /$$$$$$ /$$$$$$$ // | $$ /$$/|____ $$ /$$__ $$| $$ |____ $$| $$__ $$| $$ /$$__ $$ /$$_____/ // \ $$/$$/ /$$$$$$$| $$ \__/| $$ /$$$$$$$| $$ \ $$| $$| $$$$$$$$| $$$$$$ // \ $$$/ /$$__ $$| $$ | $$ /$$__ $$| $$ | $$| $$| $$_____/ \____ $$ // \ $/ | $$$$$$$| $$ | $$| $$$$$$$| $$$$$$$/| $$| $$$$$$$ /$$$$$$$/ // \_/ \_______/|__/ |__/ \_______/|_______/ |__/ \_______/|_______/ // Example values for variables which are being used on $scope. variables: { showingHelpText: false, packIdentity: '', pathToPack: '', currentPack: { isReloading: true, hasReloaded: false, hasReloadingFailed: false, willReloadOnRun: true, hasRunAnyMachineOnce: false }, machines: [{ identity: '', friendlyName: '', description: '', inputs: [{ id: 'thing', friendlyName: 'The thing', description: 'The thing that... you know, the thing.', example: '*', displayExample: 'some JSON', displayType: 'JSON', }], exits: [{ id: 'success', friendlyName: 'then', description: 'Done.', example: '*' }] }], currentTest: { hasSavedTest: false, isSavingTest: false, hasError: false, error: '', outcome: 'foo', output: '*', hasOutput: true, outputType: 'string', exitExample: '*', hasExitExample: false, duration: 23523 }, currentMachine: { isDefined: false, isRunning: false, hasRun: false, hasError: false, error: '', identity: '', friendlyName: '', description: '', inputs: [{ id: 'thing', friendlyName: 'The thing', description: 'The thing that... you know, the thing.', example: '*', displayExample: 'some JSON', displayType: 'JSON', configuredVal: 'some string like this, typed by a human representing the current input val' }], exits: [{ id: 'success', friendlyName: 'then', description: 'Done.', example: '*' }] }, configuredInputsForm: '===' }, // /$$$$$$ /$$ /$$ // /$$__ $$ | $$ |__/ // | $$ \__//$$ /$$ /$$$$$$$ /$$$$$$$ /$$$$$$ /$$ /$$$$$$ /$$$$$$$ /$$$$$$$ // | $$$$ | $$ | $$| $$__ $$ /$$_____/|_ $$_/ | $$ /$$__ $$| $$__ $$ /$$_____/ // | $$_/ | $$ | $$| $$ \ $$| $$ | $$ | $$| $$ \ $$| $$ \ $$| $$$$$$ // | $$ | $$ | $$| $$ | $$| $$ | $$ /$$| $$| $$ | $$| $$ | $$ \____ $$ // | $$ | $$$$$$/| $$ | $$| $$$$$$$ | $$$$/| $$| $$$$$$/| $$ | $$ /$$$$$$$/ // |__/ \______/ |__/ |__/ \_______/ \___/ |__/ \______/ |__/ |__/|_______/ // Functions available on $scope. functions: { /** * Set whether or not to reload a machinepack on run * @param {[type]} inputs [description] * @param {[type]} imports [description] */ setReloadOnRun: function (inputs, imports) { imports.$scope.currentPack.willReloadOnRun = inputs.willReload; }, /** * Reload a machinepack * @param {[type]} inputs [description] * @param {[type]} imports [description] * @return {[type]} [description] */ reloadPack: function (inputs, imports) { // Set `currentPack` states for when it's reloading imports.$scope.currentPack.hasReloaded = false; imports.$scope.currentPack.isReloading = true; imports.$scope.currentPack.hasReloadingFailed = false; // Return promise. return imports.$http.get('/', { config: imports.getInputValsFromScope(imports.$scope) //<= why is this here again? }) .then(function (res){ // Reset `currentPack` states imports.$scope.currentPack.isReloading = false; imports.$scope.currentPack.hasReloaded = true; }) .catch(function (res){ // Give information about the error // and reset `currentPack` states console.log('an error occurred',res.data); imports.$scope.currentPack.hasReloaded = false; imports.$scope.currentPack.isReloading = false; imports.$scope.currentPack.hasReloadingFailed = true; }); }, /** * Set the current machine to the one chosen from the dropdown. * @param {[type]} inputs [description] * @param {[type]} imports [description] * @return {[type]} [description] */ chooseMachine: function (inputs, imports){ // Set the current machine on the $scope and mark it as defined imports.$scope.currentMachine = inputs.choice; imports.$scope.currentMachine.isDefined = true; // Clear out all saving/error statess for machines imports.$scope.currentMachine.isRunning = false; imports.$scope.currentMachine.hasRun = false; imports.$scope.currentMachine.hasError = false; // Clear out all saving/error statess for tests imports.$scope.currentTest.isSaving = false; imports.$scope.currentTest.hasSaved = false; imports.$scope.currentTest.hasError = false; // Apply scope manually (because this is called from a directive) imports.$scope.$apply(); }, /** * Run the selected machine * @param {[type]} inputs [description] * @param {[type]} imports [description] * @return {[type]} [description] */ runMachine: function (inputs, imports) { // Set the state of `currentMachine` in the UI imports.$scope.currentMachine.hasRun = false; imports.$scope.currentMachine.isRunning = true; imports.$scope.currentMachine.hasError = false; imports.$scope.currentTest.hasSaved = false; imports.$scope.currentTest.hasError = false; // If configured to do so, force a reload of the pack // before running the machine. var promise; if (imports.$scope.currentPack.willReloadOnRun) { promise = imports.$scope.reloadPack({}, imports); } else { promise = imports.$timeout(function (){}); } promise.then(function (){ imports.$http.post('/machinepreview/'+imports.$scope.currentMachine.identity, { config: imports.getInputValsFromScope(imports.$scope) }) .then(function (res){ imports.$scope.currentMachine.isRunning = false; imports.$scope.currentMachine.hasRun = true; imports.$scope.currentTest.duration = res.data.duration; imports.$scope.currentTest.outcome = res.data.outcome; var exitDef = imports.$scope.currentMachine.exits[imports.$scope.currentTest.outcome]; // If exit def is missing, that's pretty weird. if (!exitDef) { imports.$scope.currentMachine.hasError = true; imports.$scope.currentMachine.error = new Error('Outcome from run is invalid- machine does not have an exit named "'+imports.$scope.currentTest.outcome+'"'); return; } imports.$scope.currentTest.output = res.data.output; imports.$scope.currentTest.outputType = res.data.outputType; imports.$scope.currentTest.hasOutput = !_.isUndefined(imports.$scope.currentTest.output); if (!_.isUndefined(exitDef.example)) { imports.$scope.currentTest.exitExample = exitDef.example; imports.$scope.currentTest.hasExitExample = true; // console.log('EXIT EXAMPLE for %s:', imports.$scope.currentTest.outcome,imports.$scope.currentTest.exitExample); // console.log('EXIT DEF for %s:', imports.$scope.currentTest.outcome,exitDef); } // If exit has a "getExample" function, we'll use `example:'*'` for our purposes here. else if (!_.isUndefined(exitDef.getExample)) { imports.$scope.currentTest.exitExample = '*'; imports.$scope.currentTest.hasExitExample = true; // console.log('EXIT EXAMPLE:', imports.$scope.currentTest.exitExample); // console.log('getExample:', exitDef.getExample); } else { imports.$scope.currentTest.exitExample = undefined; imports.$scope.currentTest.hasExitExample = false; } // After the first run, the button to reload on run should appear. imports.$scope.currentPack.hasRunAnyMachineOnce = true; // console.log('output:', imports.$scope.currentTest.output); // console.log('hasOutput?:', imports.$scope.currentTest.hasOutput); // console.log('outputType:', imports.$scope.currentTest.outputType); // console.log('hasExitExample?', imports.$scope.currentTest.hasExitExample); // console.log('exitExample:', imports.$scope.currentTest.exitExample); }) .catch(function (res){ console.log('an error occurred',res.data); imports.$scope.currentMachine.isRunning = false; imports.$scope.currentMachine.hasRun = false; imports.$scope.currentMachine.hasError = true; imports.$scope.currentMachine.error = res.data; // After the first run, the button to reload on run should appear. imports.$scope.currentPack.hasRunAnyMachineOnce = true; }); }); }, /** * Save the previous run of the machine as a test * @param {[type]} inputs [description] * @param {[type]} imports [description] * @return {[type]} [description] */ saveAsTest: function (inputs, imports) { // Set the state of `currentTest` in the UI imports.$scope.currentTest.hasSaved = false; imports.$scope.currentTest.isSaving = true; imports.$scope.currentTest.hasError = false; // Only actually save the expected output if the exit has an example. var expectedOutput; if (imports.$scope.currentTest.hasExitExample) { expectedOutput = JSON.stringify(imports.$scope.currentTest.output); } imports.$http.put('/preview/'+imports.$scope.currentMachine.identity+'/test', { using: imports.getInputValsFromScope(imports.$scope), returns: expectedOutput, outcome: imports.$scope.currentTest.outcome }) .then(function (res){ imports.$scope.currentTest.isSaving = false; imports.$scope.currentTest.hasSaved = true; }) .catch(function (res){ console.log('an error occurred',res.data); imports.$scope.currentTest.isSaving = false; imports.$scope.currentTest.hasSaved = false; imports.$scope.currentTest.hasError = true; imports.$scope.currentTest.error = res.data; }); } } }); // /$$ /$$ // | $$ | $$ // | $$$$$$$ /$$$$$$ | $$ /$$$$$$ /$$$$$$ /$$$$$$ // | $$__ $$ /$$__ $$| $$ /$$__ $$ /$$__ $$ /$$__ $$ // | $$ \ $$| $$$$$$$$| $$| $$ \ $$| $$$$$$$$| $$ \__/ // | $$ | $$| $$_____/| $$| $$ | $$| $$_____/| $$ // | $$ | $$| $$$$$$$| $$| $$$$$$$/| $$$$$$$| $$ // |__/ |__/ \_______/|__/| $$____/ \_______/|__/ // | $$ // | $$ // |__/ // Helper factory that returns a stringified version of the configured input values from the scope. angular.module('homepage').factory('getInputValsFromScope', [function (){ return function ($scope){ return JSON.stringify(_.reduce($scope.currentMachine.inputs, function (memo, input) { memo.push({ name: input.id, value: _.isUndefined(input.configuredVal) ? '' : input.configuredVal }); return memo; },[])); }; }]);