- {githubService.isAuthenticated ? (
+ {githubService.isAuth() ? (
✅ Authenticated - Issues will be submitted directly to GitHub
diff --git a/src/components/ContextualHelpMascot.js b/src/components/ContextualHelpMascot.js
index f1c57ef53..71aac27f5 100644
--- a/src/components/ContextualHelpMascot.js
+++ b/src/components/ContextualHelpMascot.js
@@ -7,6 +7,7 @@ import cacheManagementService from '../services/cacheManagementService';
import issueTrackingService from '../services/issueTrackingService';
import githubService from '../services/githubService';
import HelpModal from './HelpModal';
+import BugReportForm from './BugReportForm';
import TrackedItemsViewer from './TrackedItemsViewer';
import LanguageSelector from './LanguageSelector';
import useThemeImage from '../hooks/useThemeImage';
@@ -47,6 +48,7 @@ const ContextualHelpMascot = ({ pageId, helpContent, position = 'bottom-right',
const [trackedItemsCount, setTrackedItemsCount] = useState(0);
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [helpState, setHelpState] = useState(() => getSavedHelpState()); // 0: hidden, 1: non-sticky, 2: sticky
+ const [showBugReportForm, setShowBugReportForm] = useState(false);
// Theme-aware mascot image
const mascotImage = useThemeImage('sgex-mascot.png');
@@ -136,6 +138,28 @@ const ContextualHelpMascot = ({ pageId, helpContent, position = 'bottom-right',
};
}, [isAuthenticated]);
+ // Set up global bug report handler
+ useEffect(() => {
+ // Store original handler if it exists
+ const originalHandler = window.helpModalInstance;
+
+ // Create new handler that can trigger bug report form at this level
+ window.helpModalInstance = {
+ ...originalHandler,
+ showBugReportForm: () => {
+ console.log('[ContextualHelpMascot] Showing bug report form from global handler');
+ setShowBugReportForm(true);
+ }
+ };
+
+ return () => {
+ // Restore original handler on unmount
+ if (originalHandler) {
+ window.helpModalInstance = originalHandler;
+ }
+ };
+ }, []);
+
// Auto-enable DAK repository filter when visiting a DAK page
useEffect(() => {
const enableDAKFilter = async () => {
@@ -446,6 +470,20 @@ const ContextualHelpMascot = ({ pageId, helpContent, position = 'bottom-right',
onClose={() => setShowTrackedItems(false)}
/>
)}
+
+ {/* Bug Report Form Modal */}
+ {showBugReportForm && (
+
{
+ if (e.target === e.currentTarget) {
+ setShowBugReportForm(false);
+ }
+ }}>
+ setShowBugReportForm(false)}
+ contextData={contextData}
+ />
+
+ )}
>
);
};
diff --git a/src/components/HelpModal.js b/src/components/HelpModal.js
index 59f63d99c..4bd5692d4 100644
--- a/src/components/HelpModal.js
+++ b/src/components/HelpModal.js
@@ -74,7 +74,7 @@ const HelpModal = ({ topic, helpTopic, contextData, onClose, tutorialId }) => {
// For bug reports, show the new integrated form only if authenticated
if (issueType === 'bug') {
// Check if user is authenticated
- const isAuthenticated = githubService.isAuthenticated;
+ const isAuthenticated = githubService.isAuth();
console.log('[HelpModal] Bug report clicked. Authenticated:', isAuthenticated);
if (isAuthenticated) {
@@ -141,6 +141,25 @@ const HelpModal = ({ topic, helpTopic, contextData, onClose, tutorialId }) => {
if (!repository) {
console.warn('No DAK repository specified, falling back to sgex repository');
+ // Check if user is authenticated and should see the fancy bug report form
+ const isAuthenticated = githubService.isAuth();
+ console.log('[HelpModal] DAK issue clicked without repository. Authenticated:', isAuthenticated);
+
+ if (isAuthenticated) {
+ console.log('[HelpModal] Showing bug report form for DAK issue via global handler');
+ // Call the global handler to show bug report form at ContextualHelpMascot level
+ if (window.helpModalInstance?.showBugReportForm) {
+ window.helpModalInstance.showBugReportForm();
+ } else {
+ // Fallback: try to show it at this level
+ setShowBugReportForm(true);
+ }
+ return;
+ }
+
+ // If not authenticated, fall through to open GitHub issue page directly
+ console.log('[HelpModal] Not authenticated, opening GitHub issue page for DAK issue');
+
// Redirect to sgex repository with appropriate labels
const baseUrl = `${repositoryConfig.getGitHubUrl()}/issues/new`;
let params = {};
diff --git a/src/tests/AuthenticatedBugReportUI.test.js b/src/tests/AuthenticatedBugReportUI.test.js
new file mode 100644
index 000000000..94da3af16
--- /dev/null
+++ b/src/tests/AuthenticatedBugReportUI.test.js
@@ -0,0 +1,246 @@
+import React from 'react';
+import { render, screen, fireEvent, waitFor } from '@testing-library/react';
+import '@testing-library/jest-dom';
+import HelpModal from '../components/HelpModal';
+import BugReportForm from '../components/BugReportForm';
+import githubService from '../services/githubService';
+import bugReportService from '../services/bugReportService';
+
+// Mock the services
+jest.mock('../services/githubService', () => ({
+ isAuth: jest.fn(),
+ isAuthenticated: false, // This property should NOT be used
+}));
+
+jest.mock('../services/bugReportService', () => ({
+ getTemplates: jest.fn(() => Promise.resolve([
+ {
+ id: 'bug',
+ name: 'Bug Report',
+ description: 'Report a bug',
+ type: 'bug',
+ body: []
+ }
+ ])),
+ captureConsoleOutput: jest.fn(() => ({
+ stop: jest.fn(),
+ getLogs: jest.fn(() => '')
+ })),
+ takeScreenshot: jest.fn(),
+ submitIssue: jest.fn(),
+ generateIssueUrl: jest.fn(() => 'https://github.com/litlfred/sgex/issues/new')
+}));
+
+jest.mock('../config/repositoryConfig', () => ({
+ getOwner: () => 'litlfred',
+ getName: () => 'sgex',
+ getGitHubUrl: () => 'https://github.com/litlfred/sgex'
+}));
+
+// Mock window.open
+Object.defineProperty(window, 'open', {
+ writable: true,
+ value: jest.fn()
+});
+
+// Mock matchMedia
+Object.defineProperty(window, 'matchMedia', {
+ writable: true,
+ value: jest.fn().mockImplementation(query => ({
+ matches: query === '(prefers-color-scheme: light)',
+ media: query,
+ onchange: null,
+ addListener: jest.fn(),
+ removeListener: jest.fn(),
+ addEventListener: jest.fn(),
+ removeEventListener: jest.fn(),
+ dispatchEvent: jest.fn(),
+ }))
+});
+
+describe('Authenticated Bug Report UI', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ window.open.mockClear();
+ delete window.helpModalInstance;
+ });
+
+ test('HelpModal shows bug report form when authenticated', () => {
+ // Mock authenticated state
+ githubService.isAuth.mockReturnValue(true);
+
+ const contextData = { pageId: 'test-page' };
+
+ render(
+
+ );
+
+ // Click the bug report button via the global handler
+ window.helpModalInstance.openSgexIssue('bug');
+
+ // Verify isAuth() was called, not the property
+ expect(githubService.isAuth).toHaveBeenCalled();
+ });
+
+ test('HelpModal redirects to GitHub when not authenticated', () => {
+ // Mock unauthenticated state
+ githubService.isAuth.mockReturnValue(false);
+
+ const contextData = { pageId: 'test-page' };
+
+ render(
+
+ );
+
+ // Click the bug report button via the global handler
+ window.helpModalInstance.openSgexIssue('bug');
+
+ // Verify isAuth() was called
+ expect(githubService.isAuth).toHaveBeenCalled();
+
+ // Should open GitHub directly for unauthenticated users
+ expect(window.open).toHaveBeenCalled();
+ });
+
+ test('BugReportForm uses isAuth() method for authentication check', async () => {
+ // Mock authenticated state
+ githubService.isAuth.mockReturnValue(true);
+
+ bugReportService.submitIssue.mockResolvedValue({
+ success: true,
+ issue: { number: 123, html_url: 'https://github.com/test/repo/issues/123' }
+ });
+
+ const contextData = { pageId: 'test-page' };
+
+ render(
+
+ );
+
+ // Wait for templates to load
+ await waitFor(() => {
+ expect(screen.getByText('Report an Issue')).toBeInTheDocument();
+ });
+
+ // Submit button should show "Submit Issue" when authenticated
+ await waitFor(() => {
+ const submitButton = screen.getByRole('button', { name: /submit issue|opening/i });
+ expect(submitButton).toBeInTheDocument();
+ });
+
+ // Verify authentication status is displayed correctly
+ await waitFor(() => {
+ expect(screen.getByText(/Authenticated - Issues will be submitted directly to GitHub/i)).toBeInTheDocument();
+ });
+ });
+
+ test('BugReportForm shows correct UI for unauthenticated users', async () => {
+ // Mock unauthenticated state
+ githubService.isAuth.mockReturnValue(false);
+
+ const contextData = { pageId: 'test-page' };
+
+ render(
+
+ );
+
+ // Wait for templates to load
+ await waitFor(() => {
+ expect(screen.getByText('Report an Issue')).toBeInTheDocument();
+ });
+
+ // Submit button should show "Open in GitHub" when not authenticated
+ await waitFor(() => {
+ const submitButton = screen.getByRole('button', { name: /open in github|opening/i });
+ expect(submitButton).toBeInTheDocument();
+ });
+
+ // Verify authentication status shows not authenticated
+ await waitFor(() => {
+ expect(screen.getByText(/Not authenticated - Issue will open in GitHub for manual submission/i)).toBeInTheDocument();
+ });
+ });
+
+ test('isAuth() method is preferred over isAuthenticated property', () => {
+ // This test verifies that the code uses the method, not the property
+ githubService.isAuth.mockReturnValue(true);
+ githubService.isAuthenticated = false; // Set property to opposite value
+
+ const contextData = { pageId: 'test-page' };
+
+ render(
+
+ );
+
+ // Trigger bug report
+ window.helpModalInstance.openSgexIssue('bug');
+
+ // Should use isAuth() method (which returns true), not property (which is false)
+ expect(githubService.isAuth).toHaveBeenCalled();
+ // The form should show because isAuth() returns true, not because property is false
+ });
+
+ test('openDakIssue shows bug report form when authenticated and no repository', () => {
+ // Mock authenticated state
+ githubService.isAuth.mockReturnValue(true);
+
+ const contextData = { pageId: 'test-page' }; // No repository
+
+ render(
+
+ );
+
+ // Trigger DAK issue without repository
+ window.helpModalInstance.openDakIssue('content');
+
+ // Should check authentication
+ expect(githubService.isAuth).toHaveBeenCalled();
+ // Should not open GitHub
+ expect(window.open).not.toHaveBeenCalled();
+ });
+
+ test('openDakIssue redirects to GitHub when not authenticated and no repository', () => {
+ // Mock unauthenticated state
+ githubService.isAuth.mockReturnValue(false);
+
+ const contextData = { pageId: 'test-page' }; // No repository
+
+ render(
+
+ );
+
+ // Trigger DAK issue without repository
+ window.helpModalInstance.openDakIssue('content');
+
+ // Should check authentication
+ expect(githubService.isAuth).toHaveBeenCalled();
+ // Should open GitHub for unauthenticated users
+ expect(window.open).toHaveBeenCalled();
+ });
+});