Repository: asyncapi/generator
Pull request #1433: test: add tests to registered hooks
Author: login: ssala034
Labels: name: ready-to-merge
Created: 2025-03-24T17:21:42Z | Merged: 2025-04-09T08:08:52Z

**Description**
Added unit tests to test functions hooksRegistry.js for the generator. 
Result
![Screenshot 2025-03-24 131504](https://github.com/user-attachments/assets/10cbf9d8-2e24-46c3-94f7-133416ef8c5e)

**Related issue(s)**
Fixes: #1412 

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **Chores**
  - Reintroduced the `@asyncapi/generator-hooks` dependency to enhance generator functionality.
- **Refactor**
  - Improved the structure of the hooks registration process for better clarity and maintainability.
- **Tests**
  - Added a comprehensive test suite for the `hooksRegistry` module, covering various hook registration scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/apps/generator/lib/hooksRegistry.js b/apps/generator/lib/hooksRegistry.js
index 2c21389fbd..4b1c2ae222 100644
--- a/apps/generator/lib/hooksRegistry.js
+++ b/apps/generator/lib/hooksRegistry.js
@@ -9,7 +9,7 @@ const { exists, registerTypeScript } = require('./utils');
  * @param {String} templateDir Directory where template is located.
  * @param {String} hooksDir Directory where local hooks are located.
  */
-module.exports.registerHooks = async (hooks, templateConfig, templateDir, hooksDir) => {
+async function registerHooks (hooks, templateConfig, templateDir, hooksDir) {
   await registerLocalHooks(hooks, templateDir, hooksDir);
 
   if (templateConfig && Object.keys(templateConfig).length > 0) await registerConfigHooks(hooks, templateDir, templateConfig);
@@ -122,3 +122,10 @@ function addHook(hooks, mod, config) {
   });
   return hooks;
 }
+
+module.exports = {
+  registerHooks,
+  registerLocalHooks,
+  registerConfigHooks,
+  addHook
+};
\ No newline at end of file
diff --git a/apps/generator/package.json b/apps/generator/package.json
index 7846e094ab..dfff348c11 100644
--- a/apps/generator/package.json
+++ b/apps/generator/package.json
@@ -48,10 +48,10 @@
   "license": "Apache-2.0",
   "homepage": "https://github.com/asyncapi/generator",
   "dependencies": {
+    "@asyncapi/generator-hooks": "*",
     "@asyncapi/generator-react-sdk": "^1.1.2",
     "@asyncapi/multi-parser": "^2.1.1",
     "@asyncapi/nunjucks-filters": "*",
-    "@asyncapi/generator-hooks": "*",
     "@asyncapi/parser": "^3.0.14",
     "@npmcli/arborist": "5.6.3",
     "@npmcli/config": "^8.0.2",
diff --git a/apps/generator/test/hooksRegistry.test.js b/apps/generator/test/hooksRegistry.test.js
new file mode 100644
index 0000000000..2e6d1af44c
--- /dev/null
+++ b/apps/generator/test/hooksRegistry.test.js
@@ -0,0 +1,173 @@
+/**
+ * @jest-environment node
+ */
+const fs = require('fs');
+const path = require('path');
+const { addHook, registerLocalHooks, registerConfigHooks, registerHooks } = require('../lib/hooksRegistry');
+
+jest.mock('fs');
+jest.mock('path');
+
+describe('hooksRegistry', () => {
+  let hooks;
+
+  beforeEach(() => {
+    hooks = {};  // reset hooks for each test
+    jest.clearAllMocks(); // Reset all mocks
+    jest.resetModules(); // Reset modules
+  });
+
+  describe('registerHooks', () => {
+    it('registers both local and config hooks', async () => {
+      const templateDir = path.join(__dirname, 'fixtures', 'template', 'hooks');      
+      const hooksDir = path.join(__dirname, 'hooks');
+      
+      fs.mkdirSync(hooksDir, { recursive: true });
+      fs.writeFileSync(path.join(hooksDir, 'preGenerate.js'), `
+        module.exports = function localPreGenerateHook() {};
+      `);
+
+      const templateConfig = {
+        hooks: {
+          '@asyncapi/hooks-module': ['configPreGenerateHook']
+        }
+      };
+
+      jest.mock('@asyncapi/hooks-module', () => ({
+        preGenerate: [function configPreGenerateHook() {}]
+      }), { virtual: true });
+
+      const result = await registerHooks(hooks, templateConfig, templateDir, 'hooks');
+      
+      expect(result.preGenerate).toHaveLength(1); 
+      expect(result.preGenerate[0].name).toBe('configPreGenerateHook');
+      
+      fs.rmSync(hooksDir, { recursive: true, force: true });
+    });
+  });
+
+  describe('registerLocalHooks', () => {
+    const mockPreGenerateHook = function preGenerateHook() {};
+  
+    beforeAll(() => {
+      path.join.mockImplementation((...args) => args.join('/'));
+    });
+  
+    beforeEach(() => {
+      fs.existsSync.mockReturnValue(true);
+      fs.readdirSync.mockReturnValue(['preGenerate.js']);
+      
+      jest.mock('fixtures/template/hooks/preGenerate.js', () => mockPreGenerateHook, { virtual: true });
+    });
+  
+    it('handles missing hooks directory', async () => {
+      fs.existsSync.mockReturnValueOnce(false);
+      
+      const result = await registerLocalHooks(hooks, '/non/existent/path', 'hooks');
+      expect(result).toBe(hooks);
+      expect(result.preGenerate).toBeUndefined();
+    });
+    
+    it('handles errors during hook loading', async () => {
+      fs.existsSync.mockReturnValue(true);
+      fs.readdirSync.mockReturnValue(['errorHook.js']);
+      
+      jest.mock('fixtures/template/hooks/errorHook.js', () => {
+        throw new Error('Mock import error');
+      }, { virtual: true });
+      
+      await expect(registerLocalHooks(hooks, 'fixtures/template', 'hooks'))
+        .resolves.not.toThrow();
+        
+      expect(hooks).toEqual({});
+    });
+  });
+
+  describe('registerConfigHooks', () => {
+    it('registers hooks from template config', async () => {
+      const templateDir = path.join(__dirname, 'fixtures', 'template');
+      const templateConfig = {
+        hooks: {
+          '@asyncapi/hooks-module': ['preGenerateHook']
+        }
+      };
+
+      // Mock the hook module
+      jest.mock('@asyncapi/hooks-module', () => ({
+        preGenerate: [function preGenerateHook() {}]
+      }), { virtual: true });
+
+      const result = await registerConfigHooks(hooks, templateDir, templateConfig);
+      
+      expect(result.preGenerate).toHaveLength(1);
+      expect(result.preGenerate[0].name).toBe('preGenerateHook');
+    });
+
+    it('handles missing hooks in config', async () => {
+      const result = await registerConfigHooks(hooks, '', {});
+      expect(result).toBeUndefined();
+      expect(hooks).toEqual({}); 
+    });
+  });
+
+  describe('addHooks', () => {
+    it('adds hooks from module to hooks object', () => {
+      const mod = {
+        preGenerate: [function preGenerateHook() {}],
+        postGenerate: [function postGenerateHook() {}]
+      };
+
+      addHook(hooks, mod, null);
+
+      expect(hooks.preGenerate).toHaveLength(1);
+      expect(hooks.postGenerate).toHaveLength(1);
+      expect(hooks.preGenerate[0].name).toBe('preGenerateHook');
+      expect(hooks.postGenerate[0].name).toBe('postGenerateHook');
+    });
+
+    it('adds hooks from module.default if it exists', () => {
+      const mod = {
+        default: {
+          preGenerate: [function preGenerateHook() {}],
+          postGenerate: [function postGenerateHook() {}]
+        }
+      };
+
+      addHook(hooks, mod, null);
+
+      expect(hooks.preGenerate).toHaveLength(1);
+      expect(hooks.postGenerate).toHaveLength(1);
+      expect(hooks.preGenerate[0].name).toBe('preGenerateHook');
+      expect(hooks.postGenerate[0].name).toBe('postGenerateHook');
+    });
+
+    it('does not add hooks that are not in config', () => {
+      const mod = {
+        preGenerate: [function preGenerateHook() {}],
+        postGenerate: [function postGenerateHook() {}]
+      };
+      const config = ['preGenerateHook'];
+
+      addHook(hooks, mod, config);
+
+      expect(hooks.preGenerate).toHaveLength(1);
+      expect(hooks.postGenerate).toBeUndefined();
+      expect(hooks.preGenerate[0].name).toBe('preGenerateHook');
+    });
+
+    it('adds hooks that are in config', () => {
+      const mod = {
+        preGenerate: [function preGenerateHook() {}],
+        postGenerate: [function postGenerateHook() {}]
+      };
+      const config = ['preGenerateHook', 'postGenerateHook'];
+
+      addHook(hooks, mod, config);
+
+      expect(hooks.preGenerate).toHaveLength(1);
+      expect(hooks.postGenerate).toHaveLength(1);
+      expect(hooks.preGenerate[0].name).toBe('preGenerateHook');
+      expect(hooks.postGenerate[0].name).toBe('postGenerateHook');
+    });
+  });
+});
\ No newline at end of file


Reviews:
user: login: github-actions[bot]
body: Welcome to AsyncAPI. Thanks a lot for creating your first pull request. Please check out our [contributors guide](https://github.com/asyncapi/community/blob/master/CONTRIBUTING.md) useful for opening a pull request.<br />Keep in mind there are also other channels you can use to interact with AsyncAPI community. For more details check out [this issue](https://github.com/asyncapi/asyncapi/issues/115).
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T17:21:52Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (3)</summary><blockquote>

<details>
<summary>apps/generator/test/hooksRegistry.test.js (3)</summary><blockquote>

`20-47`: **Path handling might cause cross-platform issues.**

The test uses Windows-style backslashes (`\\`) in the path on line 22, which could cause problems when running tests on non-Windows platforms.

Consider using `path.join()` consistently without hardcoded separators:

```diff
-      const templateDir = path.join(__dirname, '\\fixtures\\template\\hooks');
+      const templateDir = path.join(__dirname, 'fixtures', 'template', 'hooks');
```

Additionally, ensure the directory cleanup on line 45 uses the correct path - it should use `hooksDir` instead of `templateDir` since that's what was created in the test.

```diff
-      fs.rmSync(templateDir, { recursive: true, force: true });
+      fs.rmSync(hooksDir, { recursive: true, force: true });
```

---

`72-96`: **Test assertions could be more comprehensive.**

The tests for `registerConfigHooks` cover the basics but could be improved with additional assertions.

In the second test case when handling missing hooks in config (lines 92-95), consider checking that the `hooks` object remains unchanged rather than just checking that the result is undefined:

```diff
  it('handles missing hooks in config', async () => {
    const result = await registerConfigHooks(hooks, '', {});
    expect(result).toBeUndefined();
+   expect(hooks).toEqual({}); // Verify hooks object wasn't modified
  });
```

---

`1-158`: **Consider adding a test for hook registration errors.**

While the test suite covers most scenarios, there's no test for error handling during hook registration.

Consider adding a test case that simulates an error during hook registration, for example:

```javascript
it('handles errors during hook loading', async () => {
  fs.existsSync.mockReturnValue(true);
  fs.readdirSync.mockReturnValue(['errorHook.js']);
  
  // Mock a hook that throws an error when imported
  jest.mock('fixtures/template/hooks/errorHook.js', () => {
    throw new Error('Mock import error');
  }, { virtual: true });
  
  // The function should handle the error gracefully
  await expect(registerLocalHooks(hooks, 'fixtures/template', 'hooks'))
    .resolves.not.toThrow();
    
  // Verify hooks object wasn't corrupted
  expect(hooks).toEqual({});
});
```

This would ensure the functions handle errors gracefully without crashing the application.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 719d5bcbae6d508956b74fe8108adaa2c09f7abb and 47d8a586f9074cb486932d341c71e54d0c05b85c.

</details>

<details>
<summary>📒 Files selected for processing (5)</summary>

* `apps/generator/lib/hooksRegistry.js` (2 hunks)
* `apps/generator/package.json` (2 hunks)
* `apps/generator/test/hooksRegistry.test.js` (1 hunks)
* `package.json` (1 hunks)
* `turbo.json` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Definitions (1)</summary>

<details>
<summary>apps/generator/test/hooksRegistry.test.js (1)</summary><blockquote>

<details>
<summary>apps/generator/lib/hooksRegistry.js (3)</summary>

* `require` (3-3)
* `path` (1-1)
* `registerHooks` (12-18)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>🔇 Additional comments (12)</summary><blockquote>

<details>
<summary>package.json (1)</summary>

`16-16`: **Good addition of a dedicated hook testing script!**

This new script `generator:test:hooks` follows the existing naming convention and properly targets the hooks testing in the generator package. This makes it easier to run hook-specific tests independently.

</details>
<details>
<summary>turbo.json (1)</summary>

`16-19`: **Configuration correctly mirrors the established pattern!**

The new `test:hooks` task follows the same structure as other testing tasks, properly depending on the `build` task and disabling caching to ensure fresh test runs. This maintains consistency with the existing configuration pattern.

</details>
<details>
<summary>apps/generator/package.json (2)</summary>

`20-20`: **Good addition of the Jest hook testing configuration!**

The new `test:hooks` script correctly uses Jest's `--testPathPattern` to target only hooks registry tests. This allows for focused testing of hook-related functionality.

---

`52-53`: <details>
<summary>✅ Verification successful</summary>

**Verify the new dependency is being used in the codebase.**

The addition of `@asyncapi/generator-components` as a new dependency should be validated. While the dependency movement is fine, it's important to ensure the new component is actually used somewhere in the codebase.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if @asyncapi/generator-components is used in the codebase
echo "Searching for imports of @asyncapi/generator-components..."
rg -l "@asyncapi/generator-components" --type js
```

Length of output: 244

---



</details>

**Dependency Usage Verified:** The search confirms that `@asyncapi/generator-components` is imported in `apps/generator/test/test-templates/react-template/template/models.js`. The dependency addition appears to be valid and is being utilized in the codebase.

</details>
<details>
<summary>apps/generator/lib/hooksRegistry.js (3)</summary>

`12-18`: **Good refactoring of the function declaration!**

The change from directly exporting the function to defining it as a constant first improves code readability and follows better JavaScript practices. The function logic itself remains unchanged, which is good.

---

`126-131`: **Clean export pattern provides a clearer public API!**

Consolidating the exports into a single object improves the module interface. This approach makes it explicit which functions are part of the public API and follows modern JavaScript module patterns.

---

`12-131`: <details>
<summary>❓ Verification inconclusive</summary>

**Verify new tests cover all exported functions.**

Since this PR aims to add tests for the hooks registry, we should verify that the test coverage includes all the newly consolidated exports (`registerHooks`, `registerLocalHooks`, `registerConfigHooks`, and `addHook`).

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if tests exist for the exported functions
echo "Searching for tests covering the exported hook registry functions..."
rg -A 3 "describe\(('|\").*hooks.*" apps/generator/test/
```

Length of output: 444

---



</details>

**Ensure comprehensive tests for all hook registry exports are in place.**  
We located the test suite in `apps/generator/test/hooksRegistry.test.js` (as confirmed by your `rg` output). Please double-check that this suite explicitly tests each exported function—namely, `registerHooks`, `registerLocalHooks`, `registerConfigHooks`, and `addHook`—including their edge cases (such as handling non-existent hook directories or fallback resolution for local modules).

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 28-61: Promise executor functions should not be `async`.



(lint/suspicious/noAsyncPromiseExecutor)

</details>

</details>

</details>
<details>
<summary>apps/generator/test/hooksRegistry.test.js (5)</summary>

`1-7`: **Good setup with clear imports and environment declaration.**

The Jest environment is properly set up for Node.js, and all necessary dependencies are imported correctly. The imports align with the functions being tested in the hooksRegistry module.

---

`8-10`: **Well-structured mocking approach.**

Good practice to mock the filesystem and path modules, as this prevents tests from interacting with the actual filesystem during test execution.

---

`11-19`: **Clean test organization with proper setup.**

The test suite is well-organized with a `beforeEach` hook that resets the state between tests, which helps ensure test isolation. Resetting mocks and modules is also good practice.

---

`49-70`: **Good testing for edge cases.**

The test for missing hooks directory is important and correctly handles this edge case. The mock implementation for `path.join` is also a good approach for platform-independence.

---

`98-157`: **Comprehensive tests for addHook functionality.**

The tests for `addHook` cover multiple scenarios including:
1. Adding hooks from a regular module
2. Adding hooks from a module with default export
3. Filtering hooks based on configuration
4. Adding only hooks that are in the configuration

This provides excellent coverage of the function's behavior under different conditions.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-24T17:25:32Z
user: login: ItshMoh
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T04:46:31Z
user: login: ItshMoh
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T08:29:16Z
user: login: derberg
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T11:35:12Z
user: login: derberg
body: lgtm
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-04-08T15:11:56Z
user: login: derberg
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-09T08:08:23Z

Review comments:
diff_hunk: @@ -13,6 +13,7 @@
     "generator:test": "turbo run test --filter=@asyncapi/generator",
     "generator:test:dev": "turbo run test:dev",
     "generator:test:unit": "turbo run test:unit  --filter=@asyncapi/generator",
+    "generator:test:hooks": "turbo run test:hooks --filter=@asyncapi/generator",
path: package.json
user: login: ItshMoh
body: Here i think the test for hooksRegistry should run like a Unit test. No need to make a separate command. As the parser.test.js and utils.test.js are going. 
created_at: 2025-03-26T04:43:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -13,6 +13,10 @@
       "dependsOn": ["build"],
       "cache": false 
     },
+    "test:hooks": { 
+      "dependsOn": ["build"],
path: turbo.json
user: login: ItshMoh
body: So You should remove this from turbo.json file. 
created_at: 2025-03-26T04:44:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -17,6 +17,7 @@
     "test:dev": "npm run test:unit -- --watchAll",
     "test:integration": "npm run test:cleanup && jest --testPathPattern=integration --modulePathIgnorePatterns='./__mocks__(?!\\/loglevel\\.js$)'",
     "test:integration:update": "jest --updateSnapshot --testPathPattern=integration --modulePathIgnorePatterns='./__mocks__(?!\\/loglevel\\.js$)'",
+    "test:hooks": "jest --testPathPattern=hooksRegistry",
path: apps/generator/package.json
user: login: ItshMoh
body: ```suggestion
    
```
created_at: 2025-03-26T04:45:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -17,6 +17,7 @@
     "test:dev": "npm run test:unit -- --watchAll",
     "test:integration": "npm run test:cleanup && jest --testPathPattern=integration --modulePathIgnorePatterns='./__mocks__(?!\\/loglevel\\.js$)'",
     "test:integration:update": "jest --updateSnapshot --testPathPattern=integration --modulePathIgnorePatterns='./__mocks__(?!\\/loglevel\\.js$)'",
+    "test:hooks": "jest --testPathPattern=hooksRegistry",
path: apps/generator/package.json
user: login: ItshMoh
body: Remove this command also. 
created_at: 2025-03-26T04:46:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,173 @@
+/**
+ * @jest-environment node
+ */
+const fs = require('fs')
+const path = require('path')
+const { addHook, registerLocalHooks, registerConfigHooks, registerHooks } = require('../lib/hooksRegistry');
+
+jest.mock('fs');
+jest.mock('path');
+
+describe('hooksRegistry', () => {
+  let hooks;
+
+  beforeEach(() => {
+    hooks = {};  // reset hooks for each test
+    jest.clearAllMocks(); // Reset all mocks
+    jest.resetModules(); // Reset modules
+  });
+
+  describe('registerHooks', () => {
+    it('registers both local and config hooks', async () => {
+      const templateDir = path.join(__dirname, 'fixtures', 'template', 'hooks');      
+      const hooksDir = path.join(__dirname, 'hooks');
+      
+      fs.mkdirSync(hooksDir, { recursive: true });
+      fs.writeFileSync(path.join(hooksDir, 'preGenerate.js'), `
+        module.exports = function localPreGenerateHook() {};
+      `);
+
+      const templateConfig = {
+        hooks: {
+          '@asyncapi/hooks-module': ['configPreGenerateHook']
+        }
+      };
+
+      jest.mock('@asyncapi/hooks-module', () => ({
+        preGenerate: [function configPreGenerateHook() {}]
+      }), { virtual: true });
+
+      const result = await registerHooks(hooks, templateConfig, templateDir, 'hooks');
+      
+      expect(result.preGenerate).toHaveLength(1); 
+      expect(result.preGenerate[0].name).toBe('configPreGenerateHook');
+      
+      fs.rmSync(hooksDir, { recursive: true, force: true });
+    });
+  });
+
+  describe('registerLocalHooks', () => {
+    const mockPreGenerateHook = function preGenerateHook() {};
+  
+    beforeAll(() => {
+      path.join.mockImplementation((...args) => args.join('/'));
+    });
+  
+    beforeEach(() => {
+      fs.existsSync.mockReturnValue(true);
+      fs.readdirSync.mockReturnValue(['preGenerate.js']);
+      
+      jest.mock('fixtures/template/hooks/preGenerate.js', () => mockPreGenerateHook, { virtual: true });
+    });
+  
+    it('handles missing hooks directory', async () => {
+      fs.existsSync.mockReturnValueOnce(false);
+      
+      const result = await registerLocalHooks(hooks, '/non/existent/path', 'hooks');
+      expect(result).toBe(hooks);
+      expect(result.preGenerate).toBeUndefined();
+    });
+    
+    it('handles errors during hook loading', async () => {
+      fs.existsSync.mockReturnValue(true);
+      fs.readdirSync.mockReturnValue(['errorHook.js']);
+      
+      jest.mock('fixtures/template/hooks/errorHook.js', () => {
+        throw new Error('Mock import error');
+      }, { virtual: true });
+      
+      await expect(registerLocalHooks(hooks, 'fixtures/template', 'hooks'))
+        .resolves.not.toThrow();
+        
+      expect(hooks).toEqual({});
+    });
+  });
+
+  describe('registerConfigHooks', () => {
+    it('registers hooks from template config', async () => {
+      const templateDir = path.join(__dirname, 'fixtures', 'template');
+      const templateConfig = {
+        hooks: {
+          '@asyncapi/hooks-module': ['preGenerateHook']
+        }
+      };
+
+      // Mock the hook module
+      jest.mock('@asyncapi/hooks-module', () => ({
path: apps/generator/test/hooksRegistry.test.js
user: login: ItshMoh
body: hey @ssala034 why you are testing for  the hooks-module as virtual mock. I think we should try with the @asyncapi/generator-hooks . 

@derberg  what do you think?
created_at: 2025-04-04T08:29:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -48,10 +48,11 @@
   "license": "Apache-2.0",
   "homepage": "https://github.com/asyncapi/generator",
   "dependencies": {
+    "@asyncapi/generator-components": "^1.0.0",
path: apps/generator/package.json
user: login: derberg
body: no need to add, they are not used here yet
created_at: 2025-04-07T11:17:10Z
author_association: MEMBER
diff_hunk: @@ -9,7 +9,7 @@ const { exists, registerTypeScript } = require('./utils');
  * @param {String} templateDir Directory where template is located.
  * @param {String} hooksDir Directory where local hooks are located.
  */
-module.exports.registerHooks = async (hooks, templateConfig, templateDir, hooksDir) => {
+const registerHooks = async (hooks, templateConfig, templateDir, hooksDir) => {
path: apps/generator/lib/hooksRegistry.js
user: login: derberg
body: `async function registerHooks` please
created_at: 2025-04-07T11:27:03Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,173 @@
+/**
+ * @jest-environment node
+ */
+const fs = require('fs')
+const path = require('path')
+const { addHook, registerLocalHooks, registerConfigHooks, registerHooks } = require('../lib/hooksRegistry');
+
+jest.mock('fs');
+jest.mock('path');
+
+describe('hooksRegistry', () => {
+  let hooks;
+
+  beforeEach(() => {
+    hooks = {};  // reset hooks for each test
+    jest.clearAllMocks(); // Reset all mocks
+    jest.resetModules(); // Reset modules
+  });
+
+  describe('registerHooks', () => {
+    it('registers both local and config hooks', async () => {
+      const templateDir = path.join(__dirname, 'fixtures', 'template', 'hooks');      
+      const hooksDir = path.join(__dirname, 'hooks');
+      
+      fs.mkdirSync(hooksDir, { recursive: true });
+      fs.writeFileSync(path.join(hooksDir, 'preGenerate.js'), `
+        module.exports = function localPreGenerateHook() {};
+      `);
+
+      const templateConfig = {
+        hooks: {
+          '@asyncapi/hooks-module': ['configPreGenerateHook']
+        }
+      };
+
+      jest.mock('@asyncapi/hooks-module', () => ({
+        preGenerate: [function configPreGenerateHook() {}]
+      }), { virtual: true });
+
+      const result = await registerHooks(hooks, templateConfig, templateDir, 'hooks');
+      
+      expect(result.preGenerate).toHaveLength(1); 
+      expect(result.preGenerate[0].name).toBe('configPreGenerateHook');
+      
+      fs.rmSync(hooksDir, { recursive: true, force: true });
+    });
+  });
+
+  describe('registerLocalHooks', () => {
+    const mockPreGenerateHook = function preGenerateHook() {};
+  
+    beforeAll(() => {
+      path.join.mockImplementation((...args) => args.join('/'));
+    });
+  
+    beforeEach(() => {
+      fs.existsSync.mockReturnValue(true);
+      fs.readdirSync.mockReturnValue(['preGenerate.js']);
+      
+      jest.mock('fixtures/template/hooks/preGenerate.js', () => mockPreGenerateHook, { virtual: true });
+    });
+  
+    it('handles missing hooks directory', async () => {
+      fs.existsSync.mockReturnValueOnce(false);
+      
+      const result = await registerLocalHooks(hooks, '/non/existent/path', 'hooks');
+      expect(result).toBe(hooks);
+      expect(result.preGenerate).toBeUndefined();
+    });
+    
+    it('handles errors during hook loading', async () => {
+      fs.existsSync.mockReturnValue(true);
+      fs.readdirSync.mockReturnValue(['errorHook.js']);
+      
+      jest.mock('fixtures/template/hooks/errorHook.js', () => {
+        throw new Error('Mock import error');
+      }, { virtual: true });
+      
+      await expect(registerLocalHooks(hooks, 'fixtures/template', 'hooks'))
+        .resolves.not.toThrow();
+        
+      expect(hooks).toEqual({});
+    });
+  });
+
+  describe('registerConfigHooks', () => {
+    it('registers hooks from template config', async () => {
+      const templateDir = path.join(__dirname, 'fixtures', 'template');
+      const templateConfig = {
+        hooks: {
+          '@asyncapi/hooks-module': ['preGenerateHook']
+        }
+      };
+
+      // Mock the hook module
+      jest.mock('@asyncapi/hooks-module', () => ({
path: apps/generator/test/hooksRegistry.test.js
user: login: derberg
body: approach with a mock is better as these are simple unit tests, so the more isolated the better
created_at: 2025-04-07T11:27:42Z
author_association: MEMBER

PR comments:
user: login: changeset-bot[bot]
created_at: 2025-03-24T17:21:46Z
body: ###  ⚠️  No Changeset found

Latest commit: 150038e63dbfacfeabbbd7d6b8e01cd4ea299359

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**



[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/ssala034/generator/new/adding-tests-to-registered-hooks?filename=.changeset/warm-seals-joke.md&value=---%0A%22%40fake-scope%2Ffake-pkg%22%3A%20patch%0A---%0A%0Atest%3A%20add%20tests%20to%20registered%20hooks%0A)


author_association: NONE
user: login: coderabbitai[bot]
created_at: 2025-03-24T17:21:51Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough
The pull request refactors the `registerHooks` function in the `hooksRegistry.js` file by defining it as an `async` function and restructuring the exports into a single object. Additionally, it reinstates the dependency `@asyncapi/generator-hooks` in the `package.json` file. A new Jest test suite for the hooks registry has been introduced to ensure proper registration and error handling of hooks. The overall functionality remains unchanged.

## Changes

| File(s)                                                      | Change Summary                                                                                                                                                             |
| ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `apps/generator/lib/hooksRegistry.js`                        | Refactored `registerHooks` to be an `async` function and consolidated exports for `registerHooks`, `registerLocalHooks`, `registerConfigHooks`, and `addHook`.            |
| `apps/generator/package.json`                                | Reintroduced dependency `"@asyncapi/generator-hooks": "*"` and adjusted its order in the dependencies section.                                                            |
| `apps/generator/test/hooksRegistry.test.js`                  | Introduced a new Jest test suite covering the registration functions and error handling for hooks in the module.                                                          |

## Assessment against linked issues

| Objective                                                              | Addressed | Explanation                                                                             |
| ---------------------------------------------------------------------- | --------- | --------------------------------------------------------------------------------------- |
| Extend test coverage to registered filters and hooks (#1412)           | ✅        |                                                                                         |

## Suggested labels
`ready-to-merge`

## Suggested reviewers
- magicmatatjahu
- jonaslagoni
- asyncapi-bot-eve
- derberg

## Poem
> I'm a little rabbit, hopping with glee,  
> Code’s been tidied up—oh, how fun it can be!  
> Exports are clearer, tests now in sight,  
> Dependencies in order, everything feels right.  
> With a twitch of my whiskers, I send a joyful cheer,  
> Celebrating these changes throughout the year!  
> 🐇✨

<!-- walkthrough_end -->


---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between c754792193d65eacb973b377268af885b984ca55 and 8f172bc42f54f74bba8265b27c93742958dd1ba4.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `apps/generator/test/hooksRegistry.test.js` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* apps/generator/test/hooksRegistry.test.js

</details>

</details>
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=asyncapi/generator&utm_content=1433):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai plan` to trigger planning for file edits and PR creation.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: derberg
created_at: 2025-03-25T13:51:35Z
body: @ssala034 please read messages from the bot
author_association: MEMBER
user: login: ssala034
created_at: 2025-03-25T15:00:20Z
body: > @ssala034 please read messages from the bot

@derberg read the bot feedback. Did the changes
Any more feedback.
author_association: CONTRIBUTOR
user: login: ssala034
created_at: 2025-03-26T13:25:01Z
body: @ItshMoh  any more advice?
Thank you.


author_association: CONTRIBUTOR
user: login: ItshMoh
created_at: 2025-04-04T08:31:07Z
body: hey @derberg  a query regarding review testing PRs. What things to keep in mind? what are the standards to follow? 
As i am also new with hooks so I want to know about it? 
It will be very helpful
author_association: CONTRIBUTOR
user: login: derberg
created_at: 2025-04-08T15:19:01Z
body: linter 😉 
author_association: MEMBER
user: login: sonarqubecloud[bot]
created_at: 2025-04-09T07:49:57Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=asyncapi_generator&pullRequest=1433) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=asyncapi_generator&pullRequest=1433&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=asyncapi_generator&pullRequest=1433&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator&pullRequest=1433&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=asyncapi_generator&pullRequest=1433&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=asyncapi_generator&pullRequest=1433&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=asyncapi_generator&pullRequest=1433)


author_association: NONE
user: login: derberg
created_at: 2025-04-09T08:08:33Z
body: /rtm
author_association: MEMBER