diff --git a/.cursor/rules/_navigation.mdc b/.cursor/rules/_navigation.mdc new file mode 100644 index 000000000..7a186ead8 --- /dev/null +++ b/.cursor/rules/_navigation.mdc @@ -0,0 +1,30 @@ +--- +description: Codebase Navigation Rules +globs: +alwaysApply: false +--- +# Cursor Rules Navigation Configuration + +## Global base rules (applied to all files unless overridden) +[/**/*] +.cursor/rules/base.mdc + +## Testing rules +[/test/**/tests-**.ts, /test/src/tests-**.js, /test/src/jest/**.spec.ts] +.cursor/rules/test.mdc + +### Testing subcategories + +#### Unit Testing Rules +[/test/jest/*.spec.ts] +.cursor/rules/unit-testing.mdc + +#### Integration Testing Rules +[/test/src/tests-**.ts, /test/src/tests-**.js] +.cursor/rules/integration-testing.mdc + +#### Cross Browser Testing Rules + +## Personal rules +[/**/*] +.cursor/rules/_personal_.mdc \ No newline at end of file diff --git a/.cursor/rules/code-quality.mdc b/.cursor/rules/code-quality.mdc new file mode 100644 index 000000000..03b3d6cb5 --- /dev/null +++ b/.cursor/rules/code-quality.mdc @@ -0,0 +1,54 @@ +--- +description: Coding Guidelines specific for JavaScript and TypeScript +globs: src/**.js, src/**.ts +--- +# Persona + +You are a senior javascript and typescript developer. One of those rare 10x developers that has incredible knowledge. + +# Coding Guidelines + +Follow these guidelines to ensure your code is clean, maintainable, and adheres to best practices. Remember, less code is better. Lines of code = Debt. + +# Key Mindsets + +**1** **Simplicity**: Write simple and straightforward code. +**2** **Readability**: Ensure your code is easy to read and understand. +**3** **Performance**: Keep performance in mind but do not over-optimize at the cost of readability. +**4** **Maintainability**: Write code that is easy to maintain and update. +**5** **Testability**: Ensure your code is easy to test. +**6** **Reusability**: Write reusable components and functions. + +Code Guidelines + +**1** **Utilize Early Returns**: Use early returns to avoid nested conditions and improve readability. +**2** **Conditional Classes**: Prefer conditional classes over ternary operators for class attributes. +**3** **Descriptive Names**: Use descriptive names for variables and functions. +**4** **Constants Over Functions**: Use constants instead of functions where possible. Define types if applicable. +**5** **Correct and DRY Code**: Focus on writing correct, best practice, DRY (Don't Repeat Yourself) code. +**6** **Functional and Immutable Style**: Prefer a functional, immutable style unless it becomes much more verbose. +**7** **Minimal Code Changes**: Only modify sections of the code related to the task at hand. Avoid modifying unrelated pieces of code. Accomplish goals with minimal code changes. + +Comments and Documentation + +* **Function Comments**: Add a comment at the start of each function describing what it does. +* **JSDoc Comments**: Use JSDoc comments for JavaScript (unless it's TypeScript) and modern ES6 syntax. + +Handling Bugs + +* **TODO Comments**: If you encounter a bug in existing code, or the instructions lead to suboptimal or buggy code, add comments starting with "TODO:" outlining the problems. + +Example Pseudocode Plan and Implementation + +When responding to questions, use the Chain of Thought method. Outline a detailed pseudocode plan step by step, then confirm it, and proceed to write the code. Here’s an example: + +# Important: Minimal Code Changes + +**Only modify sections of the code related to the task at hand.** +**Avoid modifying unrelated pieces of code.** +**Avoid changing existing comments.** +**Avoid any kind of cleanup unless specifically instructed to.** +**Accomplish the goal with the minimum amount of code changes.** +**Code change = potential for bugs and technical debt.** + +Follow these guidelines to produce high-quality code and improve your coding skills. If you have any questions or need clarification, don’t hesitate to ask! \ No newline at end of file diff --git a/.cursor/rules/coding-best-practices.mdc b/.cursor/rules/coding-best-practices.mdc new file mode 100644 index 000000000..fc78a1fd2 --- /dev/null +++ b/.cursor/rules/coding-best-practices.mdc @@ -0,0 +1,89 @@ +--- +description: +globs: *.js, *.ts +--- + # JavaScript and TypeScript Coding Best Practices + + Coding best practices for the mParticle project including code style, structure, and maintenance guidelines for .js and .ts files + + ## General Guidelines + + This document outlines the coding best practices to be followed in the mParticle project. + + ## Code Organization + + ## Updating Cursor Rules + + + name: update_cursor_rules_when_corrected + description: Always update rules when a correction is made + filters: + - type: event + pattern: "conversation" + + actions: + - type: suggest + message: | + When corrections are made to your code or approach: + + 1. Always update existing Cursor rules or create new ones to reflect the correction + 2. Document the reasoning behind the correction + 3. Include examples of both incorrect and correct approaches + 4. Make sure rule updates are committed along with code changes + + This ensures that future work will benefit from the lessons learned and helps maintain consistent coding practices. + + examples: + - input: | + // Correction made about unnecessary usings but rules not updated + // Future similar mistakes likely to occur + output: | + // Correction made about unnecessary usings + // Rules updated to prevent similar issues: + // .cursor/rules/coding-best-practices.mdc updated with using directive guidelines + // Future work will avoid the same mistake + + metadata: + priority: high + version: 1.0 + + + ## Code Style + + ### Naming Conventions + + - Use PascalCase for class names + - Use camelCase for method parameters and local variables + - Use camelCase for method names, and property names + - Prefix interfaces with 'I' + - Use meaningful, descriptive names that communicate intent + + ### Formatting + + - Use consistent indentation (4 spaces recommended) + - Limit line length to improve readability + - Use blank lines to separate logical blocks of code + + ## Documentation + + - Document public APIs with comments (use JSDocs format for .js files or TSDocs format for .ts files) + - Include a summary for each class and method + - Document parameters, return values, and exceptions + - Provide examples for complex APIs + + ## Error Handling + + - Be specific about the exceptions you catch + - Avoid empty catch blocks + - Log exceptions with relevant context + - Consider using exception filters + + ## Testing + + - Write unit tests for all public APIs + - Focus on testing behavior, not implementation details + - Use meaningful assertion strings that describe the scenario being tested + - Follow the Rspec pattern for organizing tests into `describe` and `it` blocks + + @version "1.0.0" + @last_updated "2025-03-10" \ No newline at end of file diff --git a/.cursor/rules/commit-messages.mdc b/.cursor/rules/commit-messages.mdc new file mode 100644 index 000000000..032e5f5d1 --- /dev/null +++ b/.cursor/rules/commit-messages.mdc @@ -0,0 +1,62 @@ +# Commit Message Conventions + + +name: commit_message_conventions +description: Standards for writing commit messages following conventional commit standards + +format: | + The commit message should follow this format: + ``` + [optional scope]: + + [optional body] + + [optional footer] + ``` + + Breaking changes must be indicated by "BREAKING CHANGE:" in the footer + +types: | + - feat: A new feature (automatic minor release) + - fix: A bug fix (automatic patch release) + - docs: Documentation only changes + - style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) + - refactor: A code change that neither fixes a bug nor adds a feature + - perf: A code change that improves performance + - test: Adding missing or correcting existing tests + - chore: Changes that don't modify src or test files, such as automatic documentation generation + - ci: Changes to CI configuration files/scripts + - revert: Revert commit + - build: Changes that affect the build system or other dependencies + +guidelines: | + 1. First line must follow the type format + 2. Description should be clear and concise + 3. Breaking changes must be noted in footer + 4. Follow conventional commit standards + +examples: + good: + - "feat: Add HashedAttributes method to RoktManager" + - "fix: Correct attribute mapping in RoktManager" + - "docs: Update README with new API methods" + - "refactor: Simplify attribute handling logic" + - "test: Add test coverage for hashedAttributes" + - | + feat: Add new authentication method + + BREAKING CHANGE: Old auth tokens are no longer supported + + bad: + - "feat(rokt): Added hashed attributes feature to roktmanager." + - "fixed bug in attributes" + - "updates" + - "New feature: hashed attributes" + +metadata: + priority: high + version: 1.0 + + +@version "1.0.0" +@last_updated "2024-03-05" \ No newline at end of file diff --git a/.cursor/rules/cursor-rules-location.mdc b/.cursor/rules/cursor-rules-location.mdc new file mode 100644 index 000000000..6d54c1ce9 --- /dev/null +++ b/.cursor/rules/cursor-rules-location.mdc @@ -0,0 +1,78 @@ +--- +description: Rules for placing and organizing Cursor rule files in the repository +globs: +--- +# Cursor Rules Location + +Rules for placing and organizing Cursor rule files in the repository. + + +name: cursor_rules_location +description: Standards for placing Cursor rule files in the correct directory +filters: + # Match any .mdc files + - type: file_extension + pattern: "\\.mdc$" + # Match files that look like Cursor rules + - type: content + pattern: "(?s).*?" + # Match file creation events + - type: event + pattern: "file_create" + +actions: + - type: reject + conditions: + - pattern: "^(?!\\.\\/\\.cursor\\/rules\\/.*\\.mdc$)" + message: "Cursor rule files (.mdc) must be placed in the .cursor/rules directory" + + - type: suggest + message: | + When creating Cursor rules: + + 1. Always place rule files in PROJECT_ROOT/.cursor/rules/: + ``` + .cursor/rules/ + ├── your-rule-name.mdc + ├── another-rule.mdc + └── ... + ``` + + 2. Follow the naming convention: + - Use kebab-case for filenames + - Always use .mdc extension + - Make names descriptive of the rule's purpose + + 3. Directory structure: + ``` + PROJECT_ROOT/ + ├── .cursor/ + │ └── rules/ + │ ├── your-rule-name.mdc + │ └── ... + └── ... + ``` + + 4. Never place rule files: + - In the project root + - In subdirectories outside .cursor/rules + - In any other location + +examples: + - input: | + # Bad: Rule file in wrong location + rules/my-rule.mdc + my-rule.mdc + .rules/my-rule.mdc + + # Good: Rule file in correct location + .cursor/rules/my-rule.mdc + output: "Correctly placed Cursor rule file" + +metadata: + priority: high + version: 1.0 + + +@version "1.0.0" +@last_updated "2025-03-05" \ No newline at end of file diff --git a/.cursor/rules/global-rule.mdc b/.cursor/rules/global-rule.mdc new file mode 100644 index 000000000..f4f8c2f1d --- /dev/null +++ b/.cursor/rules/global-rule.mdc @@ -0,0 +1,53 @@ +--- +description: +globs: +alwaysApply: true +--- +# Developer Guidelines + +## Core Principles + +1. **Examine Codebase First** + - Understand architecture before making changes + - Identify dependencies and component relationships + - Review documentation and existing tests + - Observe established patterns + +2. **Understand Requirements** + - Identify both explicit and implicit requirements + - Consider broader goals behind specific requests + - Interpret requests within project context + - Recognize unstated assumptions + +3. **Seek Clarity** + - Ask questions when requirements are ambiguous + - Validate assumptions before implementation + - Request additional context when needed + +4. **Assess Impact** + - Consider effects on related components + - Evaluate performance and security implications + - Ensure changes align with existing patterns + - Identify potential regression points + +5. **Provide Complete Solutions** + - Address full scope including edge cases + - Include necessary dependencies + - Explain implementation choices + - Consider testing approaches + +## Implementation Process + +1. Analyze relevant code through searching and reading +2. Understand component structure +3. Identify affected files +4. Consider integration points +5. Make changes while preserving existing functionality +6. Explain integration with existing system + +## Documentation + +1. Reference existing documentation when available +2. Explain technical decisions clearly +3. Justify any deviations from established patterns +4. Note areas for potential improvement diff --git a/.cursor/rules/integration-tests.mdc b/.cursor/rules/integration-tests.mdc new file mode 100644 index 000000000..6f0673c59 --- /dev/null +++ b/.cursor/rules/integration-tests.mdc @@ -0,0 +1,134 @@ +--- +description: Unit Testing Guidelines +globs: +alwaysApply: false +--- +# Integration Testing Conventions + +## Test Structure + +Integration tests should be organized in a dedicated section within test files, clearly marked with a `describe` block: + +```javascript +describe('Integration Tests', function() { + // Integration test cases here +}); +``` + +## Setup and Teardown + +1. **Before Each Test** + - Reset the test environment + - Mock necessary external dependencies + - Set up test data + - Initialize the system under test + + ```javascript + beforeEach(function() { + mParticle._resetForTests(MPConfig); + fetchMockSuccess(urls.identify, { + mpid: testMPID, + is_logged_in: false + }); + mParticle.init(apiKey, mParticle.config); + }); + ``` + +2. **After Each Test** + - Clean up mocks + - Restore original state + - Clear test data + + ```javascript + afterEach(function() { + sinon.restore(); + fetchMock.restore(); + mParticle._resetForTests(MPConfig); + deleteAllCookies(); + }); + ``` + +## Test Cases + +1. **Test Complete Workflows** + - Test entire user journeys + - Verify end-to-end functionality + - Check integration points between components + + ```javascript + it('should end a session if the session timeout expires', () => { + // Test complete session lifecycle + }); + ``` + +2. **Test Error Handling** + - Verify error propagation + - Check error logging + - Test recovery mechanisms + + ```javascript + it('integration test - should log an error if the data plan has an error on it', function(done) { + const errorMessage = 'This is an error'; + window.mParticle.config.dataPlan = { + document: { + error_message: errorMessage + } + }; + // Test error handling + }); + ``` + +3. **Test Data Flow** + - Verify data transformation + - Check data persistence + - Test data synchronization + + ```javascript + it('Should convert data plan id to server DTO', function(done) { + // Test data transformation + }); + ``` + +## Mocking and Stubs + +1. **External Dependencies** + - Mock HTTP requests + - Stub external services + - Simulate third-party integrations + + ```javascript + fetchMock.post(urls.events, 200); + sinon.stub(window.mParticle.getInstance()._IntegrationCapture, 'getQueryParams') + ``` + +2. **Browser APIs** + - Mock cookies + - Stub localStorage + - Simulate browser events + + ```javascript + window.document.cookie = '_cookie1=234'; + window.document.cookie = '_cookie2=39895811.9165333198'; + ``` + +## Best Practices + +1. **Isolation** + - Each test should be independent + - Tests should not rely on the state from other tests + - Use `beforeEach` and `afterEach` to reset state + +2. **Readability** + - Use descriptive test names + - Group related tests together + - Add comments for complex setup + +3. **Maintainability** + - Keep test setup DRY + - Use helper functions for common operations + - Document complex test scenarios + +4. **Performance** + - Mock expensive operations + - Avoid unnecessary setup + - Clean up resources after tests diff --git a/.cursor/rules/mdc-file-guidelines.mdc b/.cursor/rules/mdc-file-guidelines.mdc new file mode 100644 index 000000000..407a08ade --- /dev/null +++ b/.cursor/rules/mdc-file-guidelines.mdc @@ -0,0 +1,359 @@ +--- +description: +globs: *.mdc +alwaysApply: false +--- + +# Guidelines and best practices for creating .mdc (Markdown Configuration) files in Cursor, including structure, metadata annotations, and formatting rules + +@context { + "type": "documentation", + "purpose": "cursor_rules", + "format_version": "1.0.0", + "supported_content_types": [ + "guidelines", + "api_docs", + "examples", + "implementations" + ] +} + +@structure { + "required_sections": [ + "frontmatter", + "title", + "context", + "content_sections" + ], + "optional_sections": [ + "version", + "last_updated", + "examples", + "implementations", + "related_files" + ], + "recommended_sections": [ + "practical_examples", + "common_patterns", + "type_definitions" + ] +} + +## File Structure + +### 1. Frontmatter + +@frontmatter_rules [ + { + "id": "position", + "rule": "Must be at the very top of the file", + "severity": "error" + }, + { + "id": "description", + "rule": "Single sentence, clear purpose. The Cursor agent will use this field to decide whether to read the full rule", + "severity": "error" + }, + { + "id": "globs", + "rule": "Array of relevant file patterns the Rule will automatically apply to. If alwaysApply is `true`, `globs` can be left blank", + "severity": "error" + }, + { + "id": "alwaysApply", + "rule": "Whether rule should be applied globally, on every request. Valid values: `true/false`", + "severity": "error" + }, + { + "id": "related_docs", + "rule": "Optional array of related documentation files - MUST only reference existing files", + "severity": "error" + }, + { + "id": "file_validation", + "rule": "All referenced files must exist in the workspace", + "severity": "error" + } +] + +Example frontmatter: +```yaml +--- +description: Guidelines for implementing feature X +globs: ["**/*.{ts,tsx}"] +alwaysApply: false +related_docs: ["docs/architecture/feature-x.md"] +--- +``` + +### 2. Metadata Annotations + +@annotations { + "syntax": "@annotation_name JSON_content", + "placement": "Before relevant sections", + "format": "Valid JSON with proper indentation", + "types": { + "context": "Project and document context", + "rules": "List of rules or requirements", + "format": "Format specifications", + "options": "Available options", + "examples": "Implementation examples", + "implementations": "Full implementation details", + "related": "Related documentation or code" + } +} + +### 3. Content Structure + +@content_rules { + "headings": { + "h1": "Single main title", + "h2": "Major sections", + "h3": "Subsections", + "h4": "Detailed points" + }, + "code_blocks": { + "syntax": "Always specify language", + "examples": "Include practical examples", + "formatting": "Use proper indentation", + "context": "Add explanatory comments" + }, + "implementation_blocks": { + "structure": "Group related implementations", + "documentation": "Include inline documentation", + "types": "Specify type information", + "validation": "Include validation rules" + } +} + +## Best Practices + +@best_practices { + "organization": { + "sections": "Use clear hierarchical structure", + "annotations": "Place before relevant content", + "examples": "Include practical examples" + }, + "formatting": { + "json": "Properly formatted, valid JSON", + "markdown": "Clean, consistent spacing", + "code": "Language-specific syntax highlighting" + }, + "metadata": { + "annotations": "Use semantic names", + "context": "Provide clear scope", + "versioning": "Include version information" + }, + "implementation": { + "examples": "Provide complete, working examples", + "types": "Include type definitions", + "validation": "Specify validation rules", + "error_handling": "Document error cases" + } +} + +## Implementation Guidelines + +@implementation_rules { + "code_examples": { + "completeness": "Must be fully functional", + "types": "Include all necessary type information", + "imports": "Show all required imports", + "context": "Provide setup and usage context" + }, + "documentation": { + "inline": "Add explanatory comments", + "types": "Document type constraints", + "errors": "Document error conditions", + "usage": "Show usage patterns" + } +} + +## Example Structure + +### Basic Example +```markdown +--- +description: Example implementation of feature X +globs: ["src/features/**/*.ts"] +alwaysApply: false +--- + +# Feature X Implementation + +@context { + "type": "implementation", + "feature": "X", + "version": "1.0.0" +} + +## Overview +[Feature description] + +## Implementation + +@implementation { + "language": "typescript", + "dependencies": ["dep1", "dep2"], + "types": { + "TypeX": "Description of TypeX" + } +} + +```csharp +// Implementation code with types and validation +``` + +## Usage Examples +[Usage examples with code] +``` + +### Full Implementation Example +See the `examples` section in related .mdc files for complete implementation examples. + +## Common Patterns + +@patterns { + "rules_section": { + "format": "array of objects", + "properties": ["id", "severity", "description"], + "example": [ + { + "id": "rule_name", + "severity": "error", + "description": "Clear description" + } + ] + }, + "implementation_section": { + "format": "object with implementation details", + "required": [ + "language", + "types", + "validation" + ], + "example": { + "language": "typescript", + "types": { + "Type1": "Description" + }, + "validation": { + "rule1": "Description" + } + } + } +} + +## Examples + +### Minimal Valid MDC File +```markdown +--- +description: A simple example MDC file +globs: ["**/*.example"] +alwaysApply: false +--- + +# Example Rules + +@context { + "type": "example", + "version": "1.0.0" +} + +## Section One + +@rules [ + { + "id": "rule_one", + "severity": "error", + "description": "Description of rule one" + } +] +``` + +### Common Section Examples + +#### Rules Section +```markdown +@rules [ + { + "id": "unique_identifier", + "severity": "error|warning|info", + "description": "Clear description of the rule" + } +] +``` + +#### Options Section +```markdown +@options { + "option_name": "What this option does", + "another_option": "Description of another option" +} +``` + +#### Format Section +```markdown +@format { + "base": "Template with {placeholders}", + "options": ["array", "of", "options"], + "paths": { + "key": "value" + } +} +``` + +### Common Mistakes to Avoid + +@mistakes [ + { + "id": "missing_frontmatter", + "wrong": "Starting directly with content", + "correct": "Include frontmatter at top", + "reason": "Frontmatter is required for Cursor to properly parse the file" + }, + { + "id": "invalid_json", + "wrong": "Malformed JSON in annotations", + "correct": "Properly formatted JSON with quotes around keys", + "reason": "Annotations must contain valid JSON for proper parsing" + }, + { + "id": "inconsistent_structure", + "wrong": "Mixed levels of headings", + "correct": "Clear hierarchical structure", + "reason": "Consistent structure helps with readability and parsing" + }, + { + "id": "nonexistent_files", + "wrong": "Referencing files that don't exist in the workspace", + "correct": "Only reference files that exist and have been verified", + "reason": "Prevents broken links and maintains documentation integrity" + } +] + +## Validation + +@validation { + "required": [ + "Frontmatter must be present and valid", + "All JSON must be properly formatted", + "Main title must be present", + "At least one content section", + "Complete implementation examples when relevant", + "All referenced files must exist in the workspace" + ], + "recommended": [ + "Version information", + "Last updated date", + "Clear examples", + "Proper code formatting", + "Type definitions", + "Validation rules", + "Verify file existence before referencing" + ] +} + +@version "1.0.0" +@last_updated "2025-03-11" diff --git a/.cursor/rules/snippet-rules.mdc b/.cursor/rules/snippet-rules.mdc new file mode 100644 index 000000000..fdf63f9e6 --- /dev/null +++ b/.cursor/rules/snippet-rules.mdc @@ -0,0 +1,180 @@ +--- +description: +globs: src/mp-instance.ts,src/mparticle-instance-manager.ts,src/roktManager.ts +alwaysApply: false +--- +# Rules for Snippet.js + +# Snippet Rules + +Rules for maintaining and updating the snippet.js file which serves as a shim/stub until the full SDK loads. + + +name: snippet_rules +description: Standards for updating snippet.js when modifying public methods in the SDK +filters: + # Match SDK core files that might contain public methods + - type: file_extension + pattern: "\\.(js|ts)$" + - type: file_path + pattern: "(mp-instance|mparticle-instance-manager|roktManager)\\.ts$" + # Match the snippet file + - type: file_path + pattern: "snippet\\.js$" + +actions: + - type: suggest + message: | + When modifying SDK files that expose public methods: + + 1. Determine if Method Needs Stubbing: + - Only add methods to snippet.js if they: + * May be called before SDK initialization + * Require queuing logic to work properly after initialization + * Are customer-facing and part of the public API + - Do NOT add methods that: + * Are only called internally by the SDK + * Are only called after SDK initialization + * Are part of the initialization process itself + * Are deprecated and have a non-deprecated alternative with proper queuing + + 2. Method Arrays Organization: + ```javascript + // Public methods must be added to the appropriate method array in snippet.js: + var mainMethods = [/* core SDK methods that need queuing */]; + var ecommerceMethods = [/* ecommerce methods that need queuing */]; + var identityMethods = [/* identity methods that need queuing */]; + var roktMethods = [/* Rokt methods that need queuing */]; + ``` + + 3. Queuing Strategies: + There are three main queuing strategies in the SDK: + a. Pre-init Queue via queueIfNotInitialized: + ```javascript + const queued = queueIfNotInitialized(function() { + self.methodName(args); + }, self); + if (queued) return; + ``` + b. Ready Queue via mParticle.ready: + ```javascript + self.ready(function() { + self.methodName(args); + }); + ``` + c. Deprecated Methods with Legacy Queuing: + - Some deprecated methods may use direct ready queue calls + - Example: + ```javascript + if (!self._Store.isInitialized) { + self.ready(function() { + self.methodName(args); + }); + return; + } + ``` + WARNING: If a deprecated method implements any queuing logic, verify that: + 1. The method has a non-deprecated alternative + 2. The non-deprecated alternative has proper queueIfNotInitialized logic + 3. The deprecation notice directs users to the proper alternative + + 4. Method Stubbing: + - Methods requiring queuing must have a corresponding stub in snippet.js + - Use the preloadMethod function to create stubs: + ```javascript + function preloadMethod(method, base) { + return function() { + if (base) { + method = base + '.' + method; + } + var args = Array.prototype.slice.call(arguments); + args.unshift(method); + window.mParticle.config.rq.push(args); + }; + } + ``` + + 5. Namespace Organization: + - Core methods go directly on mParticle + - Feature-specific methods go under their respective namespaces: + * eCommerce -> mParticle.eCommerce + * Identity -> mParticle.Identity + * Rokt -> mParticle.Rokt + + 6. Method Types That Need Stubs: + - Customer-facing public methods that might be called pre-initialization + - Methods that modify state or send data + - Methods that require queueing to maintain order of operations + - Event logging or tracking methods + + 7. Testing Requirements: + - Verify stub properly queues method call + - Verify queued calls execute in order after SDK loads + - Test with both default and named instances + - Test all parameter combinations + - Verify methods work both pre and post initialization + + 8. Documentation: + ```javascript + /* Method Type Reference: + * 1. Instance Methods: Added if they need pre-init queueing + * 2. eCommerce Methods: Added if they affect cart/purchase state + * 3. Identity Methods: Added if they affect user state + * 4. Rokt Methods: Added if they need to work pre-initialization + * 5. Deprecated Methods: Should direct to non-deprecated alternatives + */ + ``` + +examples: + - input: | + // Bad: Adding all public methods to snippet + class RoktManager { + public init() { ... } // Internal init - DON'T ADD + public attachKit() { ... } // Internal setup - DON'T ADD + public selectPlacements() { ... } // Customer-facing - ADD + public hashAttributes() { ... } // Customer-facing - ADD + } + + // Good: Only adding methods that need queuing + var roktMethods = [ + 'selectPlacements', // Needs queuing - customer might call before init + 'hashAttributes' // Needs queuing - customer might call before init + ]; + output: "Correctly identified methods needing stubs" + + - input: | + // Bad: Deprecated method with legacy queuing but no alternative + logCheckout: function(step, option, attrs, customFlags) { + if (!self._Store.isInitialized) { + self.ready(function() { + self.eCommerce.logCheckout(step, option, attrs, customFlags); + }); + return; + } + // ... rest of method + } + + // Good: Deprecated method with proper alternative + logCheckout: function(step, option, attrs, customFlags) { + self.Logger.warning('mParticle.logCheckout is deprecated, please use mParticle.logProductAction instead'); + // ... rest of method using legacy queuing + } + + // Good: Non-deprecated alternative with proper queuing + logProductAction: function(productActionType, product, attrs, customFlags) { + const queued = queueIfNotInitialized(function() { + self.eCommerce.logProductAction(productActionType, product, attrs, customFlags); + }, self); + if (queued) return; + // ... rest of method + } + output: "Correctly handled deprecated method with queuing" + +metadata: + priority: high + version: 1.0 + + +@version "1.0.0" +@last_updated "2025-03-05" + diff --git a/.cursor/rules/test.mdc b/.cursor/rules/test.mdc new file mode 100644 index 000000000..ef1203b18 --- /dev/null +++ b/.cursor/rules/test.mdc @@ -0,0 +1,39 @@ +--- +description: Test Creation Workflow +globs: +alwaysApply: false +--- + # Test Creation Workflow + +As a senior software engineer tasked with creating quality assurance tests, you should follow a systematic approach to ensure comprehensive test coverage. Before writing any tests, we must follow this sequence: + +1. **Use Case Analysis** + - First, search for documented use cases in the requirements or specifications + - If no use cases are found: + - Request use cases from the stakeholder, OR + - Analyze the code under test and propose a list of use cases + - Wait for stakeholder approval on the final list of use cases before proceeding + +2. **Test Strategy Review** + - Review this documentation and related test guidelines + - Identify applicable testing patterns and conventions + - Determine which testing utilities and mock data will be needed + +3. **Incremental Implementation** + - Begin with test setup and configuration + - Verify the setup is correct before proceeding + - Implement any required general test cases + - Progress through use cases one at a time: + - Write tests for a single use case + - Seek feedback and approval + - Move to the next use case only after current one is approved + +4. **Completion** + - Review full test coverage + - Ensure all use cases are properly tested + - Verify compliance with testing standards + +# Testing Philosophy +We believe that untested software is unsafe, and we would be wary to publish unpredictable code to our users. We believe that time spent in automating testing is a worthwhile investment and is ultimately more reliable than manual testing. We therefore reserve manual testing for superficial smoke tests. Ideally, even this modicum of manual testing will disappear when our automated test coverage becomes sufficiently trustworthy. + +The principles above form the framework of our testing philosophy. We believe in creating automated tests that can afford us the confidence that changes we push to our code have not broken our product. \ No newline at end of file diff --git a/.cursor/rules/unit-testing.mdc b/.cursor/rules/unit-testing.mdc new file mode 100644 index 000000000..a5b464740 --- /dev/null +++ b/.cursor/rules/unit-testing.mdc @@ -0,0 +1,155 @@ +--- +description: Unit Testing Guidelines +globs: +alwaysApply: false +--- +# Unit Testing Guidelines + +## Overview +This document outlines the unit testing standards and best practices for the mParticle Web SDK project. Our unit testing framework uses Jest, and we follow a structured approach to ensure code quality and maintainability. + +## Test File Structure + +### Naming Convention +- Unit Test files should be named with `.spec.ts` extension +- Unit Test files should mirror the directory structure of the source files they're testing +- Example: `utils.ts` → `utils.spec.ts` + +### File Organization +- Unit Tests should be organized in the `test/jest` directory +- Each unit test file should focus on testing a single module or component +- Group related tests using `describe` blocks +- Use `beforeEach` and `afterEach` hooks for test setup and cleanup +- Use a `before` hook for test set up that applies to ALL tests + +## Testing Conventions + +### Method Naming in Tests + +When writing tests, use the following conventions to distinguish between instance and class/static methods: + +- Use `#methodName` to reference instance methods +- Use `.methodName` to reference class/static methods + +### Examples + +```javascript +// Instance method test +describe('#isInitialized', function () { + it('should be a valid method on both mParticle and mParticle.getInstance() objects', function () { + // ... + }); +}); + +// Class/static method test +describe('Types', function() { + describe('.Environment', function() { + it('should return `production`', function() { + mParticle.Types.Environment.Production.should.equal('production'); + }); + }); +}); +``` + +This convention helps developers quickly understand whether they're looking at a test for an instance method or a class/static method. It's particularly useful in test files to make it clear what kind of method is being tested. + +## Writing Tests + +### Test Structure +```typescript +describe('ModuleName', () => { + describe('functionName', () => { + beforeEach(() => { + // Setup code + }); + + afterEach(() => { + // Cleanup code + }); + + it('should do something specific', () => { + // Test implementation + }); + }); +}); +``` + +### Best Practices +1. **Test Isolation** + - Each test should be independent + - Use `beforeEach` to set up fresh test state + - Use `afterEach` to clean up after tests + - Avoid test interdependencies + +2. **Test Naming** + - Use descriptive test names that explain the expected behavior + - Follow the pattern: "should [expected behavior]" + - Example: "should return an empty object if no keys are found" + +3. **Assertions** + - Use specific assertions that test exactly what you intend + - Prefer `toEqual` for object comparisons + - Use `expect().toBe()` for primitive values + - Test both positive and negative cases + +4. **Mocking** + - Mock external dependencies + - Use Jest's mocking capabilities for functions and modules + - Reset mocks between tests using `jest.resetAllMocks()` + +5. **Edge Cases** + - Test boundary conditions + - Test error cases + - Test null/undefined inputs + - Test empty inputs + +## Example Test Pattern +```typescript +describe('functionName', () => { + it('should handle normal case', () => { + const input = 'normal input'; + const expected = 'expected output'; + expect(functionName(input)).toEqual(expected); + }); + + it('should handle edge case', () => { + const input = ''; + const expected = {}; + expect(functionName(input)).toEqual(expected); + }); + + it('should handle error case', () => { + const input = null; + expect(() => functionName(input)).toThrow(); + }); +}); +``` + +## Code Coverage +- Aim for high test coverage, especially for critical paths +- Focus on testing business logic and edge cases +- Don't sacrifice test quality for coverage metrics + +## Running Tests +- Use `npm run test:jest` to run all tests +- Use `npm run test:jest:watch` for development + +## Common Patterns + +### Testing Async Code +```typescript +it('should handle async operations', async () => { + const result = await asyncFunction(); + expect(result).toEqual(expectedValue); +}); +``` + +## Maintenance +- Keep tests up to date with code changes +- Refactor tests when refactoring code +- Suggest obsolete tests for removal, but don't remove them unless asked to do so +- Document complex test scenarios + +## Resources +- [Jest Documentation](mdc:rokt-launcher-config/https:/jestjs.io/docs/getting-started) +- Internal test examples in `test/jest/` directory \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1c05a798e..bed60760f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,9 @@ browserstack.err local.log test/cross-browser-testing/CBT-tests-es5.js test/cross-browser-testing/CBT-tests.js -coverage/ \ No newline at end of file +coverage/ + +# Personal cursor rules +.cursor/rules/personal-*.mdc +.cursor/rules/_personal.mdc +**/_personal.mdc \ No newline at end of file diff --git a/integration-tests.mdc b/integration-tests.mdc new file mode 100644 index 000000000..520d80a05 --- /dev/null +++ b/integration-tests.mdc @@ -0,0 +1,134 @@ +--- +description: +globs: +alwaysApply: false +--- +# Integration Testing Conventions + +## Test Structure + +Integration tests should be organized in a dedicated section within test files, clearly marked with a `describe` block: + +```javascript +describe('Integration Tests', function() { + // Integration test cases here +}); +``` + +## Setup and Teardown + +1. **Before Each Test** + - Reset the test environment + - Mock necessary external dependencies + - Set up test data + - Initialize the system under test + + ```javascript + beforeEach(function() { + mParticle._resetForTests(MPConfig); + fetchMockSuccess(urls.identify, { + mpid: testMPID, + is_logged_in: false + }); + mParticle.init(apiKey, mParticle.config); + }); + ``` + +2. **After Each Test** + - Clean up mocks + - Restore original state + - Clear test data + + ```javascript + afterEach(function() { + sinon.restore(); + fetchMock.restore(); + mParticle._resetForTests(MPConfig); + deleteAllCookies(); + }); + ``` + +## Test Cases + +1. **Test Complete Workflows** + - Test entire user journeys + - Verify end-to-end functionality + - Check integration points between components + + ```javascript + it('should end a session if the session timeout expires', () => { + // Test complete session lifecycle + }); + ``` + +2. **Test Error Handling** + - Verify error propagation + - Check error logging + - Test recovery mechanisms + + ```javascript + it('integration test - should log an error if the data plan has an error on it', function(done) { + const errorMessage = 'This is an error'; + window.mParticle.config.dataPlan = { + document: { + error_message: errorMessage + } + }; + // Test error handling + }); + ``` + +3. **Test Data Flow** + - Verify data transformation + - Check data persistence + - Test data synchronization + + ```javascript + it('Should convert data plan id to server DTO', function(done) { + // Test data transformation + }); + ``` + +## Mocking and Stubs + +1. **External Dependencies** + - Mock HTTP requests + - Stub external services + - Simulate third-party integrations + + ```javascript + fetchMock.post(urls.events, 200); + sinon.stub(window.mParticle.getInstance()._IntegrationCapture, 'getQueryParams') + ``` + +2. **Browser APIs** + - Mock cookies + - Stub localStorage + - Simulate browser events + + ```javascript + window.document.cookie = '_cookie1=234'; + window.document.cookie = '_cookie2=39895811.9165333198'; + ``` + +## Best Practices + +1. **Isolation** + - Each test should be independent + - Tests should not rely on the state from other tests + - Use `beforeEach` and `afterEach` to reset state + +2. **Readability** + - Use descriptive test names + - Group related tests together + - Add comments for complex setup + +3. **Maintainability** + - Keep test setup DRY + - Use helper functions for common operations + - Document complex test scenarios + +4. **Performance** + - Mock expensive operations + - Avoid unnecessary setup + - Clean up resources after tests