@@ -253,13 +253,22 @@ Index: sagemaker-code-editor/vscode/extensions/post-startup-notifications/src/ex
253253===================================================================
254254--- /dev/null
255255+++ sagemaker-code-editor/vscode/extensions/post-startup-notifications/src/extension.ts
256- @@ -0,0 +1,80 @@
256+ @@ -0,0 +1,117 @@
257257+ import * as vscode from 'vscode';
258258+ import * as fs from 'fs';
259259+ import { POST_START_UP_STATUS_FILE, SERVICE_NAME_ENV_KEY, SERVICE_NAME_ENV_VALUE } from './constant';
260260+ import { StatusFile } from './types';
261261+ import * as chokidar from 'chokidar';
262262+
263+ + // Simple method to check if user has seen a notification
264+ + function hasUserSeen(context: vscode.ExtensionContext, notificationId: string): boolean {
265+ + return context.globalState.get(`notification_seen_${notificationId}`) === true;
266+ + }
267+ +
268+ + // Simple method to mark notification as seen
269+ + function markAsSeen(context: vscode.ExtensionContext, notificationId: string): void {
270+ + context.globalState.update(`notification_seen_${notificationId}`, true);
271+ + }
263272+
264273+ let previousStatus: string | undefined;
265274+ let watcher: chokidar.FSWatcher;
@@ -274,6 +283,9 @@ Index: sagemaker-code-editor/vscode/extensions/post-startup-notifications/src/ex
274283+ }
275284+
276285+ outputChannel = vscode.window.createOutputChannel('SageMaker Unified Studio Post Startup Notifications');
286+ +
287+ + // Show Q CLI notification if user hasn't seen it before
288+ + showQCliNotification(context);
277289+
278290+ try {
279291+ watcher = chokidar.watch(POST_START_UP_STATUS_FILE, {
@@ -325,6 +337,31 @@ Index: sagemaker-code-editor/vscode/extensions/post-startup-notifications/src/ex
325337+ }
326338+ };
327339+
340+ + // Show Q CLI notification if user hasn't seen it before
341+ + function showQCliNotification(context: vscode.ExtensionContext): void {
342+ + const notificationId = 'smus_q_cli_notification';
343+ + const message = 'The Amazon Q Command Line Interface (CLI) is installed. You can now access AI-powered assistance in your terminal.';
344+ + const link = 'https://docs.aws.amazon.com/sagemaker-unified-studio/latest/userguide/q-actions.html';
345+ + const linkLabel = 'Learn More';
346+ +
347+ + if (!hasUserSeen(context, notificationId)) {
348+ + outputChannel.appendLine("User has not seen the notification")
349+ + // Show notification with Learn More button
350+ + vscode.window.showInformationMessage(
351+ + message,
352+ + { modal: false },
353+ + { title: linkLabel, isCloseAffordance: false }
354+ + ).then((selection) => {
355+ + if (selection && selection.title === linkLabel) {
356+ + vscode.env.openExternal(vscode.Uri.parse(link));
357+ + }
358+ +
359+ + // Mark as seen regardless of which button was clicked
360+ + markAsSeen(context, notificationId);
361+ + });
362+ + }
363+ + }
364+ +
328365+ export function deactivate() {
329366+ if (watcher) {
330367+ watcher.close();
@@ -334,11 +371,12 @@ Index: sagemaker-code-editor/vscode/extensions/post-startup-notifications/src/ex
334371+ outputChannel.dispose();
335372+ }
336373+ }
374+ \ No newline at end of file
337375Index: sagemaker-code-editor/vscode/extensions/post-startup-notifications/src/test/extension.test.ts
338376===================================================================
339377--- /dev/null
340378+++ sagemaker-code-editor/vscode/extensions/post-startup-notifications/src/test/extension.test.ts
341- @@ -0,0 +1,201 @@
379+ @@ -0,0 +1,267 @@
342380+ import * as vscode from 'vscode';
343381+ import * as fs from 'fs';
344382+ import * as chokidar from 'chokidar';
@@ -356,8 +394,14 @@ Index: sagemaker-code-editor/vscode/extensions/post-startup-notifications/src/te
356394+ jest.mock('vscode', () => ({
357395+ window: {
358396+ showErrorMessage: jest.fn(),
359- + showInformationMessage: jest.fn(),
397+ + showInformationMessage: jest.fn().mockReturnValue(Promise.resolve()) ,
360398+ createOutputChannel: jest.fn()
399+ + },
400+ + env: {
401+ + openExternal: jest.fn()
402+ + },
403+ + Uri: {
404+ + parse: jest.fn(url => ({ toString: () => url }))
361405+ }
362406+ }));
363407+
@@ -373,8 +417,15 @@ Index: sagemaker-code-editor/vscode/extensions/post-startup-notifications/src/te
373417+ // Reset mocks
374418+ jest.resetAllMocks();
375419+
376- + // Setup context
377- + mockContext = { subscriptions: [] } as any;
420+ + // Setup context with globalState for storage
421+ + mockContext = {
422+ + subscriptions: [],
423+ + globalState: {
424+ + get: jest.fn(),
425+ + update: jest.fn(),
426+ + keys: jest.fn().mockReturnValue([])
427+ + }
428+ + } as any;
378429+
379430+ // Setup watcher
380431+ mockWatcher = {
@@ -530,6 +581,59 @@ Index: sagemaker-code-editor/vscode/extensions/post-startup-notifications/src/te
530581+ });
531582+ });
532583+
584+ + describe('Q CLI Notification Tests', () => {
585+ + test('should show Q CLI notification with Learn More button', () => {
586+ + // Set up globalState to simulate first-time user
587+ + (mockContext.globalState.get as jest.Mock).mockReturnValue(undefined);
588+ +
589+ + activate(mockContext);
590+ +
591+ + // Verify notification is shown with correct message and button
592+ + expect(vscode.window.showInformationMessage).toHaveBeenCalledWith(
593+ + 'The Amazon Q Command Line Interface (CLI) is installed. You can now access AI-powered assistance in your terminal.',
594+ + { modal: false },
595+ + { title: 'Learn More', isCloseAffordance: false }
596+ + );
597+ + });
598+ +
599+ + test('should open documentation when Learn More is clicked', async () => {
600+ + // Set up globalState to simulate first-time user
601+ + (mockContext.globalState.get as jest.Mock).mockReturnValue(undefined);
602+ +
603+ + // Mock the user clicking "Learn More"
604+ + const mockSelection = { title: 'Learn More' };
605+ + (vscode.window.showInformationMessage as jest.Mock).mockReturnValue(Promise.resolve(mockSelection));
606+ +
607+ + activate(mockContext);
608+ +
609+ + // Wait for the promise to resolve
610+ + await new Promise(process.nextTick);
611+ +
612+ + // Verify the documentation link is opened
613+ + expect(vscode.env.openExternal).toHaveBeenCalledWith(
614+ + expect.objectContaining({
615+ + toString: expect.any(Function)
616+ + })
617+ + );
618+ +
619+ + // Verify notification is marked as seen
620+ + expect(mockContext.globalState.update).toHaveBeenCalledWith(
621+ + 'notification_seen_smus_q_cli_notification',
622+ + true
623+ + );
624+ + });
625+ +
626+ + test('should not show notification if already seen', () => {
627+ + // Set up globalState to simulate returning user who has seen notification
628+ + (mockContext.globalState.get as jest.Mock).mockReturnValue(true);
629+ +
630+ + activate(mockContext);
631+ +
632+ + // Verify notification is not shown again
633+ + expect(vscode.window.showInformationMessage).not.toHaveBeenCalled();
634+ + });
635+ + });
636+ +
533637+ describe('Deactivation Tests', () => {
534638+ test('should cleanup resources properly', () => {
535639+ activate(mockContext);
@@ -540,6 +644,7 @@ Index: sagemaker-code-editor/vscode/extensions/post-startup-notifications/src/te
540644+ });
541645+ });
542646+ });
647+ \ No newline at end of file
543648Index: sagemaker-code-editor/vscode/extensions/post-startup-notifications/src/types.ts
544649===================================================================
545650--- /dev/null
0 commit comments