msyavuz commented on code in PR #35986:
URL: https://github.com/apache/superset/pull/35986#discussion_r2527814890


##########
superset-frontend/packages/superset-ui-core/src/components/AsyncAceEditor/AsyncAceEditor.test.tsx:
##########
@@ -99,3 +101,252 @@ test('renders a custom placeholder', () => {
 
   expect(screen.getByRole('paragraph')).toBeInTheDocument();
 });
+
+test('registers afterExec event listener for command handling', async () => {
+  const ref = createRef<AceEditor>();
+  const { container } = render(<SQLEditor ref={ref as React.Ref<never>} />);
+
+  await waitFor(() => {
+    expect(container.querySelector(selector)).toBeInTheDocument();
+  });
+
+  const editorInstance = ref.current?.editor;
+  expect(editorInstance).toBeDefined();
+
+  if (!editorInstance) return;
+
+  // Verify the commands object has the 'on' method (confirms event listener 
capability)
+  expect(editorInstance.commands).toHaveProperty('on');
+  expect(typeof editorInstance.commands.on).toBe('function');
+});
+
+test('moves autocomplete popup to parent container when triggered', async () 
=> {
+  const ref = createRef<AceEditor>();
+  const { container } = render(<SQLEditor ref={ref as React.Ref<never>} />);
+
+  await waitFor(() => {
+    expect(container.querySelector(selector)).toBeInTheDocument();
+  });
+
+  const editorInstance = ref.current?.editor;
+  expect(editorInstance).toBeDefined();
+
+  if (!editorInstance) return;
+
+  // Create a mock autocomplete popup in the editor container
+  const mockAutocompletePopup = document.createElement('div');
+  mockAutocompletePopup.className = 'ace_autocomplete';
+  editorInstance.container?.appendChild(mockAutocompletePopup);
+
+  const parentContainer =
+    editorInstance.container?.closest('#ace-editor') ??
+    editorInstance.container?.parentElement;
+
+  // Manually trigger the afterExec event with insertstring command using _emit
+  type CommandManagerWithEmit = typeof editorInstance.commands & {
+    _emit: (event: string, data: unknown) => void;
+  };
+  (editorInstance.commands as CommandManagerWithEmit)._emit('afterExec', {
+    command: { name: 'insertstring' },
+    args: ['SELECT'],
+  });
+
+  await waitFor(() => {
+    // Check that the popup has the data attribute set
+    expect(mockAutocompletePopup.dataset.aceAutocomplete).toBe('true');
+    // Check that the popup is in the parent container
+    expect(parentContainer?.contains(mockAutocompletePopup)).toBe(true);
+  });
+});
+
+test('moves autocomplete popup on startAutocomplete command event', async () 
=> {
+  const ref = createRef<AceEditor>();
+  const { container } = render(<SQLEditor ref={ref as React.Ref<never>} />);
+
+  await waitFor(() => {
+    expect(container.querySelector(selector)).toBeInTheDocument();
+  });
+
+  const editorInstance = ref.current?.editor;
+  expect(editorInstance).toBeDefined();
+
+  if (!editorInstance) return;
+
+  // Create a mock autocomplete popup
+  const mockAutocompletePopup = document.createElement('div');
+  mockAutocompletePopup.className = 'ace_autocomplete';
+  editorInstance.container?.appendChild(mockAutocompletePopup);
+
+  const parentContainer =
+    editorInstance.container?.closest('#ace-editor') ??
+    editorInstance.container?.parentElement;
+
+  // Manually trigger the afterExec event with startAutocomplete command
+  type CommandManagerWithEmit = typeof editorInstance.commands & {
+    _emit: (event: string, data: unknown) => void;
+  };
+  (editorInstance.commands as CommandManagerWithEmit)._emit('afterExec', {
+    command: { name: 'startAutocomplete' },
+  });
+
+  await waitFor(() => {
+    // Check that the popup has the data attribute set
+    expect(mockAutocompletePopup.dataset.aceAutocomplete).toBe('true');
+    // Check that the popup is in the parent container
+    expect(parentContainer?.contains(mockAutocompletePopup)).toBe(true);
+  });
+});
+
+test('does not move autocomplete popup on unrelated commands', async () => {
+  const ref = createRef<AceEditor>();
+  const { container } = render(<SQLEditor ref={ref as React.Ref<never>} />);
+
+  await waitFor(() => {
+    expect(container.querySelector(selector)).toBeInTheDocument();
+  });
+
+  const editorInstance = ref.current?.editor;
+  expect(editorInstance).toBeDefined();
+
+  if (!editorInstance) return;
+
+  // Create a mock autocomplete popup in the body
+  const mockAutocompletePopup = document.createElement('div');
+  mockAutocompletePopup.className = 'ace_autocomplete';
+  document.body.appendChild(mockAutocompletePopup);
+
+  const originalParent = mockAutocompletePopup.parentElement;
+
+  // Simulate an unrelated command (e.g., 'selectall')
+  editorInstance.commands.exec('selectall', editorInstance, {});
+
+  // Wait a bit to ensure no movement happens
+  await new Promise(resolve => {
+    setTimeout(resolve, 100);
+  });
+
+  // The popup should remain in its original location
+  expect(mockAutocompletePopup.parentElement).toBe(originalParent);
+
+  // Cleanup
+  document.body.removeChild(mockAutocompletePopup);
+});
+
+test('revalidates cached autocomplete popup when detached from DOM', async () 
=> {
+  const ref = createRef<AceEditor>();
+  const { container } = render(<SQLEditor ref={ref as React.Ref<never>} />);
+
+  await waitFor(() => {
+    expect(container.querySelector(selector)).toBeInTheDocument();
+  });
+
+  const editorInstance = ref.current?.editor;
+  expect(editorInstance).toBeDefined();
+
+  if (!editorInstance) return;
+
+  // Create first autocomplete popup
+  const firstPopup = document.createElement('div');
+  firstPopup.className = 'ace_autocomplete';
+  editorInstance.container?.appendChild(firstPopup);
+
+  // Trigger command to cache the first popup
+  editorInstance.commands.exec('insertstring', editorInstance, 'SELECT');
+
+  await waitFor(() => {
+    expect(firstPopup.dataset.aceAutocomplete).toBe('true');
+  });
+
+  // Remove the first popup from DOM (simulating ACE editor replacing it)
+  firstPopup.remove();
+
+  // Create a new autocomplete popup
+  const secondPopup = document.createElement('div');
+  secondPopup.className = 'ace_autocomplete';
+  editorInstance.container?.appendChild(secondPopup);
+
+  // Trigger command again - should find and move the new popup
+  editorInstance.commands.exec('insertstring', editorInstance, ' ');
+
+  await waitFor(() => {
+    expect(secondPopup.dataset.aceAutocomplete).toBe('true');
+    const parentContainer =
+      editorInstance.container?.closest('#ace-editor') ??
+      editorInstance.container?.parentElement;
+    expect(parentContainer?.contains(secondPopup)).toBe(true);
+  });
+});
+
+test('cleans up event listeners on unmount', async () => {
+  const ref = createRef<AceEditor>();
+  const { container, unmount } = render(<SQLEditor ref={ref as 
React.Ref<never>} />);

Review Comment:
   ```suggestion
     const { container, unmount } = render(
       <SQLEditor ref={ref as React.Ref<never>} />,
     );
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to