Skip to content

Commit d83b3c7

Browse files
test: Add comprehensive module import tests for CJS and ESM
- Add CommonJS test suite validating direct require() without .default accessor - Add ES modules test suite with named exports and workaround patterns - Add TypeScript ES modules test suite with compiled output validation - Add import documentation to README with examples for both module systems - Update package.json with conditional exports for require and import paths - Update .gitignore to exclude .kiro build artifacts - Bump version to 2.1.2 to reflect module system improvements - Ensure backward compatibility with .default accessor pattern - Validate named exports accessibility across all module systems
1 parent bf662f4 commit d83b3c7

29 files changed

+1414
-44
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Logs
22
logs
33
*.log
4-
4+
.kiro
55
# Runtime data
66
pids
77
*.pid

README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,38 @@
4343
This library is written in TypeScript and the published package ships with
4444
its declaration files for a great developer experience.
4545

46+
#### Import
47+
48+
The package supports both ES modules and CommonJS.
49+
50+
**ES Modules (recommended)**
51+
52+
```ts
53+
// Default import
54+
import RBAC from '@rbac/rbac';
55+
56+
// Named imports
57+
import { createTenantRBAC, MongoRoleAdapter } from '@rbac/rbac';
58+
59+
// Combined
60+
import RBAC, { createTenantRBAC } from '@rbac/rbac';
61+
```
62+
63+
**CommonJS**
64+
65+
```js
66+
// Default import
67+
const RBAC = require('@rbac/rbac');
68+
69+
// Named imports
70+
const { createTenantRBAC, MongoRoleAdapter } = require('@rbac/rbac');
71+
72+
// Combined
73+
const RBAC = require('@rbac/rbac');
74+
const { createTenantRBAC } = require('@rbac/rbac');
75+
```
76+
77+
#### Usage
4678

4779
RBAC is a curried function thats initially takes an object with configurations,
4880
then returns another function that takes an object with roles,

package.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
{
22
"name": "@rbac/rbac",
3-
"version": "2.1.1",
3+
"version": "2.1.2",
44
"description": "Blazing Fast, Zero dependency, Hierarchical Role-Based Access Control for Node.js",
55
"main": "lib/index.js",
66
"types": "lib/index.d.ts",
7+
"exports": {
8+
".": {
9+
"types": "./lib/index.d.ts",
10+
"import": "./lib/index.mjs",
11+
"require": "./lib/index.cjs",
12+
"default": "./lib/index.js"
13+
}
14+
},
715
"scripts": {
816
"build": "tsc && vite build",
917
"dev": "tsc -w",

test/cjs-test/README.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# CommonJS Import Test
2+
3+
This test project validates CommonJS import behavior for the @rbac/rbac package.
4+
5+
## Purpose
6+
7+
This test validates **Requirement 2.1**: CommonJS consumers should be able to import RBAC directly without needing `.default` accessor.
8+
9+
## Test Files
10+
11+
### index.js
12+
The main test file that validates the expected behavior:
13+
- Imports RBAC using CommonJS syntax: `const RBAC = require('@rbac/rbac')`
14+
- Verifies RBAC is imported as a function directly (not an object requiring .default)
15+
- Tests basic RBAC functionality including permissions
16+
17+
### test-backward-compat.js
18+
Validates backward compatibility with the `.default` accessor pattern:
19+
- Uses `require('@rbac/rbac').default` to access the function
20+
- Verifies this pattern still works for existing code
21+
- Validates **Requirement 2.2**: Backward compatibility
22+
23+
### test-named-exports.js
24+
Validates that named exports are accessible in CommonJS:
25+
- Tests destructuring: `const { createTenantRBAC } = require('@rbac/rbac')`
26+
- Verifies named exports are accessible on the default export
27+
- Validates **Requirement 4.1**: Named exports preservation
28+
29+
## Running Tests
30+
31+
```bash
32+
# Install dependencies
33+
npm install
34+
35+
# Run the main test
36+
npm test
37+
38+
# Run backward compatibility test
39+
node test-backward-compat.js
40+
41+
# Run named exports test
42+
node test-named-exports.js
43+
44+
# Run all tests
45+
npm test && node test-backward-compat.js && node test-named-exports.js
46+
```
47+
48+
## Package Configuration
49+
50+
The package.json includes:
51+
- No `"type"` field - Uses default CommonJS mode
52+
- `"@rbac/rbac": "file:../../"` - Links to the local package for testing
53+
54+
## Implementation Details
55+
56+
The fix uses a CommonJS wrapper file (`lib/index.cjs`) that:
57+
1. Imports the compiled TypeScript output (`lib/index.js`)
58+
2. Exports the default export as the main `module.exports`
59+
3. Preserves all named exports
60+
4. Maintains the `.default` accessor for backward compatibility
61+
62+
The package.json `exports` field uses conditional exports:
63+
```json
64+
{
65+
"exports": {
66+
".": {
67+
"require": "./lib/index.cjs",
68+
"import": "./lib/index.mjs"
69+
}
70+
}
71+
}
72+
```
73+
74+
This ensures CommonJS consumers get the wrapper that provides direct function access.
75+
76+
## Test Results
77+
78+
All tests pass, confirming:
79+
- ✅ Direct require works: `require('@rbac/rbac')` returns function
80+
- ✅ Backward compatibility: `require('@rbac/rbac').default` still works
81+
- ✅ Named exports: `require('@rbac/rbac').createTenantRBAC` works
82+
- ✅ Destructuring: `const { createTenantRBAC } = require('@rbac/rbac')` works

test/cjs-test/index.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
const assert = require('assert');
2+
3+
// Test Requirement 2.1: CommonJS consumer should be able to require RBAC directly
4+
// without needing .default accessor
5+
const RBAC = require('@rbac/rbac');
6+
7+
console.log('Testing CommonJS import without .default accessor...');
8+
9+
// Verify that RBAC is a function directly
10+
assert.strictEqual(typeof RBAC, 'function', 'RBAC should be a function when imported via require()');
11+
12+
console.log('✅ Type check passed: RBAC is a function');
13+
14+
// Test that we can actually use it
15+
const roles = {
16+
user: {
17+
can: ['read']
18+
},
19+
admin: {
20+
can: ['read', 'write']
21+
}
22+
};
23+
24+
const rbacInstance = RBAC({ enableLogger: false })(roles);
25+
26+
assert.strictEqual(typeof rbacInstance, 'object', 'RBAC() should return an object');
27+
assert.strictEqual(typeof rbacInstance.can, 'function', 'RBAC instance should have a can method');
28+
29+
console.log('✅ Functional check passed: RBAC works correctly');
30+
31+
// Test basic permission check
32+
(async () => {
33+
try {
34+
const canRead = await rbacInstance.can('user', 'read');
35+
assert.strictEqual(canRead, true, 'User should be able to read');
36+
37+
const canWrite = await rbacInstance.can('user', 'write');
38+
assert.strictEqual(canWrite, false, 'User should not be able to write');
39+
40+
console.log('✅ Permission checks passed');
41+
console.log('✅ All CommonJS tests passed successfully!');
42+
} catch (error) {
43+
console.error('❌ Test failed:', error.message);
44+
process.exit(1);
45+
}
46+
})();

test/cjs-test/package-lock.json

Lines changed: 57 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/cjs-test/package.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "rbac-cjs-test",
3+
"version": "1.0.0",
4+
"description": "Test CommonJS import without .default accessor",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "node index.js && node test-backward-compat.js && node test-named-exports.js"
8+
},
9+
"dependencies": {
10+
"@rbac/rbac": "file:../../"
11+
}
12+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
const assert = require('assert');
2+
3+
// Test Requirement 2.2: Backward compatibility with .default accessor
4+
// Both require('@rbac/rbac') and require('@rbac/rbac').default should work identically
5+
6+
console.log('Testing backward compatibility with .default accessor...');
7+
8+
// Import using both patterns
9+
const RBACDirect = require('@rbac/rbac');
10+
const RBACWithDefault = require('@rbac/rbac').default;
11+
12+
// Verify both are functions
13+
assert.strictEqual(typeof RBACDirect, 'function', 'require("@rbac/rbac") should be a function');
14+
assert.strictEqual(typeof RBACWithDefault, 'function', 'require("@rbac/rbac").default should be a function');
15+
16+
console.log('✅ Type check passed: Both patterns return functions');
17+
18+
// Verify they return the SAME function (reference equality)
19+
assert.strictEqual(RBACDirect, RBACWithDefault,
20+
'require("@rbac/rbac") and require("@rbac/rbac").default should return the same function');
21+
22+
console.log('✅ Identity check passed: Both patterns return the same function reference');
23+
24+
// Test that both patterns work identically with the same input
25+
const roles = {
26+
user: {
27+
can: ['read']
28+
},
29+
admin: {
30+
can: ['read', 'write']
31+
}
32+
};
33+
34+
const rbacInstance1 = RBACDirect({ enableLogger: false })(roles);
35+
const rbacInstance2 = RBACWithDefault({ enableLogger: false })(roles);
36+
37+
assert.strictEqual(typeof rbacInstance1, 'object', 'RBACDirect() should return an object');
38+
assert.strictEqual(typeof rbacInstance1.can, 'function', 'RBACDirect instance should have a can method');
39+
40+
assert.strictEqual(typeof rbacInstance2, 'object', 'RBACWithDefault() should return an object');
41+
assert.strictEqual(typeof rbacInstance2.can, 'function', 'RBACWithDefault instance should have a can method');
42+
43+
console.log('✅ Functional check passed: Both patterns create valid RBAC instances');
44+
45+
// Test that both instances behave identically
46+
(async () => {
47+
try {
48+
const canRead1 = await rbacInstance1.can('user', 'read');
49+
const canRead2 = await rbacInstance2.can('user', 'read');
50+
assert.strictEqual(canRead1, canRead2, 'Both instances should return same result for user read permission');
51+
assert.strictEqual(canRead1, true, 'User should be able to read');
52+
53+
const canWrite1 = await rbacInstance1.can('user', 'write');
54+
const canWrite2 = await rbacInstance2.can('user', 'write');
55+
assert.strictEqual(canWrite1, canWrite2, 'Both instances should return same result for user write permission');
56+
assert.strictEqual(canWrite1, false, 'User should not be able to write');
57+
58+
const canWriteAdmin1 = await rbacInstance1.can('admin', 'write');
59+
const canWriteAdmin2 = await rbacInstance2.can('admin', 'write');
60+
assert.strictEqual(canWriteAdmin1, canWriteAdmin2, 'Both instances should return same result for admin write permission');
61+
assert.strictEqual(canWriteAdmin1, true, 'Admin should be able to write');
62+
63+
console.log('✅ Behavior check passed: Both patterns produce identical results');
64+
console.log('✅ All backward compatibility tests passed successfully!');
65+
} catch (error) {
66+
console.error('❌ Test failed:', error.message);
67+
process.exit(1);
68+
}
69+
})();
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
const assert = require('assert');
2+
3+
// Test that named exports are accessible
4+
const { createTenantRBAC } = require('@rbac/rbac');
5+
6+
console.log('Testing named exports in CommonJS...');
7+
8+
// Verify that createTenantRBAC is a function
9+
assert.strictEqual(typeof createTenantRBAC, 'function', 'createTenantRBAC should be a function');
10+
11+
console.log('✅ Named export check passed: createTenantRBAC is accessible');
12+
13+
// Also verify we can get both default and named exports together
14+
const RBAC = require('@rbac/rbac');
15+
assert.strictEqual(typeof RBAC, 'function', 'Default export should be a function');
16+
assert.strictEqual(typeof RBAC.createTenantRBAC, 'function', 'Named export should be accessible on default export');
17+
18+
console.log('✅ Combined export check passed');
19+
console.log('✅ All named export tests passed!');

0 commit comments

Comments
 (0)