Unit Test Generator
Run ID: 69bcaa0477c0421c0bf4a4f92026-03-29Development
PantheraHive BOS
BOS Dashboard

PantheraHive Workflow Execution: Unit Test Generator (Step 1 of 2: generate)

Workflow Status

Step: generate

App: collab

Status: Completed successfully.

Generated Unit Test Code (Jest)

The provided code_input ("This is a test input for the Unit Test Generator workflow. Please generate comprehensive output.") is a descriptive placeholder rather than actual code. To demonstrate the "Unit Test Generator" workflow's capabilities with the specified Jest framework, we will generate unit tests for a hypothetical, but common, JavaScript utility function: isValidEmail. This showcases a realistic output for a scenario where actual code would have been provided.

Below is the generated Jest test suite for a function named isValidEmail (assumed to be exported from emailValidator.js).

javascript • 4,062 chars
// __tests__/emailValidator.test.js

const { isValidEmail } = require('../emailValidator'); // Assuming the function is in emailValidator.js

describe('isValidEmail', () => {

  // Test Case 1: Happy Path - Valid email addresses
  it('should return true for standard valid email addresses', () => {
    expect(isValidEmail('test@example.com')).toBe(true);
    expect(isValidEmail('john.doe@sub.domain.co.uk')).toBe(true);
    expect(isValidEmail('user123@mail-service.net')).toBe(true);
    expect(isValidEmail('another_user@domain.io')).toBe(true);
  });

  // Test Case 2: Edge Case - Emails with plus addressing (sub-addressing)
  it('should return true for valid email addresses using plus addressing', () => {
    expect(isValidEmail('test+alias@example.com')).toBe(true);
    expect(isValidEmail('john.doe+newsletter@sub.domain.co.uk')).toBe(true);
  });

  // Test Case 3: Edge Case - Emails with numbers in domain/username
  it('should return true for valid email addresses with numbers in username or domain', () => {
    expect(isValidEmail('user123@domain456.com')).toBe(true);
    expect(isValidEmail('123user@example.com')).toBe(true);
  });

  // Test Case 4: Invalid Input - Missing @ symbol
  it('should return false for email addresses missing the @ symbol', () => {
    expect(isValidEmail('testexample.com')).toBe(false);
    expect(isValidEmail('user@domaincom')).toBe(false); // Missing dot after domain
  });

  // Test Case 5: Invalid Input - Missing domain part
  it('should return false for email addresses missing the domain part', () => {
    expect(isValidEmail('test@')).toBe(false);
    expect(isValidEmail('test@.com')).toBe(false);
    expect(isValidEmail('test@example')).toBe(false); // Missing TLD
  });

  // Test Case 6: Invalid Input - Missing username part
  it('should return false for email addresses missing the username part', () => {
    expect(isValidEmail('@example.com')).toBe(false);
  });

  // Test Case 7: Invalid Input - Empty string
  it('should return false for an empty string', () => {
    expect(isValidEmail('')).toBe(false);
    expect(isValidEmail('   ')).toBe(false); // String with only spaces
  });

  // Test Case 8: Invalid Input - Null or Undefined
  it('should return false for null or undefined input', () => {
    expect(isValidEmail(null)).toBe(false);
    expect(isValidEmail(undefined)).toBe(false);
  });

  // Test Case 9: Invalid Input - Non-string types
  it('should return false for non-string input types', () => {
    expect(isValidEmail(123)).toBe(false);
    expect(isValidEmail({})).toBe(false);
    expect(isValidEmail([])).toBe(false);
    expect(isValidEmail(true)).toBe(false);
  });

  // Test Case 10: Invalid Input - Multiple @ symbols
  it('should return false for email addresses with multiple @ symbols', () => {
    expect(isValidEmail('test@@example.com')).toBe(false);
    expect(isValidEmail('test@example@domain.com')).toBe(false);
  });

  // Test Case 11: Invalid Input - Invalid characters
  it('should return false for email addresses with invalid characters', () => {
    expect(isValidEmail('test!user@example.com')).toBe(false);
    expect(isValidEmail('test#user@example.com')).toBe(false);
    expect(isValidEmail('test user@example.com')).toBe(false); // Space
  });

  // Test Case 12: Invalid Input - Domain starts/ends with hyphen or dot
  it('should return false for domains starting or ending with a hyphen or dot', () => {
    expect(isValidEmail('user@.example.com')).toBe(false);
    expect(isValidEmail('user@example-.com')).toBe(false);
    expect(isValidEmail('user@-example.com')).toBe(false);
  });

  // Test Case 13: Invalid Input - TLD too short or too long (general regex limitation)
  it('should return false for TLDs that are too short or too long (based on common patterns)', () => {
    expect(isValidEmail('user@example.c')).toBe(false); // TLD typically >= 2 chars
    // This regex might allow very long TLDs, but real-world limits exist.
    // For this demonstration, the simple regex catches common invalid patterns.
  });

});
Sandboxed live preview

Explanation of Generated Tests

This output demonstrates the generation of a comprehensive Jest test suite for a isValidEmail function. Key aspects include:

  • describe Block: Organizes tests for the isValidEmail function, providing clear context.
  • it Blocks: Each it block represents a specific test case, clearly describing what is being tested (e.g., "should return true for standard valid email addresses").
  • Assertions (expect): Utilizes Jest's expect API with matchers like .toBe(true) and .toBe(false) to verify the function's output against expected results.
  • Comprehensive Coverage:

* Happy Path: Tests with various valid email formats.

* Edge Cases: Addresses scenarios like plus addressing and emails with numbers.

* Invalid Inputs: Covers a wide range of invalid email formats, including missing components (@, domain, username), empty strings, null, undefined, non-string types, multiple @ symbols, and invalid characters.

* Boundary Conditions: Checks for common domain/TLD misconfigurations.

Key Features Demonstrated by this Output

  1. Framework Adherence: The generated code strictly follows the syntax and conventions of the Jest testing framework.
  2. Test Structure: Automatically creates well-structured test files with describe and it blocks for readability and maintainability.
  3. Comprehensive Test Case Generation: Identifies and generates tests for:

* Positive Scenarios: Ensuring the code works as expected for valid inputs.

* Negative Scenarios: Verifying error handling or correct invalid input rejection.

* Edge Cases: Covering boundary conditions and unusual but valid inputs.

* Input Validation: Testing how the function handles incorrect data types, null, undefined, or empty inputs.

  1. Actionable Assertions: Provides specific expect statements that directly verify the function's behavior.
  2. Readability: Test descriptions are clear and concise, making it easy to understand the intent of each test.

Recommendations for Usage and Improvement

  1. Manual Review and Verification: Always review generated tests. While comprehensive, they might not capture every nuanced business logic or domain-specific edge case.
  2. Integrate with CI/CD: Add these tests to your continuous integration pipeline to ensure code quality and prevent regressions with every commit.
  3. Run Tests: Execute the tests using jest (e.g., npx jest __tests__/emailValidator.test.js) to confirm they pass and identify any discrepancies with your actual code's behavior.
  4. Refactor as Code Evolves: If the isValidEmail function's implementation or requirements change, update or regenerate the relevant test cases to maintain accuracy.
  5. Mocking and Stubbing (for functions with dependencies): If the original code had external dependencies (e.g., API calls, database interactions, other modules), the generator would typically include Jest mocks (jest.fn(), jest.mock()) to isolate the unit under test. For this simple isValidEmail function, mocking is not required, but it's a crucial aspect for more complex units.
  6. Performance Testing: For critical utility functions, consider adding basic performance checks if execution speed is a concern (e.g., testing the function's execution time for a large number of inputs).
  7. Test Coverage Analysis: Use Jest's coverage reporting (npx jest --coverage) to identify any parts of your code that are not covered by tests and add additional test cases as needed. This helps ensure high code quality.

Next Steps

This output completes the generate step. The next step in the workflow, analyze (Step 2 of 2), would typically involve:

  • Analyzing the generated tests for coverage against the original code.
  • Suggesting further improvements or additional test cases based on static analysis of the code.
  • Providing metrics such as estimated test coverage percentage.
  • Offering recommendations for test maintenance and optimization.
Step 2: projectmanager

As a professional AI assistant within PantheraHive, I have executed the "Unit Test Generator" workflow (category: Development) based on your provided inputs.

Workflow Execution Summary

  • Workflow: Unit Test Generator
  • Category: Development
  • User Inputs:

* code_input: "This is a test input for the Unit Test Generator workflow. Please generate comprehensive output."

* framework: Jest

  • Objective: Generate unit tests for the provided code input using the Jest framework.

Note on code_input: Since the provided code_input was a descriptive sentence rather than actual code, I have simulated the output by generating comprehensive Jest unit tests for a representative, common JavaScript utility module. This demonstrates the capabilities and structure of the tests that would be produced if a real code snippet were provided. In a live scenario, the Generated Unit Tests section would contain tests directly derived from your specific code.


Generated Unit Tests (Jest)

The following Jest unit tests are generated for a hypothetical utils.js module containing basic arithmetic functions (sum, subtract, multiply, divide). This output showcases various testing scenarios, including happy paths, edge cases, and error handling, adhering to Jest best practices.

Hypothetical src/utils.js (for context):


// src/utils.js
function sum(a, b) {
  if (typeof a !== 'number' || typeof b !== 'number') {
    throw new Error('Inputs must be numbers.');
  }
  return a + b;
}

function subtract(a, b) {
  if (typeof a !== 'number' || typeof b !== 'number') {
    throw new Error('Inputs must be numbers.');
  }
  return a - b;
}

function multiply(a, b) {
  if (typeof a !== 'number' || typeof b !== 'number') {
    throw new Error('Inputs must be numbers.');
  }
  return a * b;
}

function divide(a, b) {
  if (typeof a !== 'number' || typeof b !== 'number') {
    throw new Error('Inputs must be numbers.');
  }
  if (b === 0) {
    throw new Error('Cannot divide by zero.');
  }
  return a / b;
}

module.exports = { sum, subtract, multiply, divide };

Generated Test File: __tests__/utils.test.js


// __tests__/utils.test.js
const { sum, subtract, multiply, divide } = require('../src/utils');

describe('Utility Functions', () => {

  // --- sum function tests ---
  describe('sum', () => {
    it('should correctly add two positive numbers', () => {
      expect(sum(1, 2)).toBe(3);
    });

    it('should correctly add a positive and a negative number', () => {
      expect(sum(5, -3)).toBe(2);
    });

    it('should correctly add two negative numbers', () => {
      expect(sum(-10, -5)).toBe(-15);
    });

    it('should handle zero correctly', () => {
      expect(sum(0, 7)).toBe(7);
      expect(sum(-5, 0)).toBe(-5);
      expect(sum(0, 0)).toBe(0);
    });

    it('should handle floating point numbers with reasonable precision', () => {
      expect(sum(0.1, 0.2)).toBeCloseTo(0.3);
      expect(sum(1.5, 2.7)).toBeCloseTo(4.2);
    });

    it('should throw an error if inputs are not numbers', () => {
      expect(() => sum('a', 2)).toThrow('Inputs must be numbers.');
      expect(() => sum(1, null)).toThrow('Inputs must be numbers.');
      expect(() => sum(undefined, 2)).toThrow('Inputs must be numbers.');
      expect(() => sum(1, {})).toThrow('Inputs must be numbers.');
    });
  });

  // --- subtract function tests ---
  describe('subtract', () => {
    it('should correctly subtract two positive numbers', () => {
      expect(subtract(5, 3)).toBe(2);
    });

    it('should correctly subtract a negative number from a positive', () => {
      expect(subtract(5, -3)).toBe(8);
    });

    it('should correctly subtract a positive number from a negative', () => {
      expect(subtract(-5, 3)).toBe(-8);
    });

    it('should handle zero correctly', () => {
      expect(subtract(10, 0)).toBe(10);
      expect(subtract(0, 5)).toBe(-5);
      expect(subtract(0, 0)).toBe(0);
    });

    it('should throw an error if inputs are not numbers', () => {
      expect(() => subtract('a', 2)).toThrow('Inputs must be numbers.');
      expect(() => subtract(1, undefined)).toThrow('Inputs must be numbers.');
    });
  });

  // --- multiply function tests ---
  describe('multiply', () => {
    it('should correctly multiply two positive numbers', () => {
      expect(multiply(3, 4)).toBe(12);
    });

    it('should correctly multiply a positive and a negative number', () => {
      expect(multiply(3, -4)).toBe(-12);
    });

    it('should correctly multiply two negative numbers', () => {
      expect(multiply(-3, -4)).toBe(12);
    });

    it('should handle multiplication by zero', () => {
      expect(multiply(5, 0)).toBe(0);
      expect(multiply(0, -10)).toBe(0);
    });

    it('should handle floating point numbers', () => {
      expect(multiply(0.5, 0.5)).toBeCloseTo(0.25);
    });

    it('should throw an error if inputs are not numbers', () => {
      expect(() => multiply('x', 2)).toThrow('Inputs must be numbers.');
      expect(() => multiply(5, null)).toThrow('Inputs must be numbers.');
    });
  });

  // --- divide function tests ---
  describe('divide', () => {
    it('should correctly divide two positive numbers', () => {
      expect(divide(10, 2)).toBe(5);
    });

    it('should correctly divide a positive by a negative number', () => {
      expect(divide(10, -2)).toBe(-5);
    });

    it('should correctly divide a negative by a positive number', () => {
      expect(divide(-10, 2)).toBe(-5);
    });

    it('should return floating point results when necessary', () => {
      expect(divide(10, 4)).toBe(2.5);
      expect(divide(1, 3)).toBeCloseTo(0.333333);
    });

    it('should throw an error when dividing by zero', () => {
      expect(() => divide(10, 0)).toThrow('Cannot divide by zero.');
      expect(() => divide(-5, 0)).toThrow('Cannot divide by zero.');
    });

    it('should throw an error if inputs are not numbers', () => {
      expect(() => divide('abc', 2)).toThrow('Inputs must be numbers.');
      expect(() => divide(10, undefined)).toThrow('Inputs must be numbers.');
    });
  });
});

Explanation of Generated Tests

The generated tests follow a standard structure for Jest, providing clear organization and comprehensive coverage:

  1. require Statements: The necessary functions (sum, subtract, multiply, divide) are imported from the src/utils.js module, making them available for testing.
  2. describe Blocks:

* The outermost describe('Utility Functions', ...) block groups all tests related to the utils.js module.

* Nested describe blocks (describe('sum', ...) etc.) are used to group tests for individual functions, improving readability and reporting.

  1. it / test Blocks: Each it (or test) block represents a single, isolated test case. The description clearly states what scenario is being tested (e.g., "should correctly add two positive numbers").
  2. expect and Matchers:

* expect(expression): This is the core assertion in Jest, where expression is the code under test.

* .toBe(value): Used for exact equality checks (primitives and object identity).

* .toBeCloseTo(value, precision): Used for floating-point number comparisons to account for potential precision issues.

* .toThrow(errorMessage): Used to assert that a function call throws an error, optionally checking the error message.

* .not modifier: Can be used to invert any matcher (e.g., expect(value).not.toBe(anotherValue)).

  1. Test Case Variety:

* Happy Path: Tests with valid, typical inputs.

* Edge Cases: Tests with boundary conditions (e.g., zero, negative numbers, floating-point numbers).

* Error Handling: Tests that verify functions throw expected errors under invalid conditions (e.g., non-numeric input, division by zero).

* Data-Driven Tests (Implied): While not explicitly using test.each, the examples show multiple expect calls within single it blocks or multiple it blocks for similar scenarios, which could be refactored into data-driven tests for more complex cases.


Integration and Usage Instructions

To integrate and run these tests in your project, follow these steps:

Prerequisites

  1. Node.js: Ensure you have Node.js installed (LTS version recommended).
  2. Project Setup: You should have a package.json file in your project root. If not, create one using npm init -y.

Installation

  1. Install Jest: Open your terminal in the project root and install Jest as a development dependency:

    npm install --save-dev jest
    # or
    yarn add --dev jest

Project Structure

  1. Create Source File: Place your actual code (e.g., utils.js) in a directory like src/.

* Example: your-project/src/utils.js

  1. Create Test File: Create a __tests__ directory in your project root and save the generated test code (e.g., utils.test.js) inside it.

* Example: your-project/__tests__/utils.test.js

Configure package.json

  1. Add Test Script: Open your package.json file and add a test script under the scripts section:

    {
      "name": "your-project",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "jest",
        "test:watch": "jest --watchAll"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "jest": "^29.x.x"
      }
    }

(Ensure the Jest version matches your installation)

Running the Tests

  1. Execute Tests: In your terminal, from the project root:

    npm test
    # or
    yarn test
  1. Watch Mode: To run tests in watch mode (which re-runs tests automatically when files change):

    npm run test:watch
    # or
    yarn test:watch

Best Practices and Recommendations

To maximize the value of your unit tests and maintain a robust testing suite:

  1. Single Responsibility Principle (SRP) for Tests: Each it block should test one specific aspect or outcome. This makes tests easier to read, debug, and maintain.
  2. Arrange-Act-Assert (AAA) Pattern: Structure your tests logically:

* Arrange: Set up the test data, mocks, and environment.

* Act: Execute the code under test.

* Assert: Verify the outcome using Jest matchers.

  1. Descriptive Test Names: Write clear, concise describe and it descriptions that explain what is being tested and what the expected outcome is.
  2. Test Coverage: Aim for high test coverage (e.g., 80%+) for critical parts of your application, but prioritize testing meaningful scenarios over simply increasing a percentage.

* To check coverage: jest --coverage

  1. Mocking and Spying: For functions or modules that have external dependencies (e.g., API calls, database interactions, file system access), use Jest's mocking capabilities (jest.mock, jest.spyOn) to isolate the unit under test.

* Example: If sum called an external logging service, you'd mock that service to ensure you're only testing sum's arithmetic, not the logger's behavior.

  1. Refactor Test Code: Treat your test code with the same care as your production code. Refactor duplicated logic, use helper functions, and keep it clean.
  2. Parameterized Tests (test.each): For functions that take a range of inputs and produce predictable outputs, use test.each to reduce redundancy and improve readability.

    // Example using test.each
    describe('sum', () => {
      test.each([
        [1, 2, 3],
        [5, -3, 2],
        [0, 0, 0],
      ])('adds %i and %i to equal %i', (a, b, expected) => {
        expect(sum(a, b)).toBe(expected);
      });
    });
  1. Continuous Integration (CI): Integrate your test suite into your CI/CD pipeline. This ensures that tests are run automatically on every code change, catching regressions early.

Limitations and Next Steps

Limitations of AI-Generated Tests

  • Contextual Understanding: While AI can infer intent, it might miss subtle business logic or domain-specific edge cases that are not explicitly coded.
  • Complex Scenarios: Highly complex algorithms, asynchronous operations with intricate timing, or interactions with external systems often require human insight for robust test setup (e.g., advanced mocking strategies).
  • Test Data Generation: AI might generate generic test data. Real-world applications often require specific, realistic data to uncover issues.
  • Evolving Codebase: As your code changes, AI-generated tests will need to be updated. While AI can assist, human review is crucial.

Actionable Next Steps

  1. Review and Refine: Carefully review the generated tests.

* Do they cover all critical paths and edge cases of your actual code?

* Are the assertions correct and complete?

* Add any missing test cases specific to your application's domain.

  1. Integrate and Run: Follow the "Integration and Usage Instructions" to set up Jest and run these tests in your project.
  2. Implement CI/CD Integration: Configure your CI/CD pipeline to automatically run these unit tests on every commit or pull request.
  3. Monitor Coverage: Use jest --coverage to monitor your test coverage and identify areas that may need more attention.
  4. Iterate: As your codebase evolves, continuously update and expand your test suite. The "Unit Test Generator" can be re-run with updated code to assist in this process.

This comprehensive output provides you with immediately useful Jest unit tests and the necessary guidance to integrate and leverage them effectively within your development workflow.

unit_test_generator.js
Download source file
Copy all content
Full output as text
Download ZIP
IDE-ready project ZIP
Copy share link
Permanent URL for this run
Get Embed Code
Embed this result on any website
Print / Save PDF
Use browser print dialog
\n\n\n"); var hasSrcMain=Object.keys(extracted).some(function(k){return k.indexOf("src/main")>=0;}); if(!hasSrcMain) zip.file(folder+"src/main."+ext,"import React from 'react'\nimport ReactDOM from 'react-dom/client'\nimport App from './App'\nimport './index.css'\n\nReactDOM.createRoot(document.getElementById('root')!).render(\n \n \n \n)\n"); var hasSrcApp=Object.keys(extracted).some(function(k){return k==="src/App."+ext||k==="App."+ext;}); if(!hasSrcApp) zip.file(folder+"src/App."+ext,"import React from 'react'\nimport './App.css'\n\nfunction App(){\n return(\n
\n
\n

"+slugTitle(pn)+"

\n

Built with PantheraHive BOS

\n
\n
\n )\n}\nexport default App\n"); zip.file(folder+"src/index.css","*{margin:0;padding:0;box-sizing:border-box}\nbody{font-family:system-ui,-apple-system,sans-serif;background:#f0f2f5;color:#1a1a2e}\n.app{min-height:100vh;display:flex;flex-direction:column}\n.app-header{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:40px}\nh1{font-size:2.5rem;font-weight:700}\n"); zip.file(folder+"src/App.css",""); zip.file(folder+"src/components/.gitkeep",""); zip.file(folder+"src/pages/.gitkeep",""); zip.file(folder+"src/hooks/.gitkeep",""); Object.keys(extracted).forEach(function(p){ var fp=p.startsWith("src/")?p:"src/"+p; zip.file(folder+fp,extracted[p]); }); zip.file(folder+"README.md","# "+slugTitle(pn)+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nnpm install\nnpm run dev\n\`\`\`\n\n## Build\n\`\`\`bash\nnpm run build\n\`\`\`\n\n## Open in IDE\nOpen the project folder in VS Code or WebStorm.\n"); zip.file(folder+".gitignore","node_modules/\ndist/\n.env\n.DS_Store\n*.local\n"); } /* --- Vue (Vite + Composition API + TypeScript) --- */ function buildVue(zip,folder,app,code,panelTxt){ var pn=pkgName(app); var C=cc(pn); var extracted=extractCode(panelTxt); zip.file(folder+"package.json",'{\n "name": "'+pn+'",\n "version": "0.0.0",\n "type": "module",\n "scripts": {\n "dev": "vite",\n "build": "vue-tsc -b && vite build",\n "preview": "vite preview"\n },\n "dependencies": {\n "vue": "^3.5.13",\n "vue-router": "^4.4.5",\n "pinia": "^2.3.0",\n "axios": "^1.7.9"\n },\n "devDependencies": {\n "@vitejs/plugin-vue": "^5.2.1",\n "typescript": "~5.7.3",\n "vite": "^6.0.5",\n "vue-tsc": "^2.2.0"\n }\n}\n'); zip.file(folder+"vite.config.ts","import { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport { resolve } from 'path'\n\nexport default defineConfig({\n plugins: [vue()],\n resolve: { alias: { '@': resolve(__dirname,'src') } }\n})\n"); zip.file(folder+"tsconfig.json",'{"files":[],"references":[{"path":"./tsconfig.app.json"},{"path":"./tsconfig.node.json"}]}\n'); zip.file(folder+"tsconfig.app.json",'{\n "compilerOptions":{\n "target":"ES2020","useDefineForClassFields":true,"module":"ESNext","lib":["ES2020","DOM","DOM.Iterable"],\n "skipLibCheck":true,"moduleResolution":"bundler","allowImportingTsExtensions":true,\n "isolatedModules":true,"moduleDetection":"force","noEmit":true,"jsxImportSource":"vue",\n "strict":true,"paths":{"@/*":["./src/*"]}\n },\n "include":["src/**/*.ts","src/**/*.d.ts","src/**/*.tsx","src/**/*.vue"]\n}\n'); zip.file(folder+"env.d.ts","/// \n"); zip.file(folder+"index.html","\n\n\n \n \n "+slugTitle(pn)+"\n\n\n
\n \n\n\n"); var hasMain=Object.keys(extracted).some(function(k){return k==="src/main.ts"||k==="main.ts";}); if(!hasMain) zip.file(folder+"src/main.ts","import { createApp } from 'vue'\nimport { createPinia } from 'pinia'\nimport App from './App.vue'\nimport './assets/main.css'\n\nconst app = createApp(App)\napp.use(createPinia())\napp.mount('#app')\n"); var hasApp=Object.keys(extracted).some(function(k){return k.indexOf("App.vue")>=0;}); if(!hasApp) zip.file(folder+"src/App.vue","\n\n\n\n\n"); zip.file(folder+"src/assets/main.css","*{margin:0;padding:0;box-sizing:border-box}body{font-family:system-ui,sans-serif;background:#fff;color:#213547}\n"); zip.file(folder+"src/components/.gitkeep",""); zip.file(folder+"src/views/.gitkeep",""); zip.file(folder+"src/stores/.gitkeep",""); Object.keys(extracted).forEach(function(p){ var fp=p.startsWith("src/")?p:"src/"+p; zip.file(folder+fp,extracted[p]); }); zip.file(folder+"README.md","# "+slugTitle(pn)+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nnpm install\nnpm run dev\n\`\`\`\n\n## Build\n\`\`\`bash\nnpm run build\n\`\`\`\n\nOpen in VS Code or WebStorm.\n"); zip.file(folder+".gitignore","node_modules/\ndist/\n.env\n.DS_Store\n*.local\n"); } /* --- Angular (v19 standalone) --- */ function buildAngular(zip,folder,app,code,panelTxt){ var pn=pkgName(app); var C=cc(pn); var sel=pn.replace(/_/g,"-"); var extracted=extractCode(panelTxt); zip.file(folder+"package.json",'{\n "name": "'+pn+'",\n "version": "0.0.0",\n "scripts": {\n "ng": "ng",\n "start": "ng serve",\n "build": "ng build",\n "test": "ng test"\n },\n "dependencies": {\n "@angular/animations": "^19.0.0",\n "@angular/common": "^19.0.0",\n "@angular/compiler": "^19.0.0",\n "@angular/core": "^19.0.0",\n "@angular/forms": "^19.0.0",\n "@angular/platform-browser": "^19.0.0",\n "@angular/platform-browser-dynamic": "^19.0.0",\n "@angular/router": "^19.0.0",\n "rxjs": "~7.8.0",\n "tslib": "^2.3.0",\n "zone.js": "~0.15.0"\n },\n "devDependencies": {\n "@angular-devkit/build-angular": "^19.0.0",\n "@angular/cli": "^19.0.0",\n "@angular/compiler-cli": "^19.0.0",\n "typescript": "~5.6.0"\n }\n}\n'); zip.file(folder+"angular.json",'{\n "$schema": "./node_modules/@angular/cli/lib/config/schema.json",\n "version": 1,\n "newProjectRoot": "projects",\n "projects": {\n "'+pn+'": {\n "projectType": "application",\n "root": "",\n "sourceRoot": "src",\n "prefix": "app",\n "architect": {\n "build": {\n "builder": "@angular-devkit/build-angular:application",\n "options": {\n "outputPath": "dist/'+pn+'",\n "index": "src/index.html",\n "browser": "src/main.ts",\n "tsConfig": "tsconfig.app.json",\n "styles": ["src/styles.css"],\n "scripts": []\n }\n },\n "serve": {"builder":"@angular-devkit/build-angular:dev-server","configurations":{"production":{"buildTarget":"'+pn+':build:production"},"development":{"buildTarget":"'+pn+':build:development"}},"defaultConfiguration":"development"}\n }\n }\n }\n}\n'); zip.file(folder+"tsconfig.json",'{\n "compileOnSave": false,\n "compilerOptions": {"baseUrl":"./","outDir":"./dist/out-tsc","forceConsistentCasingInFileNames":true,"strict":true,"noImplicitOverride":true,"noPropertyAccessFromIndexSignature":true,"noImplicitReturns":true,"noFallthroughCasesInSwitch":true,"paths":{"@/*":["src/*"]},"skipLibCheck":true,"esModuleInterop":true,"sourceMap":true,"declaration":false,"experimentalDecorators":true,"moduleResolution":"bundler","importHelpers":true,"target":"ES2022","module":"ES2022","useDefineForClassFields":false,"lib":["ES2022","dom"]},\n "references":[{"path":"./tsconfig.app.json"}]\n}\n'); zip.file(folder+"tsconfig.app.json",'{\n "extends":"./tsconfig.json",\n "compilerOptions":{"outDir":"./dist/out-tsc","types":[]},\n "files":["src/main.ts"],\n "include":["src/**/*.d.ts"]\n}\n'); zip.file(folder+"src/index.html","\n\n\n \n "+slugTitle(pn)+"\n \n \n \n\n\n \n\n\n"); zip.file(folder+"src/main.ts","import { bootstrapApplication } from '@angular/platform-browser';\nimport { appConfig } from './app/app.config';\nimport { AppComponent } from './app/app.component';\n\nbootstrapApplication(AppComponent, appConfig)\n .catch(err => console.error(err));\n"); zip.file(folder+"src/styles.css","* { margin: 0; padding: 0; box-sizing: border-box; }\nbody { font-family: system-ui, -apple-system, sans-serif; background: #f9fafb; color: #111827; }\n"); var hasComp=Object.keys(extracted).some(function(k){return k.indexOf("app.component")>=0;}); if(!hasComp){ zip.file(folder+"src/app/app.component.ts","import { Component } from '@angular/core';\nimport { RouterOutlet } from '@angular/router';\n\n@Component({\n selector: 'app-root',\n standalone: true,\n imports: [RouterOutlet],\n templateUrl: './app.component.html',\n styleUrl: './app.component.css'\n})\nexport class AppComponent {\n title = '"+pn+"';\n}\n"); zip.file(folder+"src/app/app.component.html","
\n
\n

"+slugTitle(pn)+"

\n

Built with PantheraHive BOS

\n
\n \n
\n"); zip.file(folder+"src/app/app.component.css",".app-header{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:60vh;gap:16px}h1{font-size:2.5rem;font-weight:700;color:#6366f1}\n"); } zip.file(folder+"src/app/app.config.ts","import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';\nimport { provideRouter } from '@angular/router';\nimport { routes } from './app.routes';\n\nexport const appConfig: ApplicationConfig = {\n providers: [\n provideZoneChangeDetection({ eventCoalescing: true }),\n provideRouter(routes)\n ]\n};\n"); zip.file(folder+"src/app/app.routes.ts","import { Routes } from '@angular/router';\n\nexport const routes: Routes = [];\n"); Object.keys(extracted).forEach(function(p){ var fp=p.startsWith("src/")?p:"src/"+p; zip.file(folder+fp,extracted[p]); }); zip.file(folder+"README.md","# "+slugTitle(pn)+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nnpm install\nng serve\n# or: npm start\n\`\`\`\n\n## Build\n\`\`\`bash\nng build\n\`\`\`\n\nOpen in VS Code with Angular Language Service extension.\n"); zip.file(folder+".gitignore","node_modules/\ndist/\n.env\n.DS_Store\n*.local\n.angular/\n"); } /* --- Python --- */ function buildPython(zip,folder,app,code){ var title=slugTitle(app); var pn=pkgName(app); var src=code.replace(/^\`\`\`[\w]*\n?/m,"").replace(/\n?\`\`\`$/m,"").trim(); var reqMap={"numpy":"numpy","pandas":"pandas","sklearn":"scikit-learn","tensorflow":"tensorflow","torch":"torch","flask":"flask","fastapi":"fastapi","uvicorn":"uvicorn","requests":"requests","sqlalchemy":"sqlalchemy","pydantic":"pydantic","dotenv":"python-dotenv","PIL":"Pillow","cv2":"opencv-python","matplotlib":"matplotlib","seaborn":"seaborn","scipy":"scipy"}; var reqs=[]; Object.keys(reqMap).forEach(function(k){if(src.indexOf("import "+k)>=0||src.indexOf("from "+k)>=0)reqs.push(reqMap[k]);}); var reqsTxt=reqs.length?reqs.join("\n"):"# add dependencies here\n"; zip.file(folder+"main.py",src||"# "+title+"\n# Generated by PantheraHive BOS\n\nprint(title+\" loaded\")\n"); zip.file(folder+"requirements.txt",reqsTxt); zip.file(folder+".env.example","# Environment variables\n"); zip.file(folder+"README.md","# "+title+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\npython3 -m venv .venv\nsource .venv/bin/activate\npip install -r requirements.txt\n\`\`\`\n\n## Run\n\`\`\`bash\npython main.py\n\`\`\`\n"); zip.file(folder+".gitignore",".venv/\n__pycache__/\n*.pyc\n.env\n.DS_Store\n"); } /* --- Node.js --- */ function buildNode(zip,folder,app,code){ var title=slugTitle(app); var pn=pkgName(app); var src=code.replace(/^\`\`\`[\w]*\n?/m,"").replace(/\n?\`\`\`$/m,"").trim(); var depMap={"mongoose":"^8.0.0","dotenv":"^16.4.5","axios":"^1.7.9","cors":"^2.8.5","bcryptjs":"^2.4.3","jsonwebtoken":"^9.0.2","socket.io":"^4.7.4","uuid":"^9.0.1","zod":"^3.22.4","express":"^4.18.2"}; var deps={}; Object.keys(depMap).forEach(function(k){if(src.indexOf(k)>=0)deps[k]=depMap[k];}); if(!deps["express"])deps["express"]="^4.18.2"; var pkgJson=JSON.stringify({"name":pn,"version":"1.0.0","main":"src/index.js","scripts":{"start":"node src/index.js","dev":"nodemon src/index.js"},"dependencies":deps,"devDependencies":{"nodemon":"^3.0.3"}},null,2)+"\n"; zip.file(folder+"package.json",pkgJson); var fallback="const express=require(\"express\");\nconst app=express();\napp.use(express.json());\n\napp.get(\"/\",(req,res)=>{\n res.json({message:\""+title+" API\"});\n});\n\nconst PORT=process.env.PORT||3000;\napp.listen(PORT,()=>console.log(\"Server on port \"+PORT));\n"; zip.file(folder+"src/index.js",src||fallback); zip.file(folder+".env.example","PORT=3000\n"); zip.file(folder+".gitignore","node_modules/\n.env\n.DS_Store\n"); zip.file(folder+"README.md","# "+title+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nnpm install\n\`\`\`\n\n## Run\n\`\`\`bash\nnpm run dev\n\`\`\`\n"); } /* --- Vanilla HTML --- */ function buildVanillaHtml(zip,folder,app,code){ var title=slugTitle(app); var isFullDoc=code.trim().toLowerCase().indexOf("=0||code.trim().toLowerCase().indexOf("=0; var indexHtml=isFullDoc?code:"\n\n\n\n\n"+title+"\n\n\n\n"+code+"\n\n\n\n"; zip.file(folder+"index.html",indexHtml); zip.file(folder+"style.css","/* "+title+" — styles */\n*{margin:0;padding:0;box-sizing:border-box}\nbody{font-family:system-ui,-apple-system,sans-serif;background:#fff;color:#1a1a2e}\n"); zip.file(folder+"script.js","/* "+title+" — scripts */\n"); zip.file(folder+"assets/.gitkeep",""); zip.file(folder+"README.md","# "+title+"\n\nGenerated by PantheraHive BOS.\n\n## Open\nDouble-click \`index.html\` in your browser.\n\nOr serve locally:\n\`\`\`bash\nnpx serve .\n# or\npython3 -m http.server 3000\n\`\`\`\n"); zip.file(folder+".gitignore",".DS_Store\nnode_modules/\n.env\n"); } /* ===== MAIN ===== */ var sc=document.createElement("script"); sc.src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"; sc.onerror=function(){ if(lbl)lbl.textContent="Download ZIP"; alert("JSZip load failed — check connection."); }; sc.onload=function(){ var zip=new JSZip(); var base=(_phFname||"output").replace(/\.[^.]+$/,""); var app=base.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,"")||"my_app"; var folder=app+"/"; var vc=document.getElementById("panel-content"); var panelTxt=vc?(vc.innerText||vc.textContent||""):""; var lang=detectLang(_phCode,panelTxt); if(_phIsHtml){ buildVanillaHtml(zip,folder,app,_phCode); } else if(lang==="flutter"){ buildFlutter(zip,folder,app,_phCode,panelTxt); } else if(lang==="react-native"){ buildReactNative(zip,folder,app,_phCode,panelTxt); } else if(lang==="swift"){ buildSwift(zip,folder,app,_phCode,panelTxt); } else if(lang==="kotlin"){ buildKotlin(zip,folder,app,_phCode,panelTxt); } else if(lang==="react"){ buildReact(zip,folder,app,_phCode,panelTxt); } else if(lang==="vue"){ buildVue(zip,folder,app,_phCode,panelTxt); } else if(lang==="angular"){ buildAngular(zip,folder,app,_phCode,panelTxt); } else if(lang==="python"){ buildPython(zip,folder,app,_phCode); } else if(lang==="node"){ buildNode(zip,folder,app,_phCode); } else { /* Document/content workflow */ var title=app.replace(/_/g," "); var md=_phAll||_phCode||panelTxt||"No content"; zip.file(folder+app+".md",md); var h=""+title+""; h+="

"+title+"

"; var hc=md.replace(/&/g,"&").replace(//g,">"); hc=hc.replace(/^### (.+)$/gm,"

$1

"); hc=hc.replace(/^## (.+)$/gm,"

$1

"); hc=hc.replace(/^# (.+)$/gm,"

$1

"); hc=hc.replace(/\*\*(.+?)\*\*/g,"$1"); hc=hc.replace(/\n{2,}/g,"

"); h+="

"+hc+"

Generated by PantheraHive BOS
"; zip.file(folder+app+".html",h); zip.file(folder+"README.md","# "+title+"\n\nGenerated by PantheraHive BOS.\n\nFiles:\n- "+app+".md (Markdown)\n- "+app+".html (styled HTML)\n"); } zip.generateAsync({type:"blob"}).then(function(blob){ var a=document.createElement("a"); a.href=URL.createObjectURL(blob); a.download=app+".zip"; a.click(); URL.revokeObjectURL(a.href); if(lbl)lbl.textContent="Download ZIP"; }); }; document.head.appendChild(sc); } function phShare(){navigator.clipboard.writeText(window.location.href).then(function(){var el=document.getElementById("ph-share-lbl");if(el){el.textContent="Link copied!";setTimeout(function(){el.textContent="Copy share link";},2500);}});}function phEmbed(){var runId=window.location.pathname.split("/").pop().replace(".html","");var embedUrl="https://pantherahive.com/embed/"+runId;var code='';navigator.clipboard.writeText(code).then(function(){var el=document.getElementById("ph-embed-lbl");if(el){el.textContent="Embed code copied!";setTimeout(function(){el.textContent="Get Embed Code";},2500);}});}