Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NC]: Update demo api #48

Merged
merged 21 commits into from
Jun 14, 2024
Merged

[NC]: Update demo api #48

merged 21 commits into from
Jun 14, 2024

Conversation

nicolaschapur
Copy link
Contributor

@nicolaschapur nicolaschapur commented Jun 13, 2024

Summary by CodeRabbit

  • New Features

    • Introduced interfaces and classes for executing and undoing modifications on DOM elements.
    • Added recordingId parameter to the editor mode functions, allowing for better recording management.
    • Enhanced DemoItem component with a new uuid prop for improved item tracking.
  • Bug Fixes

    • Updated form button's onClick event handler for consistent behavior.
  • Enhancements

    • Improved modification handling with new commands and utility functions.
    • Updated modification persistence and undo features for more robust DOM manipulation handling.

# Conflicts:
#	apps/mocksi-lite/background.ts
#	apps/mocksi-lite/content/ContentApp.tsx
#	apps/mocksi-lite/content/content.tsx
# Conflicts:
#	apps/mocksi-lite/content/ContentApp.tsx
#	apps/mocksi-lite/networking.ts
Copy link

coderabbitai bot commented Jun 13, 2024

Warning

Review failed

The pull request is closed.

Walkthrough

Walkthrough

The changes revolve around enhancing the mocksi-lite application's ability to handle DOM modifications effectively. Key updates include new interfaces and classes for executing and undoing modifications, additional parameters for existing functions to support recording IDs, improved modification handling logic, and UI adjustments to ensure consistency in functionality. These enhancements collectively improve the interoperability and maintainability of the codebase, making it easier to extend and interact with DOM changes dynamically.

Changes

File Path Change Summary
apps/mocksi-lite/commands/Command.ts Introduced DOMModification interface, utility function buildQuerySelector, and classes SaveModificationCommand and LoadModificationCommand implementing the Command interface.
apps/mocksi-lite/content/EditMode/editMode.ts Updated setEditorMode function to include an optional recordingId parameter.
apps/mocksi-lite/content/EditMode/actions.ts Enhanced applyChanges function to accept an additional oldValue parameter.
apps/mocksi-lite/content/Popup/CreateDemo/DemoItem.tsx Updated DemoItem component to handle uuid prop and modified event handlers in edit and export buttons.
apps/mocksi-lite/content/Popup/CreateDemo/Form.tsx Modified "Cancel" button onClick event handler to use an arrow function.
apps/mocksi-lite/utils.ts Added imports for new commands, defined a new type DOMModificationsType, introduced a global commandsExecuted variable, revised saveModification, persistModifications, loadModifications, undoModifications, and loadAlterations functions.

Sequence Diagrams

sequenceDiagram
    participant UI as User Interface
    participant Utils as utils.ts
    participant Command as SaveModificationCommand
    participant LocalStorage as localStorage

    UI->>Utils: Call saveModification
    Utils->>Utils: Build DOMModification object
    Utils->>Command: Instantiate SaveModificationCommand
    Command->>LocalStorage: Save modification to localStorage
    Command->>Utils: Return success
    Utils->>UI: Modification saved
Loading
sequenceDiagram
    participant UI as User Interface
    participant EditMode as editMode.ts
    participant LocalStorage as localStorage

    UI->>EditMode: Call setEditorMode(turnOn, recordingId)
    alt turnOn is true
        EditMode->>LocalStorage: Save recordingId
    else turnOn is false
        EditMode->>LocalStorage: Remove recordingId
    end
    EditMode->>UI: Editor mode set
Loading

Poem

Amidst the code where changes play,
A rabbit hops in bright array,
With Save and Load, commands align,
Enhancing edits, pure design.
🎨
Buttons click, with UUIDs,
Ensuring smooth, we strive with ease.
Cheers to progress, let it stay!
🐇


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>.
    • 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 generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @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 as 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 resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration 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.

@nicolaschapur nicolaschapur marked this pull request as draft June 13, 2024 13:21
Copy link

@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

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between d4d01af and 0a660b4.

Files selected for processing (6)
  • apps/mocksi-lite/background.ts (5 hunks)
  • apps/mocksi-lite/consts.ts (1 hunks)
  • apps/mocksi-lite/content/ContentApp.tsx (3 hunks)
  • apps/mocksi-lite/content/EditMode/editMode.ts (2 hunks)
  • apps/mocksi-lite/content/Popup/CreateDemo/DemoItem.tsx (2 hunks)
  • apps/mocksi-lite/content/Popup/CreateDemo/index.tsx (1 hunks)
Files skipped from review due to trivial changes (3)
  • apps/mocksi-lite/background.ts
  • apps/mocksi-lite/consts.ts
  • apps/mocksi-lite/content/Popup/CreateDemo/index.tsx
Additional context used
Biome
apps/mocksi-lite/content/ContentApp.tsx

[error] 91-91: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event. (lint/a11y/useKeyWithClickEvents)

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

Additional comments not posted (5)
apps/mocksi-lite/content/Popup/CreateDemo/DemoItem.tsx (2)

14-16: The update to include uuid in setEditorMode aligns well with the new requirement to handle unique identifiers for editing sessions.


27-27: The removal of the arrow function in the onClick handler is a good practice to avoid unnecessary re-rendering of the component.

apps/mocksi-lite/content/ContentApp.tsx (2)

3-3: Updates to imports and utility functions are correctly aligned with the new functionalities introduced.

Also applies to: 6-6


46-49: The handleUpdate function correctly retrieves the recording ID from localStorage and sends a message for updating the demo. Ensure that error handling is robust in the message handling function.

Verification successful
#!/bin/bash
# Description: Extract and verify error handling in the sendMessage function implementation.

# Extract the sendMessage function implementation from utils.ts
ast-grep --lang ts --pattern $'export const sendMessage = ($_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_

---

```shell
#!/bin/bash
# Description: Extract and verify error handling in the sendMessage function implementation.

# Extract the sendMessage function implementation from utils.ts
ast-grep --lang ts --pattern $'export const sendMessage = ($_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_,$_

---

The `sendMessage` function in `apps/mocksi-lite/utils.ts` does include basic error handling by logging an error message if the response status is not "success". This provides a minimal level of robustness in handling potential errors.

- `apps/mocksi-lite/utils.ts`: The `sendMessage` function logs an error message if the response status is not "success".



</details>


<details>
<summary>Scripts executed</summary>

The following scripts were executed for the analysis:



---

Script:
```shell
#!/bin/bash
# Description: Verify robust error handling in message handling function.

# Test: Search for the function usage. Expect: Error handling mechanisms.
rg --type typescript $'sendMessage'

Length of output: 69



Script:

#!/bin/bash
# Description: Verify robust error handling in message handling function.

# Test: Search for the function usage. Expect: Error handling mechanisms.
rg --type ts 'sendMessage'

Length of output: 1303





Script:

#!/bin/bash
# Description: Extract and verify error handling in the sendMessage function implementation.

# Extract the sendMessage function implementation from utils.ts using a simplified pattern
ast-grep --lang ts --pattern 'export const sendMessage = $_' --json | jq '.[] | .location.start.line' | xargs -I {} sh -c 'sed -n "$(({}-1)),$(({}+10))p" apps/mocksi-lite/utils.ts'

Length of output: 2007



Script:

#!/bin/bash
# Description: Extract and verify error handling in the sendMessage function implementation.

# Locate the sendMessage function and extract surrounding lines for verification
rg 'export const sendMessage' -A 10 apps/mocksi-lite/utils.ts

Length of output: 343

apps/mocksi-lite/content/EditMode/editMode.ts (1)

44-53: The update to setEditorMode to include a recordingId parameter enhances the function's capability to handle session-specific data. Ensure that the recordingId is consistently managed across all relevant components.

Verification successful

The search results indicate that recordingId is consistently managed in the setEditorMode function. It is stored in localStorage when the mode is turned on and removed when the mode is turned off. This aligns with the intended functionality.

  • apps/mocksi-lite/consts.ts: Defines MOCKSI_RECORDING_ID.
  • apps/mocksi-lite/content/EditMode/editMode.ts: Manages recordingId in setEditorMode.
Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify consistent management of `recordingId` across components.

# Test: Search for the usage of `recordingId`. Expect: Consistent usage and removal.
rg --type typescript $'recordingId'

Length of output: 69



Script:

#!/bin/bash
# Description: Verify consistent management of `recordingId` across components.

# Test: Search for the usage of `recordingId`. Expect: Consistent usage and removal.
rg --glob '*.ts' --glob '*.tsx' 'recordingId'

Length of output: 426

@@ -83,6 +88,9 @@ export default function ContentApp({ isOpen, email }: ContentProps) {
</div>
</div>
</div>
<div className="cursor-pointer text-[#009875]" onClick={handleUpdate}>
Copy link

Choose a reason for hiding this comment

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

Add keyboard accessibility to the "Done" button to support keyboard-only users.

- <div className="cursor-pointer text-[#009875]" onClick={handleUpdate}>
+ <div className="cursor-pointer text-[#009875]" onClick={handleUpdate} onKeyUp={(event) => event.key === 'Enter' && handleUpdate()}>
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div className="cursor-pointer text-[#009875]" onClick={handleUpdate}>
<div className="cursor-pointer text-[#009875]" onClick={handleUpdate} onKeyUp={(event) => event.key === 'Enter' && handleUpdate()}>
Tools
Biome

[error] 91-91: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event. (lint/a11y/useKeyWithClickEvents)

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

Copy link

@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: 5

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 0a660b4 and 65a836e.

Files selected for processing (5)
  • apps/mocksi-lite/background.ts (4 hunks)
  • apps/mocksi-lite/commands/Command.ts (1 hunks)
  • apps/mocksi-lite/content/ContentApp.tsx (3 hunks)
  • apps/mocksi-lite/content/EditMode/editMode.ts (2 hunks)
  • apps/mocksi-lite/utils.ts (2 hunks)
Files skipped from review as they are similar to previous changes (2)
  • apps/mocksi-lite/background.ts
  • apps/mocksi-lite/content/EditMode/editMode.ts
Additional context used
Biome
apps/mocksi-lite/content/ContentApp.tsx

[error] 91-91: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event. (lint/a11y/useKeyWithClickEvents)

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

Additional comments not posted (6)
apps/mocksi-lite/commands/Command.ts (4)

1-1: Ensure consistent usage of imported constants.


3-6: Well-defined Command interface with essential methods for command pattern.


8-12: DOMModification interface is clearly defined, ensuring structured data handling.


16-30: Refactor buildQuerySelector to improve performance and maintainability.
[REFACTOR_SUGGESTion]

- const elements = document.querySelectorAll(keyToSave);
- if (elements.length > 1) {
-     keyToSave += `[${[...elements].indexOf(parentElement)}]`
- }
+ const index = Array.from(parentElement.parentNode.children).indexOf(parentElement);
+ if (index !== -1) {
+     keyToSave += `[${index}]`;
+ }

This change avoids querying the entire document and directly computes the index within its parent, which is more efficient.

apps/mocksi-lite/utils.ts (1)

7-7: saveModification function and commandsExecuted array are well-implemented to support the command pattern.

Also applies to: 28-28, 34-44

apps/mocksi-lite/content/ContentApp.tsx (1)

46-49: handleUpdate function correctly implements the update logic. Ensure consistent naming conventions for localStorage keys.

Comment on lines 61 to 67
class LoadModificationCommand implements Command {
execute(): void {
throw new Error("Method not implemented.")
}
undo(): void {
throw new Error("Method not implemented.")
}
Copy link

Choose a reason for hiding this comment

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

LoadModificationCommand methods are placeholders. Ensure implementation or documentation of future plans.

Please implement or document the intended functionality for LoadModificationCommand methods or mark them as deprecated if they are not planned for use.

Comment on lines 32 to 58
export class SaveModificationCommand implements Command {
private prevModifications;
constructor (
private localStorage: Storage,
private modification: DOMModification
) {
this.prevModifications = JSON.parse(
localStorage.getItem(MOCKSI_MODIFICATIONS) || "{}",
);
}

execute(): void {
const {keyToSave, nextText} = this.modification
const {previousText} = this.prevModifications[keyToSave] || {}
this.localStorage.setItem(
MOCKSI_MODIFICATIONS,
JSON.stringify({...this.prevModifications, [keyToSave]: {nextText, previousText}})
)
}

undo(): void {
const {keyToSave, previousText} = this.modification
this.localStorage.setItem(
MOCKSI_MODIFICATIONS,
JSON.stringify({...this.prevModifications, [keyToSave]: {previousText} || undefined})
)
}
Copy link

Choose a reason for hiding this comment

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

Tip

Codebase Verification

The SaveModificationCommand class does not handle potential JSON parsing errors in its constructor.

  • The constructor uses JSON.parse without a surrounding try-catch block to handle potential errors.

Consider adding error handling around the JSON.parse call to ensure robustness.

  • Location: apps/mocksi-lite/commands/Command.ts, SaveModificationCommand class constructor
Analysis chain

SaveModificationCommand class implements the Command pattern effectively. However, consider handling potential JSON parsing errors.

Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify JSON handling in SaveModificationCommand constructor.
# Test: Check for try-catch implementation around JSON.parse.
ast-grep --lang typescript --pattern $'constructor($_, $_) {
  $$$
  JSON.parse($_)
  $$$
}'

Length of output: 568



Script:

#!/bin/bash
# Description: Verify JSON handling in SaveModificationCommand constructor.
# Test: Check for try-catch implementation around JSON.parse.
ast-grep --lang typescript --pattern $'constructor($_) {
  try {
    $$$
    JSON.parse($_)
    $$$
  } catch (e) {
    $$$
  }
}'

Length of output: 644



Script:

#!/bin/bash
# Description: Verify JSON handling in SaveModificationCommand constructor.
# Step 1: Locate the constructor in SaveModificationCommand.
# Step 2: Check for JSON.parse and any surrounding try-catch block.

# Locate the constructor in SaveModificationCommand
ast-grep --lang typescript --pattern $'constructor($_) {
  $$$
}' --json | jq '.[] | select(.text | test("SaveModificationCommand")).text' > constructor_text.txt

# Check for JSON.parse and surrounding try-catch block within the constructor
if grep -q 'JSON.parse' constructor_text.txt; then
  echo "JSON.parse found in constructor"
  if grep -q 'try {' constructor_text.txt && grep -q 'catch (e)' constructor_text.txt; then
    echo "try-catch block found around JSON.parse"
  else
    echo "No try-catch block found around JSON.parse"
  fi
else
  echo "JSON.parse not found in constructor"
fi

Length of output: 710



Script:

#!/bin/bash
# Description: Verify JSON handling in SaveModificationCommand constructor.
# Step 1: Locate the SaveModificationCommand class and its constructor.
# Step 2: Check for JSON.parse and any surrounding try-catch block.

# Locate the SaveModificationCommand class and its constructor
rg -A 10 'class SaveModificationCommand' > SaveModificationCommand_class.txt

# Check for JSON.parse and surrounding try-catch block within the constructor
if grep -q 'JSON.parse' SaveModificationCommand_class.txt; then
  echo "JSON.parse found in constructor"
  if grep -A 5 'JSON.parse' SaveModificationCommand_class.txt | grep -q 'try {' && grep -A 5 'JSON.parse' SaveModificationCommand_class.txt | grep -q 'catch (e)'; then
    echo "try-catch block found around JSON.parse"
  else
    echo "No try-catch block found around JSON.parse"
  fi
else
  echo "JSON.parse not found in constructor"
fi

Length of output: 340

Comment on lines 47 to 65
export const persistModifications = (
recordingId: string
) => {
const alterations: Alteration[] = Object.entries<{nextText: string, previousText: string}>(
JSON.parse(
localStorage.getItem(MOCKSI_MODIFICATIONS) || "{}",
)
).map(
([querySelector, {nextText, previousText}]) => ({
selector: querySelector,
action: previousText ? 'modified' : 'added',
dom_before: previousText || '',
dom_after: nextText
})
)
const updated_timestamp = new Date()
sendMessage('updateDemo', {id: recordingId, recording: { updated_timestamp, alterations }})

}
Copy link

Choose a reason for hiding this comment

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

Tip

Codebase Verification

The persistModifications function lacks error handling and validation, particularly around the JSON.parse operation. This can lead to potential issues if the data in localStorage is not in the expected format.

  • Implement a try-catch block around the JSON.parse call to handle any parsing errors.
  • Add validation to ensure the parsed data conforms to the expected structure before proceeding.

Code Locations:

  • apps/mocksi-lite/utils.ts (function persistModifications)
Analysis chain

persistModifications function effectively handles data transformation and messaging. Ensure robust error handling and validation.

Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify error handling in persistModifications.
# Test: Check for try-catch blocks or error checks.
ast-grep --lang typescript --pattern $'function persistModifications($_) {
  $$$
  JSON.parse($_)
  $$$
}'

Length of output: 107



Script:

#!/bin/bash
# Refined search for try-catch blocks around JSON.parse within persistModifications function
ast-grep --lang typescript --pattern $'function persistModifications($_) {
  $$$
  try {
    JSON.parse($_)
  } catch ($$) {
    $$$
  }
  $$$
}'

# Search for any form of validation within persistModifications function
ast-grep --lang typescript --pattern $'function persistModifications($_) {
  $$$
  if ($_ !== null && $_ !== undefined) {
    $$$
  }
  $$$
}'

Length of output: 290



Script:

#!/bin/bash
# Search for the persistModifications function definition in the codebase
ast-grep --lang typescript --pattern $'function persistModifications($_) {
  $$$
}'

# Display the content of the file containing persistModifications function for manual inspection
fd -e ts -x grep -Hn 'function persistModifications' {} \; | cut -d: -f1 | xargs cat

Length of output: 174



Script:

#!/bin/bash
# Search for the persistModifications function definition in the codebase using ripgrep
rg 'function persistModifications' -t typescript

# Display the content of the file containing persistModifications function for manual inspection
rg 'function persistModifications' -t typescript -l | xargs cat

Length of output: 186



Script:

#!/bin/bash
# Correct the file type and search for the persistModifications function definition in the codebase using ripgrep
rg 'function persistModifications' -t ts

# Display the content of the file containing persistModifications function for manual inspection
rg 'function persistModifications' -t ts -l | xargs cat

Length of output: 100



Script:

#!/bin/bash
# Search for any definition or export of persistModifications in the codebase
rg 'persistModifications' -t ts

# Display the content of the file containing persistModifications function for manual inspection
rg 'persistModifications' -t ts -l | xargs cat

Length of output: 7960

Comment on lines 91 to 93
<div className="cursor-pointer text-[#009875]" onClick={() => setEditorMode(false, localStorage.getItem(MOCKSI_RECORDING_ID) || undefined)}>
Done
</div>
Copy link

Choose a reason for hiding this comment

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

Add keyboard accessibility to the "Done" button to support keyboard-only users.

- <div className="cursor-pointer text-[#009875]" onClick={() => setEditorMode(false, localStorage.getItem(MOCKSI_RECORDING_ID) || undefined)}>
+ <div className="cursor-pointer text-[#009875]" onClick={() => setEditorMode(false, localStorage.getItem(MOCKSI_RECORDING_ID) || undefined)} onKeyUp={(event) => event.key === 'Enter' && setEditorMode(false, localStorage.getItem(MOCKSI_RECORDING_ID) || undefined)}>
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div className="cursor-pointer text-[#009875]" onClick={() => setEditorMode(false, localStorage.getItem(MOCKSI_RECORDING_ID) || undefined)}>
Done
</div>
<div className="cursor-pointer text-[#009875]" onClick={() => setEditorMode(false, localStorage.getItem(MOCKSI_RECORDING_ID) || undefined)} onKeyUp={(event) => event.key === 'Enter' && setEditorMode(false, localStorage.getItem(MOCKSI_RECORDING_ID) || undefined)}>
Done
</div>
Tools
Biome

[error] 91-91: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event. (lint/a11y/useKeyWithClickEvents)

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

Copy link

@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: 3

Outside diff range and nitpick comments (2)
apps/mocksi-lite/content/EditMode/actions.ts (2)

18-18: Add parameter documentation for oldValue.

Consider adding a comment to describe the purpose of the oldValue parameter in the applyChanges function for better code clarity.


35-35: Ensure that previousText is correctly documented and used.

The variable previousText is used to hold the previous state before changes. It would be beneficial to add a comment explaining its role, especially since it's part of the saveModification call.

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 65a836e and 046ee1a.

Files selected for processing (8)
  • apps/mocksi-lite/background.ts (5 hunks)
  • apps/mocksi-lite/commands/Command.ts (1 hunks)
  • apps/mocksi-lite/content/ContentApp.tsx (3 hunks)
  • apps/mocksi-lite/content/EditMode/actions.ts (1 hunks)
  • apps/mocksi-lite/content/EditMode/decorator.ts (1 hunks)
  • apps/mocksi-lite/content/EditMode/editMode.ts (2 hunks)
  • apps/mocksi-lite/content/Popup/CreateDemo/DemoItem.tsx (2 hunks)
  • apps/mocksi-lite/utils.ts (2 hunks)
Files skipped from review as they are similar to previous changes (4)
  • apps/mocksi-lite/background.ts
  • apps/mocksi-lite/commands/Command.ts
  • apps/mocksi-lite/content/EditMode/editMode.ts
  • apps/mocksi-lite/content/Popup/CreateDemo/DemoItem.tsx
Additional context used
Biome
apps/mocksi-lite/content/ContentApp.tsx

[error] 95-103: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event. (lint/a11y/useKeyWithClickEvents)

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

Additional comments not posted (1)
apps/mocksi-lite/content/EditMode/decorator.ts (1)

49-49: Ensure correct handling of newValue and value.

There's potential ambiguity in how newValue and value are used. It's crucial to ensure that these variables are being used correctly throughout the function.

Comment on lines 21 to 25
// const previousNode = nodeWithTextArea?.cloneNode(true)
// const asd = previousNode?.parentElement?.replaceChild(
// document.createTextNode(oldValue),
// previousNode
// )
Copy link

@coderabbitai coderabbitai bot Jun 13, 2024

Choose a reason for hiding this comment

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

Remove commented-out code.

Commented-out code can clutter the codebase and should be removed if it's not intended for future use.

- // const previousNode = nodeWithTextArea?.cloneNode(true)
- // const asd = previousNode?.parentElement?.replaceChild(
- // 	document.createTextNode(oldValue),
- // 	previousNode
- // )
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// const previousNode = nodeWithTextArea?.cloneNode(true)
// const asd = previousNode?.parentElement?.replaceChild(
// document.createTextNode(oldValue),
// previousNode
// )

Copy link
Contributor

Choose a reason for hiding this comment

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

This was a first try to apply undo when de user cancels the edited DOM

Copy link

Choose a reason for hiding this comment

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

@NicoMorenoSirius, thank you for clarifying the purpose of the commented-out code. It's great to see proactive attempts at implementing features like undo functionality.

Since this code might still be in consideration for future refinement, here's a suggestion: You might consider keeping such code in a separate branch or marked with a TODO comment that explains its future potential. This way, the main codebase remains clean, while you still retain the ability to revisit and refine this feature.

If you need further assistance with this or any other aspect of your implementation, feel free to ask. Happy coding!

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't fully disagree with what the🐰 is saying. But for now, it’s fine so we can focus on shipping the MVP.

That being said, we’ll be stricter in future PRs

Copy link

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment on lines 80 to 97
const modifications: DOMModifcationsType = JSON.parse(
localStorage.getItem(MOCKSI_MODIFICATIONS) || "{}",
);
for (const modification of Object.entries(modifications)) {
// value here is encoded, SHOULD NOT be a security risk to put it in the innerHTML
const [querySelector, value] = modification;
const [querySelector, { nextText }] = modification;
const hasIndex = querySelector.match(/\[[0-9]+\]/);
if (hasIndex) {
const index: number = +hasIndex[0].replace("[", "").replace("]", "");
const elemToModify = document.querySelectorAll(
querySelector.replace(hasIndex[0], ""),
)[index];
//@ts-ignore
elemToModify.innerHTML = value;
elemToModify.innerHTML = nextText;
} else {
const [elemToModify] = document.querySelectorAll(querySelector);
//@ts-ignore
elemToModify.innerHTML = value;
elemToModify.innerHTML = nextText;
Copy link

Choose a reason for hiding this comment

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

Refactor loadModifications to reduce complexity and improve error handling.

The current implementation of loadModifications is complex and lacks robust error handling. Consider refactoring it to simplify the logic and add error handling mechanisms.

+ try {
+     const modifications: DOMModifcationsType = JSON.parse(
+         localStorage.getItem(MOCKSI_MODIFICATIONS) || "{}",
+     );
+     modifications.forEach((modification, querySelector) => {
+         const element = document.querySelector(querySelector);
+         if (element) {
+             element.innerHTML = modification.nextText;
+         }
+     });
+ } catch (error) {
+     console.error("Failed to load modifications:", error);
+ }

Committable suggestion was skipped due to low confidence.

Comment on lines 95 to 103
<div
className="cursor-pointer text-[#009875]"
onClick={() =>
setEditorMode(
false,
localStorage.getItem(MOCKSI_RECORDING_ID) || undefined,
)
}
>
Copy link

Choose a reason for hiding this comment

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

Add keyboard accessibility to the "Done" button.

To support keyboard-only users, it's important to add keyboard event handlers to elements that have mouse event handlers.

+ <div className="cursor-pointer text-[#009875]" onClick={() => setEditorMode(false, localStorage.getItem(MOCKSI_RECORDING_ID) || undefined)} onKeyUp={(event) => event.key === 'Enter' && setEditorMode(false, localStorage.getItem(MOCKSI_RECORDING_ID) || undefined)}>
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div
className="cursor-pointer text-[#009875]"
onClick={() =>
setEditorMode(
false,
localStorage.getItem(MOCKSI_RECORDING_ID) || undefined,
)
}
>
<div
className="cursor-pointer text-[#009875]"
onClick={() =>
setEditorMode(
false,
localStorage.getItem(MOCKSI_RECORDING_ID) || undefined,
)
}
onKeyUp={(event) => event.key === 'Enter' && setEditorMode(false, localStorage.getItem(MOCKSI_RECORDING_ID) || undefined)}
>
Tools
Biome

[error] 95-103: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event. (lint/a11y/useKeyWithClickEvents)

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

Copy link

@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: 4

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 046ee1a and 0e189d9.

Files selected for processing (3)
  • apps/mocksi-lite/background.ts (5 hunks)
  • apps/mocksi-lite/content/Popup/CreateDemo/DemoItem.tsx (2 hunks)
  • apps/mocksi-lite/utils.ts (2 hunks)
Files skipped from review due to trivial changes (1)
  • apps/mocksi-lite/content/Popup/CreateDemo/DemoItem.tsx
Files skipped from review as they are similar to previous changes (1)
  • apps/mocksi-lite/background.ts
Additional comments not posted (1)
apps/mocksi-lite/utils.ts (1)

39-48: The changes to the saveModification function look good, integrating the command pattern effectively.

Comment on lines 39 to 48
previousText: string,
) => {
const prevModifications = JSON.parse(
localStorage.getItem(MOCKSI_MODIFICATIONS) || "{}",
);
let keyToSave = parentElement.localName;
if (parentElement.id) {
keyToSave += `#${parentElement.id}`;
}
if (parentElement.className) {
keyToSave += `.${parentElement.className}`;
}
// here we check if the key we built is sufficient to get an unique element when querying
const elements = document.querySelectorAll(keyToSave);
if (elements.length === 1) {
localStorage.setItem(
MOCKSI_MODIFICATIONS,
JSON.stringify({ ...prevModifications, [keyToSave]: newText }),
);
} else {
// if not unique, we search for the index and put it on the key.
keyToSave += `[${[...elements].indexOf(parentElement)}]`;
localStorage.setItem(
MOCKSI_MODIFICATIONS,
JSON.stringify({ ...prevModifications, [keyToSave]: newText }),
);
}
// to successfully implement the do/undo commands, we must save somewhere in memory all commands being executed.
const saveModificationCommand = new SaveModificationCommand(localStorage, {
keyToSave: buildQuerySelector(parentElement),
nextText: newText,
previousText, // todo test previous
});
commandsExecuted.push(saveModificationCommand);
saveModificationCommand.execute();
Copy link

@coderabbitai coderabbitai bot Jun 13, 2024

Choose a reason for hiding this comment

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

Ensure the previous text functionality is tested as indicated by the TODO comment.

Would you like me to help by writing the unit tests for this functionality?

Copy link
Contributor

Choose a reason for hiding this comment

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

yes

Copy link

Choose a reason for hiding this comment

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

@elg0nz, here are the unit tests for the saveModification functionality in the utils.ts file, focusing on the handling of previousText:

import { saveModification, Command, SaveModificationCommand } from './utils';
import { buildQuerySelector } from './commands/Command';

// Mocks
jest.mock('./commands/Command', () => ({
  SaveModificationCommand: jest.fn().mockImplementation(() => ({
    execute: jest.fn(),
    undo: jest.fn()
  })),
  buildQuerySelector: jest.fn(() => 'mock-selector')
}));

describe('saveModification', () => {
  let mockLocalStorage: Storage;

  beforeEach(() => {
    // Setup mock for localStorage
    mockLocalStorage = {
      setItem: jest.fn(),
      getItem: jest.fn(),
      removeItem: jest.fn(),
      clear: jest.fn(),
    } as unknown as Storage;
  });

  it('should create and execute a SaveModificationCommand with previous text', () => {
    const parentElement = document.createElement('div');
    const newText = 'New text';
    const previousText = 'Old text';

    saveModification(parentElement, newText, previousText);

    expect(buildQuerySelector).toHaveBeenCalledWith(parentElement);
    expect(SaveModificationCommand).toHaveBeenCalledWith(mockLocalStorage, {
      keyToSave: 'mock-selector',
      nextText: newText,
      previousText,
    });

    const command = SaveModificationCommand.mock.instances[0];
    expect(command.execute).toHaveBeenCalled();
  });
});

This test ensures that the previousText is correctly passed to the SaveModificationCommand and that the command is properly constructed and executed. The buildQuerySelector utility is also checked to ensure it is called with the correct arguments. Let me know if there are any more functionalities or scenarios you would like to cover in these tests!

Comment on lines 14 to 16
type DOMModifcationsType = {
[querySelector: string]: { nextText: string; previousText: string };
};
Copy link

@coderabbitai coderabbitai bot Jun 13, 2024

Choose a reason for hiding this comment

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

Correct the typographical error in the type name DOMModifcationsType.

- type DOMModifcationsType = {
+ type DOMModificationsType = {
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
type DOMModifcationsType = {
[querySelector: string]: { nextText: string; previousText: string };
};
type DOMModificationsType = {
[querySelector: string]: { nextText: string; previousText: string };
};

Copy link
Contributor

Choose a reason for hiding this comment

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

Also this one

Copy link

Choose a reason for hiding this comment

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

@elg0nz, thank you for pointing that out! I've checked the rest of the code in the vicinity of the typo correction you agreed with. If there are any more corrections or specific areas you'd like me to review, please let me know!

If the correction looks good to you, you can apply the suggested fix to ensure the type name is consistent throughout the codebase. If there's anything else you need, feel free to ask!

Comment on lines 79 to 97
// v2 of loading alterations, this is from backend
export const loadAlterations = (alterations: Alteration[]) => {
for(const alteration of alterations) {
const {selector, dom_after} = alteration
const hasIndex = selector.match(/\[[0-9]+\]/);
if (hasIndex) {
const index: number = +hasIndex[0].replace("[", "").replace("]", "");
const elemToModify = document.querySelectorAll(
querySelector.replace(hasIndex[0], ""),
selector.replace(hasIndex[0], ""),
)[index];
//@ts-ignore
elemToModify.innerHTML = value;
elemToModify.innerHTML = dom_after;
} else {
const [elemToModify] = document.querySelectorAll(querySelector);
const [elemToModify] = document.querySelectorAll(selector);
//@ts-ignore
elemToModify.innerHTML = value;
elemToModify.innerHTML = dom_after;
}
}
};
}
Copy link

Choose a reason for hiding this comment

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

Consider sanitizing dom_after to prevent potential XSS attacks when assigning to innerHTML.

+ const safeDomAfter = sanitizeHtml(dom_after); // Ensure you have a sanitizeHtml function available
  elemToModify.innerHTML = safeDomAfter;

Committable suggestion was skipped due to low confidence.

Comment on lines 51 to 68
export const persistModifications = (recordingId: string) => {
const alterations: Alteration[] = Object.entries<{
nextText: string;
previousText: string;
}>(JSON.parse(localStorage.getItem(MOCKSI_MODIFICATIONS) || "{}")).map(
([querySelector, { nextText, previousText }]) => ({
selector: querySelector,
action: previousText ? "modified" : "added",
dom_before: previousText || "",
dom_after: nextText,
}),
);
for (const modification of Object.entries(modifications)) {
// value here is encoded, SHOULD NOT be a security risk to put it in the innerHTML
const [querySelector, value] = modification;
const hasIndex = querySelector.match(/\[[0-9]+\]/);
const updated_timestamp = new Date();
sendMessage("updateDemo", {
id: recordingId,
recording: { updated_timestamp, alterations },
});
};
Copy link

Choose a reason for hiding this comment

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

Add error handling around the JSON.parse operation to prevent potential runtime errors.

+ try {
+     const modifications = JSON.parse(localStorage.getItem(MOCKSI_MODIFICATIONS) || "{}");
+     // existing code
+ } catch (error) {
+     console.error("Error parsing modifications:", error);
+ }

Committable suggestion was skipped due to low confidence.

@NicoMorenoSirius NicoMorenoSirius marked this pull request as ready for review June 13, 2024 17:21
Copy link

@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: 4

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 0e189d9 and cc16cf6.

Files selected for processing (4)
  • apps/mocksi-lite/content/ContentApp.tsx (3 hunks)
  • apps/mocksi-lite/content/EditMode/editMode.ts (2 hunks)
  • apps/mocksi-lite/content/Popup/CreateDemo/DemoItem.tsx (2 hunks)
  • apps/mocksi-lite/utils.ts (2 hunks)
Additional comments not posted (4)
apps/mocksi-lite/content/Popup/CreateDemo/DemoItem.tsx (2)

22-22: Ensure setEditorMode is properly handling the uuid as the recording ID.

Verification successful

The setEditorMode function in apps/mocksi-lite/content/EditMode/editMode.ts correctly handles the recordingId parameter, storing it in localStorage when turning on the editor mode and using it to persist modifications when turning off the editor mode. The uuid parameter passed to setEditorMode in apps/mocksi-lite/content/Popup/CreateDemo/DemoItem.tsx is thus properly utilized as the recording ID.

  • In setEditorMode, the recordingId is stored in localStorage when turnOn is true.
  • When turnOn is false, recordingId is used to call persistModifications.

Locations:

  • setEditorMode definition: apps/mocksi-lite/content/EditMode/editMode.ts
  • Usage with uuid: apps/mocksi-lite/content/Popup/CreateDemo/DemoItem.tsx
Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that `setEditorMode` correctly utilizes the `uuid` parameter.
# Test: Search for `setEditorMode` usage with `uuid` parameter.
ast-grep --lang typescript --pattern $'setEditorMode($_, $_) {
  $$$
}'

Length of output: 526



Script:

#!/bin/bash
# Description: Verify that `setEditorMode` correctly utilizes the `uuid` parameter.
# Test: Search for `setEditorMode` function usage and its implementation.
ast-grep --lang typescript --pattern 'setEditorMode($_, $_)' --json

# Additional step to locate the implementation of `setEditorMode`
ast-grep --lang typescript --pattern 'function setEditorMode($_, $_) { $$$ }' --json

Length of output: 162



Script:

#!/bin/bash
# Description: Locate all function definitions and usages to identify `setEditorMode` or similar functions.
# Search for all function definitions
ast-grep --lang typescript --pattern 'function $_($_$) { $$$ }' --json

# Search for all function calls
ast-grep --lang typescript --pattern '$_($_$)' --json

Length of output: 134



Script:

#!/bin/bash
# Description: Search for the term `setEditorMode` in the codebase to gather more context.
rg 'setEditorMode'

Length of output: 687



Script:

#!/bin/bash
# Description: Display the contents of `apps/mocksi-lite/content/EditMode/editMode.ts` to verify the utilization of `recordingId`.
cat apps/mocksi-lite/content/EditMode/editMode.ts

Length of output: 4601


38-38: Confirm whether loadAlterations properly handles the alterations data structure.

Verification successful

The loadAlterations function properly handles the alterations data structure by iterating over the array and modifying the DOM based on the selector and dom_after properties of each alteration object. The function covers cases where the selector includes an index and where it does not.

  • loadAlterations definition:
    export const loadAlterations = (alterations: Alteration[]) => {
      for (const alteration of alterations) {
        const { selector, dom_after } = alteration;
        const hasIndex = selector.match(/\[[0-9]+\]/);
        if (hasIndex) {
          const index: number = +hasIndex[0].replace("[", "").replace("]", "");
          const elemToModify = document.querySelectorAll(
            selector.replace(hasIndex[0], ""),
          )[index];
          //@ts-ignore
          elemToModify.innerHTML = dom_after;
        } else {
          const [elemToModify] = document.querySelectorAll(selector);
          //@ts-ignore
          elemToModify.innerHTML = dom_after;
        }
      }
    };

This confirms that loadAlterations is correctly implemented to handle the alterations data structure.

Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that `loadAlterations` is correctly implemented to handle alterations data structure.
# Test: Search for `loadAlterations` function definition and usage.
ast-grep --lang typescript --pattern $'function loadAlterations($_) {
  $$$
}'

Length of output: 79



Script:

#!/bin/bash
# Search for `loadAlterations` as a method within classes.
ast-grep --lang typescript --pattern $'class $_ {
  $$$
  loadAlterations($_) {
    $$$
  }
  $$$
}'

# Search for `loadAlterations` as an imported function.
rg --glob '**/*.ts*' 'import { loadAlterations' --files-with-matches

Length of output: 288



Script:

#!/bin/bash
# Correct the regex error and search for `loadAlterations` as an imported function.
rg --glob '**/*.ts*' 'import { loadAlterations' --files-with-matches

# Perform a broader search for `loadAlterations` definitions.
rg --glob '**/*.ts*' 'loadAlterations'

Length of output: 525



Script:

#!/bin/bash
# Extract the definition of `loadAlterations` from `apps/mocksi-lite/utils.ts`.
rg --glob 'apps/mocksi-lite/utils.ts' 'export const loadAlterations'

Length of output: 158



Script:

#!/bin/bash
# Extract the entire implementation of `loadAlterations` from `apps/mocksi-lite/utils.ts`.
awk '/export const loadAlterations =/,/}/' apps/mocksi-lite/utils.ts

Length of output: 221



Script:

#!/bin/bash
# Extract the entire implementation of `loadAlterations` from `apps/mocksi-lite/utils.ts`.
awk '/export const loadAlterations =/,/^}/' apps/mocksi-lite/utils.ts

Length of output: 659

apps/mocksi-lite/utils.ts (2)

14-16: Correct the typographical error in the type name DOMModificationsType.

- type DOMModifcationsType = {
+ type DOMModificationsType = {

Likely invalid or redundant comment.


34-48: Ensure that commandsExecuted is correctly utilized and that SaveModificationCommand properly implements the command pattern.

Comment on lines 91 to 95
elemToModify.innerHTML = dom_after;
} else {
const [elemToModify] = document.querySelectorAll(querySelector);
const [elemToModify] = document.querySelectorAll(selector);
//@ts-ignore
elemToModify.innerHTML = value;
elemToModify.innerHTML = dom_after;
Copy link

@coderabbitai coderabbitai bot Jun 13, 2024

Choose a reason for hiding this comment

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

Consider sanitizing dom_after to prevent potential XSS attacks when assigning to innerHTML.

+ const safeDomAfter = sanitizeHtml(dom_after); // Ensure you have a sanitizeHtml function available
  elemToModify.innerHTML = safeDomAfter;
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
elemToModify.innerHTML = dom_after;
} else {
const [elemToModify] = document.querySelectorAll(querySelector);
const [elemToModify] = document.querySelectorAll(selector);
//@ts-ignore
elemToModify.innerHTML = value;
elemToModify.innerHTML = dom_after;
elemToModify.innerHTML = dom_after;
} else {
const [elemToModify] = document.querySelectorAll(selector);
//@ts-ignore
const safeDomAfter = sanitizeHtml(dom_after); // Ensure you have a sanitizeHtml function available
elemToModify.innerHTML = safeDomAfter;

Copy link
Contributor

Choose a reason for hiding this comment

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

If you want to add this, just use https://github.com/cure53/DOMPurify or similar library.

Copy link

Choose a reason for hiding this comment

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

@elg0nz, thanks for recommending the use of DOMPurify to sanitize the dom_after content before assigning it to innerHTML. Integrating DOMPurify can significantly enhance the security by ensuring that the content is free from potentially malicious scripts.

Here’s a quick example of how you might integrate DOMPurify in the existing code:

import DOMPurify from 'dompurify';

// v2 of loading alterations, this is from backend
export const loadAlterations = (alterations: Alteration[]) => {
    for (const alteration of alterations) {
        const { selector, dom_after } = alteration;
        const hasIndex = selector.match(/\[[0-9]+\]/);
        if (hasIndex) {
            const index = +hasIndex[0].replace("[", "").replace("]", "");
            const elemToModify = document.querySelectorAll(selector.replace(hasIndex[0], ""))[index];
            elemToModify.innerHTML = DOMPurify.sanitize(dom_after);  // Sanitized content
        } else {
            const [elemToModify] = document.querySelectorAll(selector);
            elemToModify.innerHTML = DOMPurify.sanitize(dom_after);  // Sanitized content
        }
    }
};

If you need any further assistance with setting this up or have any other concerns, feel free to let me know!


Learnings added
User: elg0nz
PR: Mocksi/HARlighter#48
File: apps/mocksi-lite/utils.ts:0-0
Timestamp: 2024-06-13T22:08:01.155Z
Learning: Use DOMPurify to sanitize HTML content before assigning it to `innerHTML` to prevent XSS attacks.

Note: Learnings are effective only in the context of specific code segments. To apply general coding guidelines, please configure review instructions.

Comment on lines 51 to 69
export const persistModifications = (recordingId: string) => {
const alterations: Alteration[] = Object.entries<{
nextText: string;
previousText: string;
}>(JSON.parse(localStorage.getItem(MOCKSI_MODIFICATIONS) || "{}")).map(
([querySelector, { nextText, previousText }]) => ({
selector: querySelector,
action: previousText ? "modified" : "added",
dom_before: previousText || "",
dom_after: nextText,
}),
);
for (const modification of Object.entries(modifications)) {
// value here is encoded, SHOULD NOT be a security risk to put it in the innerHTML
const [querySelector, value] = modification;
const hasIndex = querySelector.match(/\[[0-9]+\]/);
const updated_timestamp = new Date();
sendMessage("updateDemo", {
id: recordingId,
recording: { updated_timestamp, alterations },
});
localStorage.removeItem(MOCKSI_MODIFICATIONS);
};
Copy link

@coderabbitai coderabbitai bot Jun 13, 2024

Choose a reason for hiding this comment

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

Add error handling around the JSON.parse operation to prevent potential runtime errors.

+ try {
+     const modifications = JSON.parse(localStorage.getItem(MOCKSI_MODIFICATIONS) || "{}");
+     // existing code
+ } catch (error) {
+     console.error("Error parsing modifications:", error);
+ }

Committable suggestion was skipped due to low confidence.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, let's start adding more error handling. Our Rollbar is already full of exceptions 🙃

Copy link

Choose a reason for hiding this comment

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

@elg0nz, great to hear that we're on the same page regarding the importance of robust error handling! Here's a detailed example of how we can implement error handling around the JSON.parse operation in the persistModifications function to prevent runtime errors and potentially reduce the number of exceptions:

export const persistModifications = (recordingId: string) => {
    try {
        const modificationsString = localStorage.getItem(MOCKSI_MODIFICATIONS) || "{}";
        const modifications = JSON.parse(modificationsString);
        const alterations = Object.entries<{
            nextText: string;
            previousText: string;
        }>(modifications).map(([querySelector, { nextText, previousText }]) => ({
            selector: querySelector,
            action: previousText ? "modified" : "added",
            dom_before: previousText || "",
            dom_after: nextText,
        }));
        const updated_timestamp = new Date();
        sendMessage("updateDemo", {
            id: recordingId,
            recording: { updated_timestamp, alterations },
        });
        localStorage.removeItem(MOCKSI_MODIFICATIONS);
    } catch (error) {
        console.error("Failed to parse modifications from localStorage:", error);
    }
};

This snippet wraps the JSON parsing and the subsequent logic in a try block, catching and logging any errors that occur during the parsing process. This should help in mitigating the impact of corrupt or malformed data in localStorage. Let me know if this aligns with your expectations or if there are other areas where you think we could improve error handling!

Comment on lines +50 to +53
const handleUpdate = () => {
const id = localStorage.getItem(MOCKSI_RECORDING_ID);
sendMessage("updateDemo", { id });
};
Copy link

Choose a reason for hiding this comment

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

Add keyboard accessibility to the "Done" button to support keyboard-only users.

- <div className="cursor-pointer text-[#009875]" onClick={handleUpdate}>
+ <div className="cursor-pointer text-[#009875]" onClick={handleUpdate} onKeyUp={(event) => event.key === 'Enter' && handleUpdate()}>
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleUpdate = () => {
const id = localStorage.getItem(MOCKSI_RECORDING_ID);
sendMessage("updateDemo", { id });
};
const handleUpdate = () => {
const id = localStorage.getItem(MOCKSI_RECORDING_ID);
sendMessage("updateDemo", { id });
};
<div className="cursor-pointer text-[#009875]" onClick={handleUpdate} onKeyUp={(event) => event.key === 'Enter' && handleUpdate()}>

Comment on lines 50 to 64
export const setEditorMode = (turnOn: boolean, recordingId?: string) => {
if (turnOn) {
if (recordingId) localStorage.setItem(MOCKSI_RECORDING_ID, recordingId);
localStorage.setItem(MOCKSI_RECORDING_STATE, RecordingState.EDITING);
blockNodes();
document.body.addEventListener("dblclick", onDoubleClickText);
} else {
if (recordingId) {
persistModifications(recordingId);
} else {
localStorage.removeItem(MOCKSI_MODIFICATIONS);
// undoModifications()
}
localStorage.setItem(MOCKSI_RECORDING_STATE, RecordingState.CREATE);
localStorage.removeItem(MOCKSI_RECORDING_ID);
Copy link

@coderabbitai coderabbitai bot Jun 13, 2024

Choose a reason for hiding this comment

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

Refactor setEditorMode to reduce complexity and improve error handling.

+ try {
+     const modifications: DOMModificationsType = JSON.parse(
+         localStorage.getItem(MOCKSI_MODIFICATIONS) || "{}",
+     );
+     modifications.forEach((modification, querySelector) => {
+         const element = document.querySelector(querySelector);
+         if (element) {
+             element.innerHTML = modification.nextText;
+         }
+     });
+ } catch (error) {
+     console.error("Failed to load modifications:", error);
+ }

Committable suggestion was skipped due to low confidence.

Copy link
Contributor

Choose a reason for hiding this comment

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

This is a good suggestion though

Copy link

Choose a reason for hiding this comment

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

@elg0nz, thank you for the positive feedback! If you find the suggestion fits well with your project goals, feel free to implement it. Should you need any more assistance or have further questions, just let me know!

Copy link

@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: 4

Outside diff range and nitpick comments (1)
apps/mocksi-lite/content/Popup/CreateDemo/DemoItem.tsx (1)

14-20: Ensure the uuid prop is documented in the component's prop types.

Adding documentation for the new uuid prop will help other developers understand its purpose and usage within this component.

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 0e189d9 and cc16cf6.

Files selected for processing (4)
  • apps/mocksi-lite/content/ContentApp.tsx (3 hunks)
  • apps/mocksi-lite/content/EditMode/editMode.ts (2 hunks)
  • apps/mocksi-lite/content/Popup/CreateDemo/DemoItem.tsx (2 hunks)
  • apps/mocksi-lite/utils.ts (2 hunks)
Additional comments not posted (4)
apps/mocksi-lite/content/Popup/CreateDemo/DemoItem.tsx (2)

22-22: Correctly pass uuid to setEditorMode to manage editor states based on unique identifiers.

This is a good use of the uuid to ensure that the editor mode is set correctly for specific instances.


38-39: Verify that loadAlterations correctly handles the alterations data structure.

Ensure that the alterations are structured as expected by the loadAlterations function.

Verification successful

The loadAlterations function has been located in apps/mocksi-lite/utils.ts and appears to handle the alterations data structure correctly by iterating over the alterations array and manipulating DOM elements based on the provided selectors and content.

  • loadAlterations is defined as an arrow function.
  • It iterates over the alterations array.
  • For each alteration, it extracts selector and dom_after.
  • It checks for indexed selectors and appropriately modifies the inner HTML of the targeted elements.

Based on this analysis, the function appears to handle the alterations data structure as expected.

  • File: apps/mocksi-lite/utils.ts
  • Lines: 81-98
Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify the structure of alterations data passed to `loadAlterations`.

# Test: Search for the `loadAlterations` function usage. Expect: Correct handling of the data structure.
ast-grep --lang typescript --pattern $'function loadAlterations($_) {
  $$$
  $_.map($_ => $_.alterations)
  $$$
}'

Length of output: 116



Script:

#!/bin/bash
# Search for the definition of the `loadAlterations` function in the TypeScript files.
ast-grep --lang typescript --pattern 'function loadAlterations($_) {
  $$$
}'

Length of output: 79



Script:

#!/bin/bash
# Search for `loadAlterations` defined as an arrow function or a method within an object.
ast-grep --lang typescript --pattern 'const loadAlterations = ($_) => {
  $$$
}'

ast-grep --lang typescript --pattern 'loadAlterations($_) {
  $$$
}'

Length of output: 1751

apps/mocksi-lite/content/ContentApp.tsx (1)

95-115: Add keyboard accessibility to the "Done" button to support keyboard-only users.

- <div className="cursor-pointer text-[#009875]" onClick={handleUpdate}>
+ <div className="cursor-pointer text-[#009875]" onClick={handleUpdate} onKeyUp={(event) => event.key === 'Enter' && handleUpdate()}>

It's important to ensure that interactive elements are accessible via keyboard to support a wider range of users.

Likely invalid or redundant comment.

apps/mocksi-lite/content/EditMode/editMode.ts (1)

50-64: Ensure consistency in handling the recordingId parameter in setEditorMode.

The update to include recordingId enhances the functionality by allowing tracking of edits associated with specific recordings.

Comment on lines 39 to 46
previousText: string,
) => {
const prevModifications = JSON.parse(
localStorage.getItem(MOCKSI_MODIFICATIONS) || "{}",
);
let keyToSave = parentElement.localName;
if (parentElement.id) {
keyToSave += `#${parentElement.id}`;
}
if (parentElement.className) {
keyToSave += `.${parentElement.className}`;
}
// here we check if the key we built is sufficient to get an unique element when querying
const elements = document.querySelectorAll(keyToSave);
if (elements.length === 1) {
localStorage.setItem(
MOCKSI_MODIFICATIONS,
JSON.stringify({ ...prevModifications, [keyToSave]: newText }),
);
} else {
// if not unique, we search for the index and put it on the key.
keyToSave += `[${[...elements].indexOf(parentElement)}]`;
localStorage.setItem(
MOCKSI_MODIFICATIONS,
JSON.stringify({ ...prevModifications, [keyToSave]: newText }),
);
}
// to successfully implement the do/undo commands, we must save somewhere in memory all commands being executed.
const saveModificationCommand = new SaveModificationCommand(localStorage, {
keyToSave: buildQuerySelector(parentElement),
nextText: newText,
previousText, // todo test previous
});
Copy link

@coderabbitai coderabbitai bot Jun 13, 2024

Choose a reason for hiding this comment

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

Address the TODO comment about testing the previousText functionality.

Would you like me to help by writing the unit tests for this functionality?

Copy link
Contributor

Choose a reason for hiding this comment

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

@CodeRabbit write unit tests

Copy link

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment on lines 51 to 69
export const persistModifications = (recordingId: string) => {
const alterations: Alteration[] = Object.entries<{
nextText: string;
previousText: string;
}>(JSON.parse(localStorage.getItem(MOCKSI_MODIFICATIONS) || "{}")).map(
([querySelector, { nextText, previousText }]) => ({
selector: querySelector,
action: previousText ? "modified" : "added",
dom_before: previousText || "",
dom_after: nextText,
}),
);
for (const modification of Object.entries(modifications)) {
// value here is encoded, SHOULD NOT be a security risk to put it in the innerHTML
const [querySelector, value] = modification;
const hasIndex = querySelector.match(/\[[0-9]+\]/);
const updated_timestamp = new Date();
sendMessage("updateDemo", {
id: recordingId,
recording: { updated_timestamp, alterations },
});
localStorage.removeItem(MOCKSI_MODIFICATIONS);
};
Copy link

Choose a reason for hiding this comment

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

Add error handling around the JSON.parse operation in persistModifications to prevent potential runtime errors.

+ try {
+     const modifications = JSON.parse(localStorage.getItem(MOCKSI_MODIFICATIONS) || "{}");
+     // existing code
+ } catch (error) {
+     console.error("Error parsing modifications:", error);
+ }

Ensure robust error handling to manage cases where the localStorage data might not be in the expected format.

Committable suggestion was skipped due to low confidence.

Comment on lines 81 to 95
export const loadAlterations = (alterations: Alteration[]) => {
for (const alteration of alterations) {
const { selector, dom_after } = alteration;
const hasIndex = selector.match(/\[[0-9]+\]/);
if (hasIndex) {
const index: number = +hasIndex[0].replace("[", "").replace("]", "");
const elemToModify = document.querySelectorAll(
querySelector.replace(hasIndex[0], ""),
selector.replace(hasIndex[0], ""),
)[index];
//@ts-ignore
elemToModify.innerHTML = value;
elemToModify.innerHTML = dom_after;
} else {
const [elemToModify] = document.querySelectorAll(querySelector);
const [elemToModify] = document.querySelectorAll(selector);
//@ts-ignore
elemToModify.innerHTML = value;
elemToModify.innerHTML = dom_after;
Copy link

Choose a reason for hiding this comment

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

Consider sanitizing dom_after to prevent potential XSS attacks when assigning to innerHTML.

+ const safeDomAfter = sanitizeHtml(dom_after); // Ensure you have a sanitizeHtml function available
  elemToModify.innerHTML = safeDomAfter;

It's crucial to sanitize any data that is dynamically inserted into the DOM to prevent XSS vulnerabilities.

Committable suggestion was skipped due to low confidence.

Comment on lines +50 to +53
const handleUpdate = () => {
const id = localStorage.getItem(MOCKSI_RECORDING_ID);
sendMessage("updateDemo", { id });
};
Copy link

Choose a reason for hiding this comment

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

Ensure error handling is implemented in handleUpdate when retrieving the ID from localStorage.

Consider adding a check to ensure that the ID retrieved from localStorage is not null before proceeding to send the message.

Copy link

@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: 5

Outside diff range and nitpick comments (1)
apps/mocksi-lite/utils.ts (1)

Line range hint 117-126: Enhance error handling in sendMessage to cover more specific error scenarios and improve reliability.

+ chrome.runtime.sendMessage({ message, body }, (response) => {
+     if (!response) {
+         console.error("No response from background script");
+         return;
+     }
+     if (response?.status !== "success") {
+         console.error("Failed to send message to background script");
+         return;
+     }
+ });
Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between cc16cf6 and d284e1a.

Files selected for processing (4)
  • apps/mocksi-lite/commands/Command.ts (1 hunks)
  • apps/mocksi-lite/content/EditMode/actions.ts (1 hunks)
  • apps/mocksi-lite/content/EditMode/editMode.ts (2 hunks)
  • apps/mocksi-lite/utils.ts (2 hunks)
Files skipped from review as they are similar to previous changes (3)
  • apps/mocksi-lite/commands/Command.ts
  • apps/mocksi-lite/content/EditMode/actions.ts
  • apps/mocksi-lite/content/EditMode/editMode.ts
Additional comments not posted (1)
apps/mocksi-lite/utils.ts (1)

34-48: The implementation of saveModification using the command pattern is well-executed.

import {
MOCKSI_MODIFICATIONS,
MOCKSI_RECORDING_STATE,
RecordingState,
SignupURL,
} from "./consts";

type DOMModifcationsType = {
Copy link

@coderabbitai coderabbitai bot Jun 13, 2024

Choose a reason for hiding this comment

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

Correct the typographical error in the type name.

- type DOMModifcationsType = {
+ type DOMModificationsType = {
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
type DOMModifcationsType = {
type DOMModificationsType = {

Copy link
Contributor

Choose a reason for hiding this comment

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

^ yup, let's fix that

Copy link

Choose a reason for hiding this comment

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

@elg0nz Great! You can proceed with the change. Make sure to update all references to DOMModifcationsType to DOMModificationsType in your codebase to maintain consistency. If you need any further assistance, feel free to ask!

Comment on lines +70 to +73
export const undoModifications = () => {
loadModifications();
localStorage.removeItem(MOCKSI_MODIFICATIONS);
};
Copy link

Choose a reason for hiding this comment

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

Consider reordering operations in undoModifications to ensure modifications are loaded before removing the item from localStorage.

+ loadModifications();
+ localStorage.removeItem(MOCKSI_MODIFICATIONS);

Committable suggestion was skipped due to low confidence.

Comment on lines 50 to 68
export const persistModifications = (recordingId: string) => {
const alterations: Alteration[] = Object.entries<{
nextText: string;
previousText: string;
}>(JSON.parse(localStorage.getItem(MOCKSI_MODIFICATIONS) || "{}")).map(
([querySelector, { nextText, previousText }]) => ({
selector: querySelector,
action: previousText ? "modified" : "added",
dom_before: previousText || "",
dom_after: nextText,
}),
);
let keyToSave = parentElement.localName;
if (parentElement.id) {
keyToSave += `#${parentElement.id}`;
}
if (parentElement.className) {
keyToSave += `.${parentElement.className}`;
}
// here we check if the key we built is sufficient to get an unique element when querying
const elements = document.querySelectorAll(keyToSave);
if (elements.length === 1) {
localStorage.setItem(
MOCKSI_MODIFICATIONS,
JSON.stringify({ ...prevModifications, [keyToSave]: newText }),
);
} else {
// if not unique, we search for the index and put it on the key.
keyToSave += `[${[...elements].indexOf(parentElement)}]`;
localStorage.setItem(
MOCKSI_MODIFICATIONS,
JSON.stringify({ ...prevModifications, [keyToSave]: newText }),
);
const updated_timestamp = new Date();
sendMessage("updateDemo", {
id: recordingId,
recording: { updated_timestamp, alterations },
});
// localStorage.removeItem(MOCKSI_MODIFICATIONS);
};
Copy link

Choose a reason for hiding this comment

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

Add error handling around the JSON.parse operation to prevent potential runtime errors.

+ try {
+     const modifications = JSON.parse(localStorage.getItem(MOCKSI_MODIFICATIONS) || "{}");
+     // existing code
+ } catch (error) {
+     console.error("Error parsing modifications:", error);
+ }
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const persistModifications = (recordingId: string) => {
const alterations: Alteration[] = Object.entries<{
nextText: string;
previousText: string;
}>(JSON.parse(localStorage.getItem(MOCKSI_MODIFICATIONS) || "{}")).map(
([querySelector, { nextText, previousText }]) => ({
selector: querySelector,
action: previousText ? "modified" : "added",
dom_before: previousText || "",
dom_after: nextText,
}),
);
let keyToSave = parentElement.localName;
if (parentElement.id) {
keyToSave += `#${parentElement.id}`;
}
if (parentElement.className) {
keyToSave += `.${parentElement.className}`;
}
// here we check if the key we built is sufficient to get an unique element when querying
const elements = document.querySelectorAll(keyToSave);
if (elements.length === 1) {
localStorage.setItem(
MOCKSI_MODIFICATIONS,
JSON.stringify({ ...prevModifications, [keyToSave]: newText }),
);
} else {
// if not unique, we search for the index and put it on the key.
keyToSave += `[${[...elements].indexOf(parentElement)}]`;
localStorage.setItem(
MOCKSI_MODIFICATIONS,
JSON.stringify({ ...prevModifications, [keyToSave]: newText }),
);
const updated_timestamp = new Date();
sendMessage("updateDemo", {
id: recordingId,
recording: { updated_timestamp, alterations },
});
// localStorage.removeItem(MOCKSI_MODIFICATIONS);
};
export const persistModifications = (recordingId: string) => {
try {
const alterations: Alteration[] = Object.entries<{
nextText: string;
previousText: string;
}>(JSON.parse(localStorage.getItem(MOCKSI_MODIFICATIONS) || "{}")).map(
([querySelector, { nextText, previousText }]) => ({
selector: querySelector,
action: previousText ? "modified" : "added",
dom_before: previousText || "",
dom_after: nextText,
}),
);
const updated_timestamp = new Date();
sendMessage("updateDemo", {
id: recordingId,
recording: { updated_timestamp, alterations },
});
// localStorage.removeItem(MOCKSI_MODIFICATIONS);
} catch (error) {
console.error("Error parsing modifications:", error);
}
};

Comment on lines +76 to +91
export const loadAlterations = (alterations: Alteration[]) => {
for (const alteration of alterations) {
const { selector, dom_after } = alteration;
const hasIndex = selector.match(/\[[0-9]+\]/);
if (hasIndex) {
const index: number = +hasIndex[0].replace("[", "").replace("]", "");
const elemToModify = document.querySelectorAll(
selector.replace(hasIndex[0], ""),
)[index];
//@ts-ignore
elemToModify.innerHTML = dom_after;
} else {
const [elemToModify] = document.querySelectorAll(selector);
//@ts-ignore
elemToModify.innerHTML = dom_after;
}
Copy link

Choose a reason for hiding this comment

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

Consider sanitizing dom_after to prevent potential XSS attacks when assigning to innerHTML.

+ const safeDomAfter = sanitizeHtml(dom_after); // Ensure you have a sanitizeHtml function available
  elemToModify.innerHTML = safeDomAfter;
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const loadAlterations = (alterations: Alteration[]) => {
for (const alteration of alterations) {
const { selector, dom_after } = alteration;
const hasIndex = selector.match(/\[[0-9]+\]/);
if (hasIndex) {
const index: number = +hasIndex[0].replace("[", "").replace("]", "");
const elemToModify = document.querySelectorAll(
selector.replace(hasIndex[0], ""),
)[index];
//@ts-ignore
elemToModify.innerHTML = dom_after;
} else {
const [elemToModify] = document.querySelectorAll(selector);
//@ts-ignore
elemToModify.innerHTML = dom_after;
}
export const loadAlterations = (alterations: Alteration[]) => {
for (const alteration of alterations) {
const { selector, dom_after } = alteration;
const hasIndex = selector.match(/\[[0-9]+\]/);
if (hasIndex) {
const index: number = +hasIndex[0].replace("[", "").replace("]", "");
const elemToModify = document.querySelectorAll(
selector.replace(hasIndex[0], ""),
)[index];
//@ts-ignore
const safeDomAfter = sanitizeHtml(dom_after); // Ensure you have a sanitizeHtml function available
elemToModify.innerHTML = safeDomAfter;
} else {
const [elemToModify] = document.querySelectorAll(selector);
//@ts-ignore
const safeDomAfter = sanitizeHtml(dom_after); // Ensure you have a sanitizeHtml function available
elemToModify.innerHTML = safeDomAfter;
}
}
}

Comment on lines 95 to 114
// This is from localStorage
export const loadModifications = () => {
const modifications = JSON.parse(
const modifications: DOMModifcationsType = JSON.parse(
localStorage.getItem(MOCKSI_MODIFICATIONS) || "{}",
);
for (const modification of Object.entries(modifications)) {
// value here is encoded, SHOULD NOT be a security risk to put it in the innerHTML
const [querySelector, value] = modification;
const [querySelector, { previousText }] = modification;
const hasIndex = querySelector.match(/\[[0-9]+\]/);
if (hasIndex) {
const index: number = +hasIndex[0].replace("[", "").replace("]", "");
const elemToModify = document.querySelectorAll(
querySelector.replace(hasIndex[0], ""),
)[index];
//@ts-ignore
elemToModify.innerHTML = value;
elemToModify.innerHTML = previousText;
} else {
const [elemToModify] = document.querySelectorAll(querySelector);
//@ts-ignore
elemToModify.innerHTML = value;
elemToModify.innerHTML = previousText;
Copy link

Choose a reason for hiding this comment

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

Ensure that the content assigned to innerHTML is sanitized to prevent XSS vulnerabilities.

+ const safePreviousText = sanitizeHtml(previousText); // Ensure you have a sanitizeHtml function available
  elemToModify.innerHTML = safePreviousText;
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// This is from localStorage
export const loadModifications = () => {
const modifications = JSON.parse(
const modifications: DOMModifcationsType = JSON.parse(
localStorage.getItem(MOCKSI_MODIFICATIONS) || "{}",
);
for (const modification of Object.entries(modifications)) {
// value here is encoded, SHOULD NOT be a security risk to put it in the innerHTML
const [querySelector, value] = modification;
const [querySelector, { previousText }] = modification;
const hasIndex = querySelector.match(/\[[0-9]+\]/);
if (hasIndex) {
const index: number = +hasIndex[0].replace("[", "").replace("]", "");
const elemToModify = document.querySelectorAll(
querySelector.replace(hasIndex[0], ""),
)[index];
//@ts-ignore
elemToModify.innerHTML = value;
elemToModify.innerHTML = previousText;
} else {
const [elemToModify] = document.querySelectorAll(querySelector);
//@ts-ignore
elemToModify.innerHTML = value;
elemToModify.innerHTML = previousText;
// This is from localStorage
export const loadModifications = () => {
const modifications: DOMModifcationsType = JSON.parse(
localStorage.getItem(MOCKSI_MODIFICATIONS) || "{}",
);
for (const modification of Object.entries(modifications)) {
// value here is encoded, SHOULD NOT be a security risk to put it in the innerHTML
const [querySelector, { previousText }] = modification;
const hasIndex = querySelector.match(/\[[0-9]+\]/);
if (hasIndex) {
const index: number = +hasIndex[0].replace("[", "").replace("]", "");
const elemToModify = document.querySelectorAll(
querySelector.replace(hasIndex[0], ""),
)[index];
//@ts-ignore
const safePreviousText = sanitizeHtml(previousText); // Ensure you have a sanitizeHtml function available
elemToModify.innerHTML = safePreviousText;
} else {
const [elemToModify] = document.querySelectorAll(querySelector);
//@ts-ignore
const safePreviousText = sanitizeHtml(previousText); // Ensure you have a sanitizeHtml function available
elemToModify.innerHTML = safePreviousText;

Copy link
Contributor

@elg0nz elg0nz left a comment

Choose a reason for hiding this comment

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

Looking really good!
Just add the JSON parse error handling and I'll approve this PR :)

@@ -0,0 +1,72 @@
import { MOCKSI_MODIFICATIONS } from "../consts";

export interface Command {
Copy link
Contributor

Choose a reason for hiding this comment

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

Love this!
I'm literally working on writing a spec for some details about this interface, and this is a great start! Thanks

Comment on lines 50 to 64
export const setEditorMode = (turnOn: boolean, recordingId?: string) => {
if (turnOn) {
if (recordingId) localStorage.setItem(MOCKSI_RECORDING_ID, recordingId);
localStorage.setItem(MOCKSI_RECORDING_STATE, RecordingState.EDITING);
blockNodes();
document.body.addEventListener("dblclick", onDoubleClickText);
} else {
if (recordingId) {
persistModifications(recordingId);
} else {
localStorage.removeItem(MOCKSI_MODIFICATIONS);
// undoModifications()
}
localStorage.setItem(MOCKSI_RECORDING_STATE, RecordingState.CREATE);
localStorage.removeItem(MOCKSI_RECORDING_ID);
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a good suggestion though

Comment on lines 21 to 25
// const previousNode = nodeWithTextArea?.cloneNode(true)
// const asd = previousNode?.parentElement?.replaceChild(
// document.createTextNode(oldValue),
// previousNode
// )
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't fully disagree with what the🐰 is saying. But for now, it’s fine so we can focus on shipping the MVP.

That being said, we’ll be stricter in future PRs

import {
MOCKSI_MODIFICATIONS,
MOCKSI_RECORDING_STATE,
RecordingState,
SignupURL,
} from "./consts";

type DOMModifcationsType = {
Copy link
Contributor

Choose a reason for hiding this comment

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

^ yup, let's fix that

Comment on lines 14 to 16
type DOMModifcationsType = {
[querySelector: string]: { nextText: string; previousText: string };
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Also this one

Comment on lines 39 to 46
previousText: string,
) => {
const prevModifications = JSON.parse(
localStorage.getItem(MOCKSI_MODIFICATIONS) || "{}",
);
let keyToSave = parentElement.localName;
if (parentElement.id) {
keyToSave += `#${parentElement.id}`;
}
if (parentElement.className) {
keyToSave += `.${parentElement.className}`;
}
// here we check if the key we built is sufficient to get an unique element when querying
const elements = document.querySelectorAll(keyToSave);
if (elements.length === 1) {
localStorage.setItem(
MOCKSI_MODIFICATIONS,
JSON.stringify({ ...prevModifications, [keyToSave]: newText }),
);
} else {
// if not unique, we search for the index and put it on the key.
keyToSave += `[${[...elements].indexOf(parentElement)}]`;
localStorage.setItem(
MOCKSI_MODIFICATIONS,
JSON.stringify({ ...prevModifications, [keyToSave]: newText }),
);
}
// to successfully implement the do/undo commands, we must save somewhere in memory all commands being executed.
const saveModificationCommand = new SaveModificationCommand(localStorage, {
keyToSave: buildQuerySelector(parentElement),
nextText: newText,
previousText, // todo test previous
});
Copy link
Contributor

Choose a reason for hiding this comment

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

@CodeRabbit write unit tests

Comment on lines 51 to 69
export const persistModifications = (recordingId: string) => {
const alterations: Alteration[] = Object.entries<{
nextText: string;
previousText: string;
}>(JSON.parse(localStorage.getItem(MOCKSI_MODIFICATIONS) || "{}")).map(
([querySelector, { nextText, previousText }]) => ({
selector: querySelector,
action: previousText ? "modified" : "added",
dom_before: previousText || "",
dom_after: nextText,
}),
);
for (const modification of Object.entries(modifications)) {
// value here is encoded, SHOULD NOT be a security risk to put it in the innerHTML
const [querySelector, value] = modification;
const hasIndex = querySelector.match(/\[[0-9]+\]/);
const updated_timestamp = new Date();
sendMessage("updateDemo", {
id: recordingId,
recording: { updated_timestamp, alterations },
});
localStorage.removeItem(MOCKSI_MODIFICATIONS);
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, let's start adding more error handling. Our Rollbar is already full of exceptions 🙃

Comment on lines 91 to 95
elemToModify.innerHTML = dom_after;
} else {
const [elemToModify] = document.querySelectorAll(querySelector);
const [elemToModify] = document.querySelectorAll(selector);
//@ts-ignore
elemToModify.innerHTML = value;
elemToModify.innerHTML = dom_after;
Copy link
Contributor

Choose a reason for hiding this comment

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

If you want to add this, just use https://github.com/cure53/DOMPurify or similar library.

Comment on lines 39 to 48
previousText: string,
) => {
const prevModifications = JSON.parse(
localStorage.getItem(MOCKSI_MODIFICATIONS) || "{}",
);
let keyToSave = parentElement.localName;
if (parentElement.id) {
keyToSave += `#${parentElement.id}`;
}
if (parentElement.className) {
keyToSave += `.${parentElement.className}`;
}
// here we check if the key we built is sufficient to get an unique element when querying
const elements = document.querySelectorAll(keyToSave);
if (elements.length === 1) {
localStorage.setItem(
MOCKSI_MODIFICATIONS,
JSON.stringify({ ...prevModifications, [keyToSave]: newText }),
);
} else {
// if not unique, we search for the index and put it on the key.
keyToSave += `[${[...elements].indexOf(parentElement)}]`;
localStorage.setItem(
MOCKSI_MODIFICATIONS,
JSON.stringify({ ...prevModifications, [keyToSave]: newText }),
);
}
// to successfully implement the do/undo commands, we must save somewhere in memory all commands being executed.
const saveModificationCommand = new SaveModificationCommand(localStorage, {
keyToSave: buildQuerySelector(parentElement),
nextText: newText,
previousText, // todo test previous
});
commandsExecuted.push(saveModificationCommand);
saveModificationCommand.execute();
Copy link
Contributor

Choose a reason for hiding this comment

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

yes

* [NC]: adds url on create recording and edit and play button validation

* [NC]: biome fixes
Copy link

@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

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between d284e1a and 1fb971b.

Files selected for processing (3)
  • apps/mocksi-lite/background.ts (5 hunks)
  • apps/mocksi-lite/content/Popup/CreateDemo/DemoItem.tsx (2 hunks)
  • apps/mocksi-lite/content/Popup/CreateDemo/Form.tsx (1 hunks)
Files skipped from review due to trivial changes (1)
  • apps/mocksi-lite/content/Popup/CreateDemo/DemoItem.tsx
Files skipped from review as they are similar to previous changes (1)
  • apps/mocksi-lite/background.ts

@@ -49,7 +49,7 @@ const Form = ({ onCancel }: FormProps) => {
/>
</div>
<div className={"mt-[42px] flex justify-end gap-4"}>
<Button onClick={onCancel} variant={Variant.secondary}>
<Button onClick={() => onCancel()} variant={Variant.secondary}>
Copy link

Choose a reason for hiding this comment

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

Consider simplifying the onClick handler for the "Cancel" button unless there are specific reasons for using an arrow function.

Using an arrow function is generally necessary when this binding is an issue, or when passing additional parameters dynamically. If onCancel does not utilize this and no additional parameters are passed, you could revert to the simpler onClick={onCancel}.

@NicoMorenoSirius
Copy link
Contributor

NicoMorenoSirius commented Jun 14, 2024

@elg0nz
I think is ready, please take a look!

And yes, this is a first glance of implementing command patterns.
Of course Feedback is welcome

Copy link
Contributor

@elg0nz elg0nz left a comment

Choose a reason for hiding this comment

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

🚀

@NicoMorenoSirius NicoMorenoSirius merged commit 6e15643 into main Jun 14, 2024
2 checks passed
@NicoMorenoSirius NicoMorenoSirius deleted the update-demo branch June 14, 2024 15:01
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.

None yet

3 participants