diff --git a/packages/atlas-service/src/atlas-service.spec.ts b/packages/atlas-service/src/atlas-service.spec.ts index 209737f1baf..4676114596a 100644 --- a/packages/atlas-service/src/atlas-service.spec.ts +++ b/packages/atlas-service/src/atlas-service.spec.ts @@ -29,9 +29,10 @@ function getAtlasService( const atlasService = new AtlasService( authService, preferences, - createNoopLogger() + createNoopLogger(), + undefined, + ATLAS_CONFIG ); - atlasService['config'] = ATLAS_CONFIG; return atlasService; } diff --git a/packages/atlas-service/src/atlas-service.ts b/packages/atlas-service/src/atlas-service.ts index 4ffe27627d6..ce8d4138548 100644 --- a/packages/atlas-service/src/atlas-service.ts +++ b/packages/atlas-service/src/atlas-service.ts @@ -52,14 +52,17 @@ function getAutomationAgentClusterId( } export class AtlasService { - private config: AtlasServiceConfig; constructor( private readonly authService: AtlasAuthService, private readonly preferences: PreferencesAccess, private readonly logger: Logger, - private readonly options?: AtlasServiceOptions - ) { - this.config = getAtlasConfig(preferences); + private readonly options?: AtlasServiceOptions, + private readonly defaultConfigOverride?: AtlasServiceConfig + ) {} + // Config value is dynamic to make sure that process.env overrides are taken + // into account in runtime + get config(): AtlasServiceConfig { + return this.defaultConfigOverride ?? getAtlasConfig(this.preferences); } adminApiEndpoint(path?: string): string { return `${this.config.atlasApiBaseUrl}${normalizePath(path)}`; diff --git a/packages/atlas-service/src/util.ts b/packages/atlas-service/src/util.ts index 0fa67be649c..080be1f5915 100644 --- a/packages/atlas-service/src/util.ts +++ b/packages/atlas-service/src/util.ts @@ -255,6 +255,7 @@ export function getAtlasConfig( const { atlasServiceBackendPreset } = preferences.getPreferences(); const envConfig = { atlasApiBaseUrl: process.env.COMPASS_ATLAS_SERVICE_UNAUTH_BASE_URL_OVERRIDE, + cloudBaseUrl: process.env.COMPASS_CLOUD_BASE_URL_OVERRIDE, atlasLogin: { clientId: process.env.COMPASS_CLIENT_ID_OVERRIDE, issuer: process.env.COMPASS_OIDC_ISSUER_OVERRIDE, diff --git a/packages/compass-assistant/src/compass-assistant-provider.spec.tsx b/packages/compass-assistant/src/compass-assistant-provider.spec.tsx index 58351688d07..c5368006fb6 100644 --- a/packages/compass-assistant/src/compass-assistant-provider.spec.tsx +++ b/packages/compass-assistant/src/compass-assistant-provider.spec.tsx @@ -28,7 +28,7 @@ import { CompassAssistantDrawer } from './compass-assistant-drawer'; import { createBrokenTransport, createMockChat } from '../test/utils'; import type { AtlasAiService } from '@mongodb-js/compass-generative-ai/provider'; import type { TrackFunction } from '@mongodb-js/compass-telemetry'; -import { createLogger } from '@mongodb-js/compass-logging'; +import { createNoopLogger } from '@mongodb-js/compass-logging/provider'; function createMockProvider({ mockAtlasService, @@ -480,7 +480,7 @@ describe('CompassAssistantProvider', function () { .stub() .returns('https://localhost:3000'), } as unknown as AtlasService, - logger: createLogger('COMPASS-ASSISTANT-TEST'), + logger: createNoopLogger(), track: track as unknown as TrackFunction, }); await renderOpenAssistantDrawer({ @@ -654,47 +654,42 @@ describe('CompassAssistantProvider', function () { }); }); + let sandbox: sinon.SinonSandbox; + + beforeEach(function () { + sandbox = sinon.createSandbox(); + }); + + afterEach(function () { + if (sandbox) { + sandbox.reset(); + } + }); + it('uses the Atlas Service assistantApiEndpoint', async function () { const mockAtlasService = { - assistantApiEndpoint: sinon + assistantApiEndpoint: sandbox .stub() .returns('https://example.com/assistant/api/v1'), }; - const mockAtlasAiService = { - ensureAiFeatureAccess: sinon.stub().callsFake(() => { - return Promise.resolve(); - }), - }; - - const mockAtlasAuthService = {}; - - const MockedProvider = CompassAssistantProvider.withMockServices({ + const chat = createDefaultChat({ + originForPrompt: 'foo', + appNameForPrompt: 'bar', atlasService: mockAtlasService as unknown as AtlasService, - atlasAiService: mockAtlasAiService as unknown as AtlasAiService, - atlasAuthService: mockAtlasAuthService as unknown as AtlasAuthService, + logger: createNoopLogger(), + track: () => undefined, }); - render( - - - - , - { - preferences: { - enableAIAssistant: true, - enableGenAIFeatures: true, - enableGenAIFeaturesAtlasOrg: true, - cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true }, - }, - } - ); + const fetchStub = sandbox + .stub(globalThis, 'fetch') + .resolves({ ok: true, headers: [] } as any); - await waitFor(() => { - expect(mockAtlasService.assistantApiEndpoint.calledOnce).to.be.true; - }); + await chat.sendMessage({ text: 'hello' }); + + expect(mockAtlasService.assistantApiEndpoint.calledOnce).to.be.true; + expect(fetchStub.lastCall.args[0]).to.eq( + 'https://example.com/assistant/api/v1/responses' + ); }); }); diff --git a/packages/compass-assistant/src/compass-assistant-provider.tsx b/packages/compass-assistant/src/compass-assistant-provider.tsx index e9ced74b509..db7633f65c6 100644 --- a/packages/compass-assistant/src/compass-assistant-provider.tsx +++ b/packages/compass-assistant/src/compass-assistant-provider.tsx @@ -334,6 +334,7 @@ export function createDefaultChat({ transport: Chat['transport']; }; }): Chat { + const initialBaseUrl = 'http://PLACEHOLDER_BASE_URL_TO_BE_REPLACED.invalid'; return new Chat({ transport: options?.transport ?? @@ -343,8 +344,21 @@ export function createDefaultChat({ target: appNameForPrompt, }), model: createOpenAI({ - baseURL: atlasService.assistantApiEndpoint(), + baseURL: initialBaseUrl, apiKey: '', + fetch(url, init) { + return globalThis.fetch( + // The `baseUrl` can be dynamically changed, but `createOpenAI` + // constructor doesn't allow us to change it after initial call. + // Instead we're going to update it every time the fetch call + // happens + String(url).replace( + initialBaseUrl, + atlasService.assistantApiEndpoint() + ), + init + ); + }, }).responses('mongodb-chat-latest'), }), onError: (err: Error) => { diff --git a/packages/compass-components/src/components/compass-components-provider.tsx b/packages/compass-components/src/components/compass-components-provider.tsx index 8da0abfaf86..754edc45a8c 100644 --- a/packages/compass-components/src/components/compass-components-provider.tsx +++ b/packages/compass-components/src/components/compass-components-provider.tsx @@ -44,6 +44,11 @@ type CompassComponentsProviderProps = { * Set to disable context menus in the application. */ disableContextMenus?: boolean; + + /** + * Set to disable all guide cues in the application. + */ + disableGuideCues?: boolean; } & { onNextGuideGue?: GuideCueProviderProps['onNext']; onNextGuideCueGroup?: GuideCueProviderProps['onNextGroup']; @@ -128,7 +133,8 @@ export const CompassComponentsProvider = ({ utmMedium, stackedElementsZIndex, popoverPortalContainer: _popoverPortalContainer, - disableContextMenus, + disableContextMenus = false, + disableGuideCues = false, ...signalHooksProviderProps }: CompassComponentsProviderProps) => { const darkMode = useDarkMode(_darkMode); @@ -164,6 +170,7 @@ export const CompassComponentsProvider = ({ utmMedium={utmMedium} > diff --git a/packages/compass-components/src/components/guide-cue/guide-cue-service.spec.ts b/packages/compass-components/src/components/guide-cue/guide-cue-service.spec.ts index 625ef8450d7..0c6604afa08 100644 --- a/packages/compass-components/src/components/guide-cue/guide-cue-service.spec.ts +++ b/packages/compass-components/src/components/guide-cue/guide-cue-service.spec.ts @@ -604,10 +604,8 @@ describe('GuideCueService', function () { }); context('when disabled', function () { - const initialValue = process.env.DISABLE_GUIDE_CUES; before(function () { - process.env.DISABLE_GUIDE_CUES = 'true'; - + guideCueService.enabled = false; guideCueService.addCue({ cueId: '1', isIntersecting: true, step: 1 }); guideCueService.addCue({ cueId: '3', @@ -624,7 +622,7 @@ describe('GuideCueService', function () { }); after(function () { - process.env.DISABLE_GUIDE_CUES = initialValue; + guideCueService.enabled = true; }); it('does not add a cue', function () { diff --git a/packages/compass-components/src/components/guide-cue/guide-cue-service.ts b/packages/compass-components/src/components/guide-cue/guide-cue-service.ts index 9443a6108e3..df06d14359d 100644 --- a/packages/compass-components/src/components/guide-cue/guide-cue-service.ts +++ b/packages/compass-components/src/components/guide-cue/guide-cue-service.ts @@ -38,12 +38,14 @@ export class GuideCueService extends EventTarget { private _activeGroupId: GroupName | null = null; private _activeCue: Cue | null = null; + enabled = true; + constructor(private readonly _storage: GuideCueStorage) { super(); } addCue(cue: Omit) { - if (process.env.DISABLE_GUIDE_CUES === 'true') { + if (!this.enabled) { return; } const cueIndex = this.getCueIndex(cue.cueId, cue.groupId); diff --git a/packages/compass-components/src/components/guide-cue/guide-cue.tsx b/packages/compass-components/src/components/guide-cue/guide-cue.tsx index d036c6a9b50..a9f840f0387 100644 --- a/packages/compass-components/src/components/guide-cue/guide-cue.tsx +++ b/packages/compass-components/src/components/guide-cue/guide-cue.tsx @@ -33,10 +33,16 @@ export type GroupCue = Cue & { type GuideCueContextValue = { onNext?: (cue: Cue) => void; onNextGroup?: (groupCue: GroupCue) => void; + enabled?: boolean; }; -const GuideCueContext = React.createContext({}); + +const GuideCueContext = React.createContext({ + enabled: true, +}); + export const GuideCueProvider: React.FC = ({ children, + enabled = true, ...callbacks }) => { const callbacksRef = useRef(callbacks); @@ -49,9 +55,13 @@ export const GuideCueProvider: React.FC = ({ onNextGroup(groupCue: GroupCue) { callbacksRef.current.onNextGroup?.(groupCue); }, + enabled, }), - [] + [enabled] ); + useEffect(() => { + guideCueService.enabled = enabled; + }, [enabled]); return ( {children} @@ -253,7 +263,7 @@ export const GuideCue = ({ return ( <> - {readyToRender && ( + {context.enabled && readyToRender && ( ( browser: CompassBrowser, name: K ): Promise { - return await browser.execute(async (_name) => { - return ( - // eslint-disable-next-line @typescript-eslint/no-require-imports - (await require('electron').ipcRenderer.invoke('compass:get-preferences'))[ - _name - ] + return (await getFeatures(browser))[name]; +} + +export async function getFeatures( + browser: CompassBrowser +): Promise { + if (isTestingWeb()) { + // When running in Compass web we cannot use the IPC to read the + // preferences so we use a global function + await browser.waitUntil(async () => { + return await browser.execute(() => { + return ( + Symbol.for('@compass-web-sandbox-preferences-access') in globalThis + ); + }); + }); + return await browser.execute(() => { + return (globalThis as any)[ + Symbol.for('@compass-web-sandbox-preferences-access') + ].getPreferences(); + }); + } + return await browser.execute(async () => { + // eslint-disable-next-line @typescript-eslint/no-require-imports + return await require('electron').ipcRenderer.invoke( + 'compass:get-preferences' ); - }, name); + }); } diff --git a/packages/compass-e2e-tests/helpers/commands/set-env.ts b/packages/compass-e2e-tests/helpers/commands/set-env.ts index 40ca31ae95b..58b2df75b21 100644 --- a/packages/compass-e2e-tests/helpers/commands/set-env.ts +++ b/packages/compass-e2e-tests/helpers/commands/set-env.ts @@ -2,8 +2,7 @@ import type { CompassBrowser } from '../compass-browser'; import { isTestingWeb } from '../test-runner-context'; /** - * Sets an environment variable override in Compass Web. - * This is only supported in Compass Web tests, not in Compass Desktop. + * Sets an environment variable override in Compass. This works the same way both for Compass desktop and web runtimes * * @example * // Set the Atlas service URL override in a test @@ -20,24 +19,35 @@ export async function setEnv( browser: CompassBrowser, key: string, value: string -): Promise { +): Promise> { + // In web, use injected function to set the env if (isTestingWeb()) { - // When running in Compass web we use a global function to set env vars - await browser.execute( + await browser.waitUntil(async () => { + return await browser.execute(() => { + return Symbol.for('@compass-web-sandbox-set-env') in globalThis; + }); + }); + return await browser.execute( (_key, _value) => { const kSandboxSetEnvFn = Symbol.for('@compass-web-sandbox-set-env'); // eslint-disable-next-line @typescript-eslint/no-explicit-any - (globalThis as any)[kSandboxSetEnvFn]?.(_key, _value); + return (globalThis as any)[kSandboxSetEnvFn](_key, _value) as Record< + string, + string + >; + }, + key, + value + ); + } else { + // In electron, just set the existing global var + return await browser.execute( + (_key, _value) => { + process.env[_key] = _value; + return process.env as Record; }, key, value ); - return; } - - // When running in Compass desktop, we can't dynamically change env vars - // after the process has started, so we throw an error - throw new Error( - 'setEnv is only supported in Compass web. For Compass desktop, set environment variables before starting the app.' - ); } diff --git a/packages/compass-e2e-tests/helpers/commands/set-feature.ts b/packages/compass-e2e-tests/helpers/commands/set-feature.ts index 7d37bede91e..b8b68951bb4 100644 --- a/packages/compass-e2e-tests/helpers/commands/set-feature.ts +++ b/packages/compass-e2e-tests/helpers/commands/set-feature.ts @@ -12,17 +12,24 @@ export async function setFeature( ): Promise { if (isTestingWeb()) { // When running in Compass web we cannot use the IPC to update the - // preferences so we use a global function. + // preferences so we use a global function + await browser.waitUntil(async () => { + return await browser.execute(() => { + return ( + Symbol.for('@compass-web-sandbox-preferences-access') in globalThis + ); + }); + }); await browser.execute( async (_name, _value) => { const kSandboxUpdateFn = Symbol.for( - '@compass-web-sandbox-update-preferences' + '@compass-web-sandbox-preferences-access' ); const attributes: Partial = { [_name]: _value === null ? undefined : _value, }; // eslint-disable-next-line @typescript-eslint/no-explicit-any - await (globalThis as any)[kSandboxUpdateFn]?.(attributes); + await (globalThis as any)[kSandboxUpdateFn].savePreferences(attributes); }, name, value diff --git a/packages/compass-e2e-tests/helpers/compass-web-sandbox.ts b/packages/compass-e2e-tests/helpers/compass-web-sandbox.ts index be0b83805dd..caccb9f9608 100644 --- a/packages/compass-e2e-tests/helpers/compass-web-sandbox.ts +++ b/packages/compass-e2e-tests/helpers/compass-web-sandbox.ts @@ -11,7 +11,6 @@ import { } from './test-runner-paths'; import type { ConnectionInfo } from '@mongodb-js/connection-info'; import ConnectionString from 'mongodb-connection-string-url'; -import { MOCK_ASSISTANT_SERVER_PORT } from './assistant-service'; const debug = Debug('compass-e2e-tests:compass-web-sandbox'); @@ -22,9 +21,6 @@ const debug = Debug('compass-e2e-tests:compass-web-sandbox'); process.env.OPEN_BROWSER = 'false'; // tell webpack dev server not to open the default browser process.env.DISABLE_DEVSERVER_OVERLAY = 'true'; process.env.APP_ENV = 'webdriverio'; -// Set the assistant base URL override for tests so we can mock the assistant server -process.env.COMPASS_ASSISTANT_BASE_URL_OVERRIDE = `http://localhost:${MOCK_ASSISTANT_SERVER_PORT}`; -process.env.COMPASS_OVERRIDE_ENABLE_AI_FEATURES = 'true'; const wait = (ms: number) => { return new Promise((resolve) => { diff --git a/packages/compass-e2e-tests/helpers/compass.ts b/packages/compass-e2e-tests/helpers/compass.ts index 103598c880b..c6a4f4dce24 100644 --- a/packages/compass-e2e-tests/helpers/compass.ts +++ b/packages/compass-e2e-tests/helpers/compass.ts @@ -597,6 +597,34 @@ async function processCommonOpts({ }; } +async function setCommonRendererEnv(browser: CompassBrowser) { + // TODO(COMPASS-9977) Turn off virtual scrolling in e2e tests until we can fix + // browser.scrollToVirtualItem() to work with it + await browser.setEnv('COMPASS_DISABLE_VIRTUAL_TABLE_RENDERING', 'true'); +} + +async function setCommonFeatures(browser: CompassBrowser) { + // Guide cues might affect too many tests in a way where the auto showing of the cue prevents + // clicks from working on elements. Dealing with this case-by-case is way too much work, so + // we disable the cues completely for the e2e tests + await browser.setFeature('enableGuideCues', false); + + // Making sure end-of-life connection modal is not shown, simplify any test connecting to such a server + await browser.setFeature('showEndOfLifeConnectionModal', false); + + // It's helpful to have devtools pre-enabled when running tests + await browser.setFeature('enableDevTools', true); + + const { enableGuideCues, showEndOfLifeConnectionModal, enableDevTools } = + await browser.getFeatures(); + + debug('Updated common feature flags to new value', { + enableGuideCues, + showEndOfLifeConnectionModal, + enableDevTools, + }); +} + async function startCompassElectron( name: string, opts: StartCompassOptions = {} @@ -652,18 +680,6 @@ async function startCompassElectron( process.env.HADRON_PRODUCT_NAME_OVERRIDE = 'MongoDB Compass WebdriverIO'; } - // Guide cues might affect too many tests in a way where the auto showing of the cue prevents - // clicks from working on elements. Dealing with this case-by-case is way too much work, so - // we disable the cues completely for the e2e tests - process.env.DISABLE_GUIDE_CUES = 'true'; - - // Making sure end-of-life connection modal is not shown, simplify any test connecting to such a server - process.env.COMPASS_DISABLE_END_OF_LIFE_CONNECTION_MODAL = 'true'; - - // TODO(COMPASS-9977) Turn off virtual scrolling in e2e tests until we can fix - // browser.scrollToVirtualItem() to work with it - process.env.COMPASS_DISABLE_VIRTUAL_TABLE_RENDERING = 'true'; - const options = { automationProtocol: 'webdriver' as const, capabilities: { @@ -772,7 +788,7 @@ export type StoredAtlasCloudCookies = { expirationDate: number; }[]; -export async function startBrowser( +async function startBrowser( name: string, // eslint-disable-next-line @typescript-eslint/no-unused-vars opts: StartCompassOptions = {} @@ -1125,6 +1141,9 @@ export async function init( }); } + await setCommonRendererEnv(browser); + await setCommonFeatures(browser); + if (compass.needsCloseWelcomeModal) { await browser.closeWelcomeModal(); } diff --git a/packages/compass-e2e-tests/helpers/atlas-service.ts b/packages/compass-e2e-tests/helpers/mock-atlas-service.ts similarity index 56% rename from packages/compass-e2e-tests/helpers/atlas-service.ts rename to packages/compass-e2e-tests/helpers/mock-atlas-service.ts index cad0687736c..47d7068fefb 100644 --- a/packages/compass-e2e-tests/helpers/atlas-service.ts +++ b/packages/compass-e2e-tests/helpers/mock-atlas-service.ts @@ -61,33 +61,62 @@ export async function startMockAtlasServiceServer( let response = _response; const server = http .createServer((req, res) => { - if (req.method === 'GET') { - requests.push({ - req, - content: null, - }); - return aiFeatureEnableResponse(req, res); + res.setHeader('Access-Control-Allow-Origin', req.headers.origin ?? '*'); + res.setHeader('Access-Control-Allow-Credentials', 'true'); + res.setHeader( + 'Access-Control-Allow-Headers', + req.headers['access-control-request-headers'] ?? '*' + ); + + if (req.method === 'OPTIONS') { + res.statusCode = 200; + res.end(); + return; + } + + if (req.url?.startsWith('/settings/optInDataExplorerGenAIFeatures')) { + res.statusCode = 200; + res.end(); + return; } - let body = ''; - req - .setEncoding('utf8') - .on('data', (chunk) => { - body += chunk; - }) - .on('end', () => { - const jsonObject = JSON.parse(body); + if (req.url?.startsWith('/unauth/ai/api/v1/mql-query')) { + if (req.method === 'GET') { requests.push({ req, - content: jsonObject, + content: null, }); + return aiFeatureEnableResponse(req, res); + } + + let body = ''; + + req + .setEncoding('utf8') + .on('data', (chunk) => { + body += chunk; + }) + .on('end', () => { + const jsonObject = JSON.parse(body); + requests.push({ + req, + content: jsonObject, + }); + + res.setHeader('Content-Type', 'application/json'); + if (response.status !== 200) { + res.writeHead(response.status); + } + return res.end(JSON.stringify(response.body)); + }); + + return; + } - res.setHeader('Content-Type', 'application/json'); - if (response.status !== 200) { - res.writeHead(response.status); - } - return res.end(JSON.stringify(response.body)); - }); + res.statusCode = 404; + res.statusMessage = + 'Route not found in the mock Atlas backend. Did you forget to add a mock route support?'; + res.end(); }) .listen(0); await once(server, 'listening'); diff --git a/packages/compass-e2e-tests/helpers/test-runner-global-fixtures.ts b/packages/compass-e2e-tests/helpers/test-runner-global-fixtures.ts index 50d6fdd520a..8a6def6ee32 100644 --- a/packages/compass-e2e-tests/helpers/test-runner-global-fixtures.ts +++ b/packages/compass-e2e-tests/helpers/test-runner-global-fixtures.ts @@ -93,10 +93,6 @@ export async function mochaGlobalSetup(this: Mocha.Runner) { if (isTestingWeb(context) && !isTestingAtlasCloudExternal(context)) { debug('Starting Compass Web server ...'); - // TODO(COMPASS-9977) Turn off virtual scrolling in e2e tests until we can fix - // browser.scrollToVirtualItem() to work with it - process.env.COMPASS_DISABLE_VIRTUAL_TABLE_RENDERING = 'true'; - if (isTestingAtlasCloudSandbox(context)) { const compassWeb = await spawnCompassWebSandboxAndSignInToAtlas( { diff --git a/packages/compass-e2e-tests/tests/assistant.test.ts b/packages/compass-e2e-tests/tests/assistant.test.ts index d1dce32ddd7..a6366b13829 100644 --- a/packages/compass-e2e-tests/tests/assistant.test.ts +++ b/packages/compass-e2e-tests/tests/assistant.test.ts @@ -9,14 +9,12 @@ import { cleanup, screenshotIfFailed, DEFAULT_CONNECTION_NAME_1, - skipForWeb, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; import * as Selectors from '../helpers/selectors'; -import { startMockAtlasServiceServer } from '../helpers/atlas-service'; +import { startMockAtlasServiceServer } from '../helpers/mock-atlas-service'; import { startMockAssistantServer } from '../helpers/assistant-service'; import type { MockAssistantResponse } from '../helpers/assistant-service'; -import { isTestingWeb } from '../helpers/test-runner-context'; import { context } from '../helpers/test-runner-context'; @@ -51,12 +49,22 @@ describe('MongoDB Assistant', function () { mockAtlasServer = await startMockAtlasServiceServer(); mockAssistantServer = await startMockAssistantServer(); - process.env.COMPASS_ATLAS_SERVICE_UNAUTH_BASE_URL_OVERRIDE = - mockAtlasServer.endpoint; - telemetry = await startTelemetryServer(); compass = await init(this.test?.fullTitle()); + await compass.browser.setEnv( + 'COMPASS_ATLAS_SERVICE_UNAUTH_BASE_URL_OVERRIDE', + mockAtlasServer.endpoint + ); + await compass.browser.setEnv( + 'COMPASS_CLOUD_BASE_URL_OVERRIDE', + mockAtlasServer.endpoint + ); + await compass.browser.setEnv( + 'COMPASS_ASSISTANT_BASE_URL_OVERRIDE', + mockAssistantServer.endpoint + ); + sendMessage = async ( text: string, { @@ -124,13 +132,8 @@ describe('MongoDB Assistant', function () { }; setAIFeatures = async (newValue: boolean) => { - if (isTestingWeb()) { - await browser.setEnv( - 'COMPASS_OVERRIDE_ENABLE_AI_FEATURES', - newValue ? 'true' : 'false' - ); - } await browser.setFeature('enableGenAIFeatures', newValue); + await browser.setFeature('enableGenAISampleDocumentPassing', newValue); if (newValue) { await browser.$(Selectors.AssistantDrawerButton).waitForDisplayed(); @@ -142,26 +145,6 @@ describe('MongoDB Assistant', function () { }; setAIOptIn = async (newValue: boolean) => { - if ( - isTestingWeb() || - ((await browser.getFeature('optInGenAIFeatures')) === true && - newValue === false) - ) { - await cleanup(compass); - // Reseting the opt-in to false can be tricky so it's best to start over in this case. - compass = await init(this.test?.fullTitle(), { firstRun: true }); - await setup(); - - if (isTestingWeb()) { - await setAIFeatures(true); - } - await browser.setFeature( - 'optInGenAIFeatures', - newValue ? 'true' : 'false' - ); - return; - } - await browser.setFeature('optInGenAIFeatures', newValue); }; @@ -171,9 +154,6 @@ describe('MongoDB Assistant', function () { after(async function () { await mockAtlasServer.stop(); await mockAssistantServer.stop(); - - delete process.env.COMPASS_ATLAS_SERVICE_UNAUTH_BASE_URL_OVERRIDE; - await cleanup(compass); await telemetry.stop(); }); @@ -195,12 +175,7 @@ describe('MongoDB Assistant', function () { }); it('does not show the assistant drawer button when AI features are disabled', async function () { - // we cannot opt back out on web because it is stored server-side - skipForWeb( - this, - 'E2E testing for assistant drawer visibility on compass-web is not yet implemented' - ); - + await setAIOptIn(false); await setAIFeatures(false); const drawerButton = browser.$(Selectors.AssistantDrawerButton); @@ -225,12 +200,7 @@ describe('MongoDB Assistant', function () { }); describe('before opt-in', function () { - // we cannot opt back out on web because it is stored server-side before(async function () { - skipForWeb( - this, - 'E2E testing for opt-in on compass-web is not yet implemented' - ); await setAIOptIn(false); }); @@ -291,11 +261,6 @@ describe('MongoDB Assistant', function () { describe('opting in', function () { before(async function () { - // we cannot opt back out on web because it is stored server-side - skipForWeb( - this, - 'E2E testing for opt-in on compass-web is not yet implemented' - ); await setAIOptIn(false); await openAssistantDrawer(browser); }); diff --git a/packages/compass-e2e-tests/tests/collection-ai-query.test.ts b/packages/compass-e2e-tests/tests/collection-ai-query.test.ts index d1454932598..2614fd4ad9d 100644 --- a/packages/compass-e2e-tests/tests/collection-ai-query.test.ts +++ b/packages/compass-e2e-tests/tests/collection-ai-query.test.ts @@ -7,15 +7,13 @@ import { init, cleanup, screenshotIfFailed, - skipForWeb, - TEST_COMPASS_WEB, DEFAULT_CONNECTION_NAME_1, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; import * as Selectors from '../helpers/selectors'; import { createNumbersCollection } from '../helpers/insert-data'; -import { startMockAtlasServiceServer } from '../helpers/atlas-service'; -import type { MockAtlasServerResponse } from '../helpers/atlas-service'; +import { startMockAtlasServiceServer } from '../helpers/mock-atlas-service'; +import type { MockAtlasServerResponse } from '../helpers/mock-atlas-service'; describe('Collection ai query', function () { let compass: Compass; @@ -27,10 +25,6 @@ describe('Collection ai query', function () { let clearRequests: () => void; before(async function () { - skipForWeb(this, 'ai queries not yet available in compass-web'); - - process.env.COMPASS_E2E_SKIP_AI_OPT_IN = 'true'; - // Start a mock server to pass an ai response. const { endpoint, @@ -45,11 +39,18 @@ describe('Collection ai query', function () { clearRequests = _clearRequests; setMockAtlasServerResponse = _setMockAtlasServerResponse; - process.env.COMPASS_ATLAS_SERVICE_UNAUTH_BASE_URL_OVERRIDE = endpoint; - telemetry = await startTelemetryServer(); compass = await init(this.test?.fullTitle()); browser = compass.browser; + + await browser.setEnv( + 'COMPASS_ATLAS_SERVICE_UNAUTH_BASE_URL_OVERRIDE', + endpoint + ); + + await browser.setFeature('enableGenAISampleDocumentPassing', true); + await browser.setFeature('optInGenAIFeatures', true); + await browser.setupDefaultConnections(); }); @@ -66,14 +67,8 @@ describe('Collection ai query', function () { }); after(async function () { - if (TEST_COMPASS_WEB) { - return; - } - await stopMockAtlasServer(); - delete process.env.COMPASS_E2E_SKIP_AI_OPT_IN; - await cleanup(compass); await telemetry.stop(); }); diff --git a/packages/compass-generative-ai/src/atlas-ai-service.spec.ts b/packages/compass-generative-ai/src/atlas-ai-service.spec.ts index b2748eda075..1767fef6dd5 100644 --- a/packages/compass-generative-ai/src/atlas-ai-service.spec.ts +++ b/packages/compass-generative-ai/src/atlas-ai-service.spec.ts @@ -21,28 +21,30 @@ const ATLAS_USER = { const BASE_URL = 'http://example.com'; -const mockConnectionInfo: ConnectionInfo = { - id: 'TEST', - connectionOptions: { - connectionString: 'mongodb://localhost:27020', - }, - atlasMetadata: { - orgId: 'testOrg', - projectId: 'testProject', - clusterName: 'pineapple', - regionalBaseUrl: null, - metricsId: 'metricsId', - metricsType: 'replicaSet', - instanceSize: 'M10', - clusterType: 'REPLICASET', - clusterUniqueId: 'clusterUniqueId', - clusterState: 'IDLE', - supports: { - globalWrites: false, - rollingIndexes: false, +const getMockConnectionInfo = (): ConnectionInfo => { + return { + id: 'TEST', + connectionOptions: { + connectionString: 'mongodb://localhost:27020', + }, + atlasMetadata: { + orgId: 'testOrg', + projectId: 'testProject', + clusterName: 'pineapple', + regionalBaseUrl: null, + metricsId: 'metricsId', + metricsType: 'replicaSet', + instanceSize: 'M10', + clusterType: 'REPLICASET', + clusterUniqueId: 'clusterUniqueId', + clusterState: 'IDLE', + supports: { + globalWrites: false, + rollingIndexes: false, + }, + userConnectionString: 'mongodb+srv://localhost:27020', }, - userConnectionString: 'mongodb+srv://localhost:27020', - }, + }; }; class MockAtlasService { @@ -104,9 +106,19 @@ describe('AtlasAiService', function () { ] as const; for (const { apiURLPreset, expectedEndpoints } of endpointBasepathTests) { - describe(`api URL Preset "${apiURLPreset}"`, function () { + const describeName = + apiURLPreset === 'admin-api' + ? 'connection WITHOUT atlas metadata' + : 'connection WITH atlas metadata'; + describe(describeName, function () { let atlasAiService: AtlasAiService; + const mockConnectionInfo = getMockConnectionInfo(); + + if (apiURLPreset === 'admin-api') { + delete mockConnectionInfo.atlasMetadata; + } + beforeEach(function () { const mockAtlasService = new MockAtlasService(); atlasAiService = new AtlasAiService({ @@ -574,26 +586,6 @@ describe('AtlasAiService', function () { expect(requestBody).to.have.property('schema'); }); - it('throws AtlasAiServiceInvalidInputError when connection info lacks atlas metadata', async function () { - const connectionInfoWithoutAtlas = { - ...mockConnectionInfo, - atlasMetadata: undefined, - }; - - try { - await atlasAiService.getMockDataSchema( - mockSchemaInput, - connectionInfoWithoutAtlas as any - ); - expect.fail('Expected getMockDataSchema to throw'); - } catch (err) { - expect(err).to.be.instanceOf(AtlasAiServiceInvalidInputError); - expect((err as Error).message).to.match( - /atlasMetadata is not available/i - ); - } - }); - it('throws AtlasAiServiceApiResponseParseError when API response has invalid format', async function () { const invalidMockResponse = { invalidField: 'invalid data', diff --git a/packages/compass-generative-ai/src/atlas-ai-service.ts b/packages/compass-generative-ai/src/atlas-ai-service.ts index c2df97354bd..22d930e436d 100644 --- a/packages/compass-generative-ai/src/atlas-ai-service.ts +++ b/packages/compass-generative-ai/src/atlas-ai-service.ts @@ -208,10 +208,11 @@ const aiURLConfig = { query: 'unauth/ai/api/v1/mql-query', }, cloud: { - aggregation: (groupId: string) => `ai/v1/groups/${groupId}/mql-aggregation`, - query: (groupId: string) => `ai/v1/groups/${groupId}/mql-query`, - 'mock-data-schema': (groupId: string) => - `ai/v1/groups/${groupId}/mock-data-schema`, + aggregation: (projectId: string) => + `ai/v1/groups/${projectId}/mql-aggregation`, + query: (projectId: string) => `ai/v1/groups/${projectId}/mql-query`, + 'mock-data-schema': (projectId: string) => + `ai/v1/groups/${projectId}/mock-data-schema`, }, } as const; @@ -290,18 +291,13 @@ export class AtlasAiService { */ private getUrlForEndpoint( resourceType: AIResourceType, - connectionInfo?: ConnectionInfo + connectionInfo: ConnectionInfo ) { - if (this.apiURLPreset === 'cloud') { - const atlasMetadata = connectionInfo?.atlasMetadata; - if (!atlasMetadata) { - throw new AtlasAiServiceInvalidInputError( - "Can't perform generative ai request: atlasMetadata is not available" - ); - } + const atlasMetadata = connectionInfo.atlasMetadata; + if (atlasMetadata) { return this.atlasService.cloudEndpoint( - aiURLConfig[this.apiURLPreset][resourceType](atlasMetadata.projectId) + aiURLConfig.cloud[resourceType](atlasMetadata.projectId) ); } @@ -311,7 +307,7 @@ export class AtlasAiService { ); } - const urlPath = aiURLConfig[this.apiURLPreset][resourceType]; + const urlPath = aiURLConfig['admin-api'][resourceType]; return this.atlasService.adminApiEndpoint(urlPath); } @@ -353,8 +349,7 @@ export class AtlasAiService { }: { urlId: 'query' | 'aggregation'; input: GenerativeAiInput; - - connectionInfo?: ConnectionInfo; + connectionInfo: ConnectionInfo; }, validationFn: (res: any) => asserts res is T ): Promise => { diff --git a/packages/compass-preferences-model/src/preferences-schema.tsx b/packages/compass-preferences-model/src/preferences-schema.tsx index eeefc5b4f0b..08eb3531efe 100644 --- a/packages/compass-preferences-model/src/preferences-schema.tsx +++ b/packages/compass-preferences-model/src/preferences-schema.tsx @@ -134,6 +134,7 @@ export type InternalUserPreferences = { isMaximized?: boolean; isFullScreen?: boolean; }; + enableGuideCues: boolean; }; // UserPreferences contains all preferences stored to disk. @@ -450,11 +451,7 @@ export const storedUserPreferencesProps: Required<{ cli: false, global: false, description: null, - validator: z - .boolean() - .default( - process.env.COMPASS_DISABLE_END_OF_LIFE_CONNECTION_MODAL !== 'true' - ), + validator: z.boolean().default(true), type: 'boolean', }, /** @@ -721,7 +718,7 @@ export const storedUserPreferencesProps: Required<{ long: `Enable the Chromium Developer Tools that can be used to debug Electron's process.`, }, deriveValue: deriveFeatureRestrictingOptionsState('enableDevTools'), - validator: z.boolean().default(process.env.APP_ENV === 'webdriverio'), + validator: z.boolean().default(false), type: 'boolean', }, /** @@ -1093,6 +1090,16 @@ export const storedUserPreferencesProps: Required<{ type: 'number', }, + enableGuideCues: { + ui: false, + cli: false, + global: false, + omitFromHelp: true, + description: null, + validator: z.boolean().default(true), + type: 'boolean', + }, + ...allFeatureFlagsProps, }; diff --git a/packages/compass-web/polyfills/process/index.ts b/packages/compass-web/polyfills/process/index.ts index 009fd9e5af2..0a446bd6b7f 100644 --- a/packages/compass-web/polyfills/process/index.ts +++ b/packages/compass-web/polyfills/process/index.ts @@ -6,17 +6,4 @@ import hrtime from 'browser-process-hrtime'; (process as any).platform = 'Unknown'; (process as any).arch = 'Unknown'; -// Allow e2e tests to override environment variables -if (process.env.APP_ENV === 'webdriverio') { - const kSandboxSetEnvFn = Symbol.for('@compass-web-sandbox-set-env'); - // eslint-disable-next-line no-console - console.info( - `[compass-web sandbox] call window[Symbol.for('@compass-web-sandbox-set-env')]('KEY', 'value') to dynamically set environment variables` - ); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (globalThis as any)[kSandboxSetEnvFn] = (key: string, value: string) => { - process.env[key] = value; - }; -} - export { process }; diff --git a/packages/compass-web/sandbox/index.tsx b/packages/compass-web/sandbox/index.tsx index cb9c6e4b0e7..01722fd0c0c 100644 --- a/packages/compass-web/sandbox/index.tsx +++ b/packages/compass-web/sandbox/index.tsx @@ -8,12 +8,13 @@ import { } from '@mongodb-js/compass-components'; import { CompassWeb } from '../src/index'; import { SandboxConnectionStorageProvider } from '../src/connection-storage'; +import './sandbox-process'; import { sandboxLogger } from './sandbox-logger'; import { sandboxTelemetry } from './sandbox-telemetry'; import { useAtlasProxySignIn } from './sandbox-atlas-sign-in'; import { sandboxConnectionStorage } from './sandbox-connection-storage'; import { useWorkspaceTabRouter } from './sandbox-workspace-tab-router'; -import { SandboxPreferencesUpdateProvider } from '../src/preferences'; +import { SandboxPreferencesGlobalAccessProvider } from '../src/preferences'; const sandboxContainerStyles = css({ width: '100%', @@ -90,14 +91,11 @@ const App = () => { return { readOnly: true }; })(); - const overrideGenAIFeatures = - process.env.COMPASS_OVERRIDE_ENABLE_AI_FEATURES === 'true'; - return ( - + { enableRollingIndexes: isAtlas, showDisabledConnections: true, enableGenAIFeaturesAtlasProject: - overrideGenAIFeatures || - (isAtlas && !!enableGenAIFeaturesAtlasProject), + !isAtlas || !!enableGenAIFeaturesAtlasProject, enableGenAISampleDocumentPassing: - overrideGenAIFeatures || - (isAtlas && !!enableGenAISampleDocumentPassing), + !isAtlas || !!enableGenAISampleDocumentPassing, enableGenAIFeaturesAtlasOrg: - overrideGenAIFeatures || - (isAtlas && !!enableGenAIFeaturesAtlasOrg), - optInGenAIFeatures: - overrideGenAIFeatures || (isAtlas && !!optInGenAIFeatures), + !isAtlas || !!enableGenAIFeaturesAtlasOrg, + optInGenAIFeatures: isAtlas ? !!optInGenAIFeatures : false, enableDataModeling: true, enableMyQueries: false, ...groupRolePreferences, @@ -135,7 +129,7 @@ const App = () => { onFailToLoadConnections={onFailToLoadConnections} > - + ); }; diff --git a/packages/compass-web/sandbox/sandbox-atlas-sign-in.tsx b/packages/compass-web/sandbox/sandbox-atlas-sign-in.tsx index b7d7b1f72e9..51c5c964bc0 100644 --- a/packages/compass-web/sandbox/sandbox-atlas-sign-in.tsx +++ b/packages/compass-web/sandbox/sandbox-atlas-sign-in.tsx @@ -130,25 +130,20 @@ export function useAtlasProxySignIn(): AtlasLoginReturnValue { userRoles, currentOrganization, } = params; - const overrideGenAIFeatures = - process.env.COMPASS_OVERRIDE_ENABLE_AI_FEATURES === 'true'; setProjectParams({ orgId: currentOrganization.id, projectId, csrfToken, csrfTime, optInGenAIFeatures: isOptedIntoDataExplorerGenAIFeatures, - enableGenAIFeaturesAtlasOrg: - overrideGenAIFeatures || genAIFeaturesEnabled, + enableGenAIFeaturesAtlasOrg: genAIFeaturesEnabled, enableGenAISampleDocumentPassing: !groupEnabledFeatureFlags.includes( 'DISABLE_DATA_EXPLORER_GEN_AI_SAMPLE_DOCUMENT_PASSING' ), - enableGenAIFeaturesAtlasProject: - overrideGenAIFeatures || - groupEnabledFeatureFlags.includes( - 'ENABLE_DATA_EXPLORER_GEN_AI_FEATURES' - ), + enableGenAIFeaturesAtlasProject: groupEnabledFeatureFlags.includes( + 'ENABLE_DATA_EXPLORER_GEN_AI_FEATURES' + ), userRoles, }); setStatus('signed-in'); diff --git a/packages/compass-web/sandbox/sandbox-process.ts b/packages/compass-web/sandbox/sandbox-process.ts new file mode 100644 index 00000000000..6a850b49e12 --- /dev/null +++ b/packages/compass-web/sandbox/sandbox-process.ts @@ -0,0 +1,12 @@ +const kSandboxSetEnvFn = Symbol.for('@compass-web-sandbox-set-env'); +// eslint-disable-next-line no-console +console.info( + `[compass-web sandbox] call window[Symbol.for('@compass-web-sandbox-set-env')]('KEY', 'value') to dynamically set environment variables` +); +// Even though it doesn't look like it, process is module scoped in web bundle, +// to allow overriding it in runtime in the sandbox we add a special global +// scoped method +// eslint-disable-next-line @typescript-eslint/no-explicit-any +(globalThis as any)[kSandboxSetEnvFn] = (key: string, value: string) => { + process.env[key] = value; +}; diff --git a/packages/compass-web/src/entrypoint.tsx b/packages/compass-web/src/entrypoint.tsx index e2b33f2e2a9..1ade5ef2d2e 100644 --- a/packages/compass-web/src/entrypoint.tsx +++ b/packages/compass-web/src/entrypoint.tsx @@ -47,7 +47,10 @@ import type { AllPreferences, AtlasCloudFeatureFlags, } from 'compass-preferences-model/provider'; -import { PreferencesProvider } from 'compass-preferences-model/provider'; +import { + PreferencesProvider, + usePreference, +} from 'compass-preferences-model/provider'; import FieldStorePlugin from '@mongodb-js/compass-field-store'; import { atlasServiceLocator, @@ -55,7 +58,10 @@ import { } from '@mongodb-js/atlas-service/provider'; import { AtlasAiServiceProvider } from '@mongodb-js/compass-generative-ai/provider'; import { LoggerProvider } from '@mongodb-js/compass-logging/provider'; -import { TelemetryProvider } from '@mongodb-js/compass-telemetry/provider'; +import { + TelemetryProvider, + useTelemetry, +} from '@mongodb-js/compass-telemetry/provider'; import CompassConnections from '@mongodb-js/compass-connections'; import { AtlasCloudConnectionStorageProvider } from './connection-storage'; import { AtlasCloudAuthServiceProvider } from './atlas-auth-service'; @@ -383,6 +389,79 @@ const connectedContainerStyles = css({ display: 'flex', }); +const CompassWebComponentsProvider: React.FunctionComponent<{ + darkMode?: boolean; +}> = ({ darkMode, children }) => { + const track = useTelemetry(); + const disableGuideCues = !usePreference('enableGuideCues'); + return ( + + ); +}; + /** @public */ const CompassWeb = ({ appName, @@ -452,67 +531,7 @@ const CompassWeb = ({ return ( - + ); diff --git a/packages/compass-web/src/preferences.tsx b/packages/compass-web/src/preferences.tsx index 73ce582a970..91bccc01a68 100644 --- a/packages/compass-web/src/preferences.tsx +++ b/packages/compass-web/src/preferences.tsx @@ -1,55 +1,48 @@ -import React, { useContext, useEffect, useRef, useState } from 'react'; +import React, { useCallback, useContext, useEffect, useRef } from 'react'; import type { AllPreferences, AtlasCloudFeatureFlags, + PreferencesAccess, } from 'compass-preferences-model/provider'; import { CompassWebPreferencesAccess } from 'compass-preferences-model/provider'; -export type SandboxPreferencesUpdateTrigger = ( - updatePreference: ( - preferences: Partial - ) => Promise +type SandboxSetPreferencesGlobalAccess = ( + preferences: PreferencesAccess ) => () => void; -const SandboxPreferencesUpdateTriggerContext = - React.createContext(null); +const SandboxPreferencesGlobalAccessContext = + React.createContext(null); -const kSandboxUpdateFn = Symbol.for('@compass-web-sandbox-update-preferences'); +const kSandboxPreferencesAccess = Symbol.for( + '@compass-web-sandbox-preferences-access' +); /** * Only used in the sandbox to provide a way to update preferences. * @internal */ -export const SandboxPreferencesUpdateProvider = ({ +export const SandboxPreferencesGlobalAccessProvider = ({ children, }: { children: React.ReactNode; }) => { - const [updateTrigger] = useState(() => { - return ( - updatePreferencesFn: ( - preferences: Partial - ) => Promise - ) => { - // eslint-disable-next-line no-console - console.info( - `[compass-web sandbox] call window[Symbol.for('@compass-web-sandbox-update-preferences')]({}) to dynamically update preferences` - ); - (globalThis as any)[kSandboxUpdateFn] = ( - preferences: Partial - ) => { - return updatePreferencesFn(preferences); - }; - return () => { - delete (globalThis as any)[kSandboxUpdateFn]; - }; + const setPreferencesAccess = useCallback((preferences: PreferencesAccess) => { + (globalThis as any)[kSandboxPreferencesAccess] = preferences; + // eslint-disable-next-line no-console + console.info( + `[compass-web sandbox] call window[Symbol.for('@compass-web-sandbox-preferences-access')].savePreferences({}) to dynamically update preferences` + ); + return () => { + delete (globalThis as any)[kSandboxPreferencesAccess]; }; - }); + }, []); return ( - + {children} - + ); }; @@ -92,17 +85,15 @@ export function useCompassWebPreferences( ) ); - const onPreferencesUpdateTriggered = useContext( - SandboxPreferencesUpdateTriggerContext + const setPreferencesAccess = useContext( + SandboxPreferencesGlobalAccessContext ); useEffect(() => { // This is used by our sandbox so that we can call a global function in the - // browser from the sandbox / testing runtime to update preferences. - return onPreferencesUpdateTriggered?.(async (preferences) => { - return await preferencesAccess.current.savePreferences(preferences); - }); - }, [onPreferencesUpdateTriggered]); + // browser from the sandbox / testing runtime to access preferences. + return setPreferencesAccess?.(preferencesAccess.current); + }, [setPreferencesAccess]); return preferencesAccess; } diff --git a/packages/compass-web/webpack.config.js b/packages/compass-web/webpack.config.js index 93cbc21d1ef..bff3ec5ad3b 100644 --- a/packages/compass-web/webpack.config.js +++ b/packages/compass-web/webpack.config.js @@ -151,28 +151,10 @@ module.exports = (env, args) => { // Can be either `web` or `webdriverio`, helpful if we need special // behavior for tests in sandbox 'process.env.APP_ENV': JSON.stringify(process.env.APP_ENV ?? 'web'), - ...(process.env.COMPASS_ASSISTANT_BASE_URL_OVERRIDE - ? { - 'process.env.COMPASS_ASSISTANT_BASE_URL_OVERRIDE': JSON.stringify( - process.env.COMPASS_ASSISTANT_BASE_URL_OVERRIDE - ), - } - : {}), - ...(process.env.COMPASS_OVERRIDE_ENABLE_AI_FEATURES - ? { - 'process.env.COMPASS_OVERRIDE_ENABLE_AI_FEATURES': JSON.stringify( - process.env.COMPASS_OVERRIDE_ENABLE_AI_FEATURES - ), - } - : {}), - ...(process.env.COMPASS_DISABLE_VIRTUAL_TABLE_RENDERING - ? { - 'process.env.COMPASS_DISABLE_VIRTUAL_TABLE_RENDERING': - JSON.stringify( - process.env.COMPASS_DISABLE_VIRTUAL_TABLE_RENDERING - ), - } - : {}), + // NB: DefinePlugin completely replaces matched string with a provided + // value, in most cases WE DO NOT WANT THAT and process variables in the + // code are added to be able to change them in runtime. Do not add new + // records unless you're super sure it's needed }), new webpack.ProvidePlugin({ diff --git a/packages/compass/src/app/components/home.tsx b/packages/compass/src/app/components/home.tsx index db15bedbbb5..565dc5bffc6 100644 --- a/packages/compass/src/app/components/home.tsx +++ b/packages/compass/src/app/components/home.tsx @@ -163,8 +163,10 @@ export default function ThemedHome( ): ReturnType { const track = useTelemetry(); const disableContextMenus = !usePreference('enableContextMenus'); + const disableGuideCues = !usePreference('enableGuideCues'); return ( { track('Guide Cue Dismissed', { groupId: cue.groupId,