 aacb45156b
			
		
	
	aacb45156b
	
	
	
		
			
			- Install Jest for unit testing with React Testing Library - Install Playwright for end-to-end testing - Configure Jest with proper TypeScript support and module mapping - Create test setup files and utilities for both unit and e2e tests Components: * Jest configuration with coverage thresholds * Playwright configuration with browser automation * Unit tests for LoginForm, AuthContext, and useSocketIO hook * E2E tests for authentication, dashboard, and agents workflows * GitHub Actions workflow for automated testing * Mock data and API utilities for consistent testing * Test documentation with best practices Testing features: - Unit tests with 70% coverage threshold - E2E tests with API mocking and user journey testing - CI/CD integration for automated test runs - Cross-browser testing support with Playwright - Authentication system testing end-to-end 🚀 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			331 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			331 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /*
 | |
|  * Jake JavaScript build tool
 | |
|  * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
 | |
|  *
 | |
|  * 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.
 | |
|  *
 | |
| */
 | |
| 
 | |
| if (!global.jake) {
 | |
| 
 | |
|   let EventEmitter = require('events').EventEmitter;
 | |
|   // And so it begins
 | |
|   global.jake = new EventEmitter();
 | |
| 
 | |
|   let fs = require('fs');
 | |
|   let chalk = require('chalk');
 | |
|   let taskNs = require('./task');
 | |
|   let Task = taskNs.Task;
 | |
|   let FileTask = taskNs.FileTask;
 | |
|   let DirectoryTask = taskNs.DirectoryTask;
 | |
|   let Rule = require('./rule').Rule;
 | |
|   let Namespace = require('./namespace').Namespace;
 | |
|   let RootNamespace = require('./namespace').RootNamespace;
 | |
|   let api = require('./api');
 | |
|   let utils = require('./utils');
 | |
|   let Program = require('./program').Program;
 | |
|   let loader = require('./loader')();
 | |
|   let pkg = JSON.parse(fs.readFileSync(__dirname + '/../package.json').toString());
 | |
| 
 | |
|   const MAX_RULE_RECURSION_LEVEL = 16;
 | |
| 
 | |
|   // Globalize jake and top-level API methods (e.g., `task`, `desc`)
 | |
|   Object.assign(global, api);
 | |
| 
 | |
|   // Copy utils onto base jake
 | |
|   jake.logger = utils.logger;
 | |
|   jake.exec = utils.exec;
 | |
| 
 | |
|   // File utils should be aliased directly on base jake as well
 | |
|   Object.assign(jake, utils.file);
 | |
| 
 | |
|   // Also add top-level API methods to exported object for those who don't want to
 | |
|   // use the globals (`file` here will overwrite the 'file' utils namespace)
 | |
|   Object.assign(jake, api);
 | |
| 
 | |
|   Object.assign(jake, new (function () {
 | |
| 
 | |
|     this._invocationChain = [];
 | |
|     this._taskTimeout = 30000;
 | |
| 
 | |
|     // Public properties
 | |
|     // =================
 | |
|     this.version = pkg.version;
 | |
|     // Used when Jake exits with a specific error-code
 | |
|     this.errorCode = null;
 | |
|     // Loads Jakefiles/jakelibdirs
 | |
|     this.loader = loader;
 | |
|     // The root of all ... namespaces
 | |
|     this.rootNamespace = new RootNamespace();
 | |
|     // Non-namespaced tasks are placed into the default
 | |
|     this.defaultNamespace = this.rootNamespace;
 | |
|     // Start in the default
 | |
|     this.currentNamespace = this.defaultNamespace;
 | |
|     // Saves the description created by a 'desc' call that prefaces a
 | |
|     // 'task' call that defines a task.
 | |
|     this.currentTaskDescription = null;
 | |
|     this.program = new Program();
 | |
|     this.FileList = require('filelist').FileList;
 | |
|     this.PackageTask = require('./package_task').PackageTask;
 | |
|     this.PublishTask = require('./publish_task').PublishTask;
 | |
|     this.TestTask = require('./test_task').TestTask;
 | |
|     this.Task = Task;
 | |
|     this.FileTask = FileTask;
 | |
|     this.DirectoryTask = DirectoryTask;
 | |
|     this.Namespace = Namespace;
 | |
|     this.Rule = Rule;
 | |
| 
 | |
|     this.parseAllTasks = function () {
 | |
|       let _parseNs = function (ns) {
 | |
|         let nsTasks = ns.tasks;
 | |
|         let nsNamespaces = ns.childNamespaces;
 | |
|         for (let q in nsTasks) {
 | |
|           let nsTask = nsTasks[q];
 | |
|           jake.Task[nsTask.fullName] = nsTask;
 | |
|         }
 | |
|         for (let p in nsNamespaces) {
 | |
|           let nsNamespace = nsNamespaces[p];
 | |
|           _parseNs(nsNamespace);
 | |
|         }
 | |
|       };
 | |
|       _parseNs(jake.defaultNamespace);
 | |
|     };
 | |
| 
 | |
|     /**
 | |
|      * Displays the list of descriptions available for tasks defined in
 | |
|      * a Jakefile
 | |
|      */
 | |
|     this.showAllTaskDescriptions = function (f) {
 | |
|       let p;
 | |
|       let maxTaskNameLength = 0;
 | |
|       let task;
 | |
|       let padding;
 | |
|       let name;
 | |
|       let descr;
 | |
|       let filter = typeof f == 'string' ? f : null;
 | |
|       let taskParams;
 | |
|       let len;
 | |
| 
 | |
|       for (p in jake.Task) {
 | |
|         if (!Object.prototype.hasOwnProperty.call(jake.Task, p)) {
 | |
|           continue;
 | |
|         }
 | |
|         if (filter && p.indexOf(filter) == -1) {
 | |
|           continue;
 | |
|         }
 | |
|         task = jake.Task[p];
 | |
|         taskParams = task.params;
 | |
| 
 | |
|         // Record the length of the longest task name -- used for
 | |
|         // pretty alignment of the task descriptions
 | |
|         if (task.description) {
 | |
|           len = p.length + taskParams.length;
 | |
|           maxTaskNameLength = len > maxTaskNameLength ?
 | |
|             len : maxTaskNameLength;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       // Print out each entry with descriptions neatly aligned
 | |
|       for (p in jake.Task) {
 | |
|         if (!Object.prototype.hasOwnProperty.call(jake.Task, p)) {
 | |
|           continue;
 | |
|         }
 | |
|         if (filter && p.indexOf(filter) == -1) {
 | |
|           continue;
 | |
|         }
 | |
|         task = jake.Task[p];
 | |
| 
 | |
|         taskParams = "";
 | |
|         if (task.params != "") {
 | |
|           taskParams = "[" + task.params + "]";
 | |
|         }
 | |
| 
 | |
|         //name = '\033[32m' + p + '\033[39m ';
 | |
|         name = chalk.green(p);
 | |
| 
 | |
|         descr = task.description;
 | |
|         if (descr) {
 | |
|           descr = chalk.gray('# ' + descr);
 | |
| 
 | |
|           // Create padding-string with calculated length
 | |
|           padding = (new Array(maxTaskNameLength - p.length - taskParams.length + 4)).join(' ');
 | |
| 
 | |
|           console.log('jake ' + name + taskParams + padding + descr);
 | |
|         }
 | |
|       }
 | |
|     };
 | |
| 
 | |
|     this.createTask = function () {
 | |
|       let args = Array.prototype.slice.call(arguments);
 | |
|       let arg;
 | |
|       let obj;
 | |
|       let task;
 | |
|       let type;
 | |
|       let name;
 | |
|       let action;
 | |
|       let opts = {};
 | |
|       let prereqs = [];
 | |
| 
 | |
|       type = args.shift();
 | |
| 
 | |
|       // name, [deps], [action]
 | |
|       // Name (string) + deps (array) format
 | |
|       if (typeof args[0] == 'string') {
 | |
|         name = args.shift();
 | |
|         if (Array.isArray(args[0])) {
 | |
|           prereqs = args.shift();
 | |
|         }
 | |
|       }
 | |
|       // name:deps, [action]
 | |
|       // Legacy object-literal syntax, e.g.: {'name': ['depA', 'depB']}
 | |
|       else {
 | |
|         obj = args.shift();
 | |
|         for (let p in obj) {
 | |
|           prereqs = prereqs.concat(obj[p]);
 | |
|           name = p;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       // Optional opts/callback or callback/opts
 | |
|       while ((arg = args.shift())) {
 | |
|         if (typeof arg == 'function') {
 | |
|           action = arg;
 | |
|         }
 | |
|         else {
 | |
|           opts = Object.assign(Object.create(null), arg);
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       task = jake.currentNamespace.resolveTask(name);
 | |
|       if (task && !action) {
 | |
|         // Task already exists and no action, just update prereqs, and return it.
 | |
|         task.prereqs = task.prereqs.concat(prereqs);
 | |
|         return task;
 | |
|       }
 | |
| 
 | |
|       switch (type) {
 | |
|       case 'directory':
 | |
|         action = function action() {
 | |
|           jake.mkdirP(name);
 | |
|         };
 | |
|         task = new DirectoryTask(name, prereqs, action, opts);
 | |
|         break;
 | |
|       case 'file':
 | |
|         task = new FileTask(name, prereqs, action, opts);
 | |
|         break;
 | |
|       default:
 | |
|         task = new Task(name, prereqs, action, opts);
 | |
|       }
 | |
| 
 | |
|       jake.currentNamespace.addTask(task);
 | |
| 
 | |
|       if (jake.currentTaskDescription) {
 | |
|         task.description = jake.currentTaskDescription;
 | |
|         jake.currentTaskDescription = null;
 | |
|       }
 | |
| 
 | |
|       // FIXME: Should only need to add a new entry for the current
 | |
|       // task-definition, not reparse the entire structure
 | |
|       jake.parseAllTasks();
 | |
| 
 | |
|       return task;
 | |
|     };
 | |
| 
 | |
|     this.attemptRule = function (name, ns, level) {
 | |
|       let prereqRule;
 | |
|       let prereq;
 | |
|       if (level > MAX_RULE_RECURSION_LEVEL) {
 | |
|         return null;
 | |
|       }
 | |
|       // Check Rule
 | |
|       prereqRule = ns.matchRule(name);
 | |
|       if (prereqRule) {
 | |
|         prereq = prereqRule.createTask(name, level);
 | |
|       }
 | |
|       return prereq || null;
 | |
|     };
 | |
| 
 | |
|     this.createPlaceholderFileTask = function (name, namespace) {
 | |
|       let parsed = name.split(':');
 | |
|       let filePath = parsed.pop(); // Strip any namespace
 | |
|       let task;
 | |
| 
 | |
|       task = namespace.resolveTask(name);
 | |
| 
 | |
|       // If there's not already an existing dummy FileTask for it,
 | |
|       // create one
 | |
|       if (!task) {
 | |
|         // Create a dummy FileTask only if file actually exists
 | |
|         if (fs.existsSync(filePath)) {
 | |
|           task = new jake.FileTask(filePath);
 | |
|           task.dummy = true;
 | |
|           let ns;
 | |
|           if (parsed.length) {
 | |
|             ns = namespace.resolveNamespace(parsed.join(':'));
 | |
|           }
 | |
|           else {
 | |
|             ns = namespace;
 | |
|           }
 | |
|           if (!namespace) {
 | |
|             throw new Error('Invalid namespace, cannot add FileTask');
 | |
|           }
 | |
|           ns.addTask(task);
 | |
|           // Put this dummy Task in the global Tasks list so
 | |
|           // modTime will be eval'd correctly
 | |
|           jake.Task[`${ns.path}:${filePath}`] = task;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       return task || null;
 | |
|     };
 | |
| 
 | |
| 
 | |
|     this.run = function () {
 | |
|       let args = Array.prototype.slice.call(arguments);
 | |
|       let program = this.program;
 | |
|       let loader = this.loader;
 | |
|       let preempt;
 | |
|       let opts;
 | |
| 
 | |
|       program.parseArgs(args);
 | |
|       program.init();
 | |
| 
 | |
|       preempt = program.firstPreemptiveOption();
 | |
|       if (preempt) {
 | |
|         preempt();
 | |
|       }
 | |
|       else {
 | |
|         opts = program.opts;
 | |
|         // jakefile flag set but no jakefile yet
 | |
|         if (opts.autocomplete && opts.jakefile === true) {
 | |
|           process.stdout.write('no-complete');
 | |
|           return;
 | |
|         }
 | |
|         // Load Jakefile and jakelibdir files
 | |
|         let jakefileLoaded = loader.loadFile(opts.jakefile);
 | |
|         let jakelibdirLoaded = loader.loadDirectory(opts.jakelibdir);
 | |
| 
 | |
|         if(!jakefileLoaded && !jakelibdirLoaded && !opts.autocomplete) {
 | |
|           fail('No Jakefile. Specify a valid path with -f/--jakefile, ' +
 | |
|               'or place one in the current directory.');
 | |
|         }
 | |
| 
 | |
|         program.run();
 | |
|       }
 | |
|     };
 | |
| 
 | |
|   })());
 | |
| }
 | |
| 
 | |
| module.exports = jake;
 |