 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>
		
			
				
	
	
		
			204 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			204 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| var fs = require('fs');
 | |
| var util = require('util');
 | |
| var path = require('path');
 | |
| 
 | |
| let shim;
 | |
| class Y18N {
 | |
|     constructor(opts) {
 | |
|         // configurable options.
 | |
|         opts = opts || {};
 | |
|         this.directory = opts.directory || './locales';
 | |
|         this.updateFiles = typeof opts.updateFiles === 'boolean' ? opts.updateFiles : true;
 | |
|         this.locale = opts.locale || 'en';
 | |
|         this.fallbackToLanguage = typeof opts.fallbackToLanguage === 'boolean' ? opts.fallbackToLanguage : true;
 | |
|         // internal stuff.
 | |
|         this.cache = Object.create(null);
 | |
|         this.writeQueue = [];
 | |
|     }
 | |
|     __(...args) {
 | |
|         if (typeof arguments[0] !== 'string') {
 | |
|             return this._taggedLiteral(arguments[0], ...arguments);
 | |
|         }
 | |
|         const str = args.shift();
 | |
|         let cb = function () { }; // start with noop.
 | |
|         if (typeof args[args.length - 1] === 'function')
 | |
|             cb = args.pop();
 | |
|         cb = cb || function () { }; // noop.
 | |
|         if (!this.cache[this.locale])
 | |
|             this._readLocaleFile();
 | |
|         // we've observed a new string, update the language file.
 | |
|         if (!this.cache[this.locale][str] && this.updateFiles) {
 | |
|             this.cache[this.locale][str] = str;
 | |
|             // include the current directory and locale,
 | |
|             // since these values could change before the
 | |
|             // write is performed.
 | |
|             this._enqueueWrite({
 | |
|                 directory: this.directory,
 | |
|                 locale: this.locale,
 | |
|                 cb
 | |
|             });
 | |
|         }
 | |
|         else {
 | |
|             cb();
 | |
|         }
 | |
|         return shim.format.apply(shim.format, [this.cache[this.locale][str] || str].concat(args));
 | |
|     }
 | |
|     __n() {
 | |
|         const args = Array.prototype.slice.call(arguments);
 | |
|         const singular = args.shift();
 | |
|         const plural = args.shift();
 | |
|         const quantity = args.shift();
 | |
|         let cb = function () { }; // start with noop.
 | |
|         if (typeof args[args.length - 1] === 'function')
 | |
|             cb = args.pop();
 | |
|         if (!this.cache[this.locale])
 | |
|             this._readLocaleFile();
 | |
|         let str = quantity === 1 ? singular : plural;
 | |
|         if (this.cache[this.locale][singular]) {
 | |
|             const entry = this.cache[this.locale][singular];
 | |
|             str = entry[quantity === 1 ? 'one' : 'other'];
 | |
|         }
 | |
|         // we've observed a new string, update the language file.
 | |
|         if (!this.cache[this.locale][singular] && this.updateFiles) {
 | |
|             this.cache[this.locale][singular] = {
 | |
|                 one: singular,
 | |
|                 other: plural
 | |
|             };
 | |
|             // include the current directory and locale,
 | |
|             // since these values could change before the
 | |
|             // write is performed.
 | |
|             this._enqueueWrite({
 | |
|                 directory: this.directory,
 | |
|                 locale: this.locale,
 | |
|                 cb
 | |
|             });
 | |
|         }
 | |
|         else {
 | |
|             cb();
 | |
|         }
 | |
|         // if a %d placeholder is provided, add quantity
 | |
|         // to the arguments expanded by util.format.
 | |
|         const values = [str];
 | |
|         if (~str.indexOf('%d'))
 | |
|             values.push(quantity);
 | |
|         return shim.format.apply(shim.format, values.concat(args));
 | |
|     }
 | |
|     setLocale(locale) {
 | |
|         this.locale = locale;
 | |
|     }
 | |
|     getLocale() {
 | |
|         return this.locale;
 | |
|     }
 | |
|     updateLocale(obj) {
 | |
|         if (!this.cache[this.locale])
 | |
|             this._readLocaleFile();
 | |
|         for (const key in obj) {
 | |
|             if (Object.prototype.hasOwnProperty.call(obj, key)) {
 | |
|                 this.cache[this.locale][key] = obj[key];
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     _taggedLiteral(parts, ...args) {
 | |
|         let str = '';
 | |
|         parts.forEach(function (part, i) {
 | |
|             const arg = args[i + 1];
 | |
|             str += part;
 | |
|             if (typeof arg !== 'undefined') {
 | |
|                 str += '%s';
 | |
|             }
 | |
|         });
 | |
|         return this.__.apply(this, [str].concat([].slice.call(args, 1)));
 | |
|     }
 | |
|     _enqueueWrite(work) {
 | |
|         this.writeQueue.push(work);
 | |
|         if (this.writeQueue.length === 1)
 | |
|             this._processWriteQueue();
 | |
|     }
 | |
|     _processWriteQueue() {
 | |
|         const _this = this;
 | |
|         const work = this.writeQueue[0];
 | |
|         // destructure the enqueued work.
 | |
|         const directory = work.directory;
 | |
|         const locale = work.locale;
 | |
|         const cb = work.cb;
 | |
|         const languageFile = this._resolveLocaleFile(directory, locale);
 | |
|         const serializedLocale = JSON.stringify(this.cache[locale], null, 2);
 | |
|         shim.fs.writeFile(languageFile, serializedLocale, 'utf-8', function (err) {
 | |
|             _this.writeQueue.shift();
 | |
|             if (_this.writeQueue.length > 0)
 | |
|                 _this._processWriteQueue();
 | |
|             cb(err);
 | |
|         });
 | |
|     }
 | |
|     _readLocaleFile() {
 | |
|         let localeLookup = {};
 | |
|         const languageFile = this._resolveLocaleFile(this.directory, this.locale);
 | |
|         try {
 | |
|             // When using a bundler such as webpack, readFileSync may not be defined:
 | |
|             if (shim.fs.readFileSync) {
 | |
|                 localeLookup = JSON.parse(shim.fs.readFileSync(languageFile, 'utf-8'));
 | |
|             }
 | |
|         }
 | |
|         catch (err) {
 | |
|             if (err instanceof SyntaxError) {
 | |
|                 err.message = 'syntax error in ' + languageFile;
 | |
|             }
 | |
|             if (err.code === 'ENOENT')
 | |
|                 localeLookup = {};
 | |
|             else
 | |
|                 throw err;
 | |
|         }
 | |
|         this.cache[this.locale] = localeLookup;
 | |
|     }
 | |
|     _resolveLocaleFile(directory, locale) {
 | |
|         let file = shim.resolve(directory, './', locale + '.json');
 | |
|         if (this.fallbackToLanguage && !this._fileExistsSync(file) && ~locale.lastIndexOf('_')) {
 | |
|             // attempt fallback to language only
 | |
|             const languageFile = shim.resolve(directory, './', locale.split('_')[0] + '.json');
 | |
|             if (this._fileExistsSync(languageFile))
 | |
|                 file = languageFile;
 | |
|         }
 | |
|         return file;
 | |
|     }
 | |
|     _fileExistsSync(file) {
 | |
|         return shim.exists(file);
 | |
|     }
 | |
| }
 | |
| function y18n$1(opts, _shim) {
 | |
|     shim = _shim;
 | |
|     const y18n = new Y18N(opts);
 | |
|     return {
 | |
|         __: y18n.__.bind(y18n),
 | |
|         __n: y18n.__n.bind(y18n),
 | |
|         setLocale: y18n.setLocale.bind(y18n),
 | |
|         getLocale: y18n.getLocale.bind(y18n),
 | |
|         updateLocale: y18n.updateLocale.bind(y18n),
 | |
|         locale: y18n.locale
 | |
|     };
 | |
| }
 | |
| 
 | |
| var nodePlatformShim = {
 | |
|     fs: {
 | |
|         readFileSync: fs.readFileSync,
 | |
|         writeFile: fs.writeFile
 | |
|     },
 | |
|     format: util.format,
 | |
|     resolve: path.resolve,
 | |
|     exists: (file) => {
 | |
|         try {
 | |
|             return fs.statSync(file).isFile();
 | |
|         }
 | |
|         catch (err) {
 | |
|             return false;
 | |
|         }
 | |
|     }
 | |
| };
 | |
| 
 | |
| const y18n = (opts) => {
 | |
|     return y18n$1(opts, nodePlatformShim);
 | |
| };
 | |
| 
 | |
| module.exports = y18n;
 |