Skip to content

Conversation

@taj54
Copy link
Member

@taj54 taj54 commented Aug 5, 2025

Resolved two main issues causing test failures:

  1. Race condition from setTimeout(..., 0) during player init.
  2. Tight coupling to internal refs not exposed by the component.

Fixes:

  • Mocked @interactive-video-labs/core to isolate component behavior.
  • Used vi.useFakeTimers() to control async timing.
  • Exposed playerRef via setup to improve test accessibility.

Summary by CodeRabbit

  • New Features

    • Introduced a new Vue 3 component, InteractiveVideo, providing an interactive video player with support for analytics events, cue points, translations, and customizable player options.
  • Tests

    • Added comprehensive tests for the InteractiveVideo component, including rendering, player initialization, and analytics event handling.
  • Chores

    • Updated project dependencies and configurations to improve module resolution and development experience, including adjustments to package management and TypeScript/Vitest settings.

Resolved two main issues causing test failures:
1. Race condition from `setTimeout(..., 0)` during player init.
2. Tight coupling to internal refs not exposed by the component.

Fixes:
- Mocked `@interactive-video-labs/core` to isolate component behavior.
- Used `vi.useFakeTimers()` to control async timing.
- Exposed `playerRef` via setup to improve test accessibility.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 5, 2025

Walkthrough

A new Vue 3 component, InteractiveVideo, was introduced as a wrapper for the IVLabsPlayer from @interactive-video-labs/core, along with corresponding tests. Project configuration files were updated: package.json reorganized dependencies and set the module type, tsconfig.json changed module resolution, and vitest.config.ts added Vue plugin support and enabled global test variables.

Changes

Cohort / File(s) Change Summary
Vue Interactive Video Component
src/index.ts
Introduced a new InteractiveVideo Vue 3 component wrapping IVLabsPlayer, with props for video URL, analytics callback, cues, translations, and player options. Exposes player reference and manages lifecycle and prop updates.
Component Tests
test/InteractiveVideo.test.ts
Added Vitest-based tests for InteractiveVideo, mocking IVLabsPlayer, verifying rendering, player initialization, and analytics event forwarding.
Package Configuration
package.json
Set "type": "module", reorganized peerDependencies order, moved several packages to devDependencies, and downgraded vitest in devDependencies. No script or metadata changes.
TypeScript Config
tsconfig.json
Changed moduleResolution from "Node" to "bundler" for module import resolution.
Vitest & Vite Plugin Config
vitest.config.ts
Added @vitejs/plugin-vue to plugins, enabled globals: true for tests, and ensured jsdom environment for Vue component testing.

Sequence Diagram(s)

sequenceDiagram
  participant App as Vue App
  participant IV as InteractiveVideo (component)
  participant Player as IVLabsPlayer

  App->>IV: Mounts with props (videoUrl, cues, translations, etc.)
  IV->>Player: Initializes IVLabsPlayer with config
  Player-->>IV: Emits analytics event
  IV-->>App: Calls onAnalyticsEvent callback (if provided)
  IV->>Player: Loads cues and translations (if provided)
  App->>IV: Updates cues or translations props
  IV->>Player: Updates cues/translations
  App->>IV: Unmounts
  IV->>Player: Destroys player instance
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~15 minutes

Poem

In a meadow of code, a new player appears,
Vue and video magic, hopping past old frontiers.
With cues and translations, events in the breeze,
Tests sprout like clover, reviewed with such ease.
The config now tidier, the bunny leaps high—
Interactive tales under digital sky!
🐇✨

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between b78f4c1 and 56bc41e.

📒 Files selected for processing (1)
  • package.json (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • package.json
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • 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 explain this code block.
  • 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 explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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 for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @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 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 for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
test/InteractiveVideo.test.ts (1)

69-76: Verify the event callback extraction logic.

The test assumes the PLAYER_LOADED event listener is the first matching call, but this could be fragile if event registration order changes.

Consider making the event callback extraction more robust:

-// Simulate an event being fired by the player
-const eventCallback = (mockPlayerInstance.on as any).mock.calls.find(call => call[0] === 'PLAYER_LOADED')[1];
-eventCallback({ some: 'payload' });
+// Simulate an event being fired by the player
+const playerLoadedCall = (mockPlayerInstance.on as any).mock.calls.find(call => call[0] === 'PLAYER_LOADED');
+expect(playerLoadedCall).toBeDefined();
+const eventCallback = playerLoadedCall[1];
+eventCallback({ some: 'payload' });
src/index.ts (3)

42-66: Consider eliminating the setTimeout to fully resolve race conditions.

While the tests now control this with fake timers, the setTimeout(..., 0) pattern was identified as causing race conditions in the PR objectives. Consider using nextTick() or checking DOM readiness more directly.

Replace the setTimeout pattern with Vue's nextTick:

-        // Use a timeout to ensure the element is in the DOM
-        setTimeout(() => {
-          if (containerRef.value) {
-            const player = new IVLabsPlayer(`#${uniqueId}`, playerConfig);
-            // ... rest of initialization
-          }
-        }, 0);
+        // Use nextTick to ensure the element is in the DOM
+        await nextTick();
+        if (containerRef.value) {
+          const player = new IVLabsPlayer(`#${uniqueId}`, playerConfig);
+          // ... rest of initialization
+        }

Note: You'll need to make the onMounted callback async and import nextTick from Vue.


48-56: Reduce repetitive event listener code.

The event listener setup is repetitive and could be simplified for better maintainability.

Consider using a more concise approach:

-              if (props.onAnalyticsEvent) {
-                player.on('PLAYER_LOADED', (payload?: AnalyticsPayload) => (props.onAnalyticsEvent as Function)('PLAYER_LOADED', payload));
-                player.on('VIDEO_STARTED', (payload?: AnalyticsPayload) => (props.onAnalyticsEvent as Function)('VIDEO_STARTED', payload));
-                player.on('VIDEO_PAUSED', (payload?: AnalyticsPayload) => (props.onAnalyticsEvent as Function)('VIDEO_PAUSED', payload));
-                player.on('VIDEO_ENDED', (payload?: AnalyticsPayload) => (props.onAnalyticsEvent as Function)('VIDEO_ENDED', payload));
-                player.on('CUE_TRIGGERED', (payload?: AnalyticsPayload) => (props.onAnalyticsEvent as Function)('CUE_TRIGGERED', payload));
-                player.on('INTERACTION_COMPLETED', (payload?: AnalyticsPayload) => (props.onAnalyticsEvent as Function)('INTERACTION_COMPLETED', payload));
-                player.on('ERROR', (payload?: AnalyticsPayload) => (props.onAnalyticsEvent as Function)('ERROR', payload));
-              }
+              if (props.onAnalyticsEvent) {
+                const events: AnalyticsEvent[] = [
+                  'PLAYER_LOADED', 'VIDEO_STARTED', 'VIDEO_PAUSED', 'VIDEO_ENDED',
+                  'CUE_TRIGGERED', 'INTERACTION_COMPLETED', 'ERROR'
+                ];
+                events.forEach(event => {
+                  player.on(event, (payload?: AnalyticsPayload) => 
+                    props.onAnalyticsEvent!(event, payload)
+                  );
+                });
+              }

28-28: Improve unique ID generation robustness.

The current ID generation using Math.random() could theoretically produce collisions.

Consider using a more robust approach:

-    const uniqueId = `ivlabs-player-${Math.random().toString(36).substr(2, 9)}`;
+    const uniqueId = `ivlabs-player-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between f3f421d and b78f4c1.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (5)
  • package.json (2 hunks)
  • src/index.ts (1 hunks)
  • test/InteractiveVideo.test.ts (1 hunks)
  • tsconfig.json (1 hunks)
  • vitest.config.ts (1 hunks)
🔇 Additional comments (7)
tsconfig.json (1)

5-5: LGTM! Module resolution change aligns with ES module approach.

The change from "Node" to "bundler" module resolution properly supports the project's ES module configuration and modern bundling practices.

vitest.config.ts (2)

3-3: Good addition of Vue plugin support.

The Vue plugin integration enables proper Single File Component compilation during testing.


8-9: Appropriate test environment configuration.

The combination of globals: true and jsdom environment properly supports Vue component testing by providing global test APIs and DOM simulation.

package.json (1)

4-4: Good addition of ES module support.

Setting "type": "module" properly configures the package for ES module semantics, aligning with the TypeScript configuration changes.

test/InteractiveVideo.test.ts (2)

6-18: Excellent mocking strategy for test isolation.

The comprehensive mock of @interactive-video-labs/core properly isolates the component during testing, addressing the tight coupling issues mentioned in the PR objectives.


21-28: Proper timer control implementation.

The use of fake timers in setup/teardown directly addresses the race condition issues caused by setTimeout(..., 0) mentioned in the PR objectives.

src/index.ts (1)

25-27: Good exposure of playerRef for testing.

Exposing playerRef via the setup function addresses the tight coupling issues mentioned in the PR objectives, making tests less fragile.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@taj54 taj54 merged commit 2d4de3a into interactive-video-labs:main Aug 5, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant