Path: blob/main/extensions/copilot/src/extension/test/node/configurations.spec.ts
13399 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/4import * as fs from 'fs';5import * as path from 'path';6import { describe, expect, it } from 'vitest';7import { Config, ConfigKey } from '../../../platform/configuration/common/configurationService';8import { packageJson } from '../../../platform/env/common/packagejson';910describe('Configurations', () => {11it('package.json configuration contains stable, experimental, preview, and advanced sections', () => {12const configurationContributions = packageJson.contributes.configuration;1314// Should have 4 sections15expect(configurationContributions, 'package.json should have exactly 4 sections').toHaveLength(4);1617// Should have a stable section18const stableSection = configurationContributions.find(section => section.id === 'stable');19const preview = configurationContributions.find(section => section.id === 'preview');20const experimental = configurationContributions.find(section => section.id === 'experimental');21const advanced = configurationContributions.find(section => section.id === 'advanced');2223expect(stableSection, 'stable configuration section is missing').toBeDefined();24expect(preview, 'preview configuration section is missing').toBeDefined();25expect(experimental, 'experimental configuration section is missing').toBeDefined();26expect(advanced, 'advanced configuration section is missing').toBeDefined();27});2829it('package.json configuration tags are correct for each section', () => {30const configurationContributions = packageJson.contributes.configuration;3132const stableSection = configurationContributions.find(section => section.id === 'stable')!;33for (const settingId of Object.keys(stableSection?.properties)) {34const setting = stableSection.properties[settingId];35expect(setting.tags ?? [], settingId).not.toContain('preview');36expect(setting.tags ?? [], settingId).not.toContain('experimental');37expect(setting.tags ?? [], settingId).not.toContain('advanced');38}3940const previewSection = configurationContributions.find(section => section.id === 'preview')!;41for (const settingId of Object.keys(previewSection?.properties)) {42const setting = previewSection.properties[settingId];43expect(setting.tags ?? [], settingId).toContain('preview');44expect(setting.tags ?? [], settingId).not.toContain('experimental');45expect(setting.tags ?? [], settingId).not.toContain('advanced');46}4748const experimentalSection = configurationContributions.find(section => section.id === 'experimental')!;49for (const settingId of Object.keys(experimentalSection?.properties)) {50const setting = experimentalSection.properties[settingId];51expect(setting.tags ?? [], settingId).toContain('experimental');52expect(setting.tags ?? [], settingId).not.toContain('preview');53expect(setting.tags ?? [], settingId).not.toContain('advanced');54}5556const advancedSection = configurationContributions.find(section => section.id === 'advanced')!;57for (const settingId of Object.keys(advancedSection?.properties)) {58const setting = advancedSection.properties[settingId];59expect(setting.tags ?? [], settingId).toContain('advanced');60expect(setting.tags ?? [], settingId).not.toContain('preview');61}62});636465it('settings in code should match package.json', () => {6667const configurationsInPackageJson = packageJson.contributes.configuration.flatMap(section => Object.keys(section.properties));68const advancedConfigurationsInPackageJson = packageJson.contributes.configuration.filter(section => section.id === 'advanced').flatMap(section => Object.keys(section.properties));69const otherConfigurationsInPackageJson = packageJson.contributes.configuration.filter(section => section.id !== 'advanced').flatMap(section => Object.keys(section.properties));7071// Get keys from code72const internalKeys = Object.values(ConfigKey.TeamInternal).map(setting => setting.fullyQualifiedId);73const sharedKeys = Object.values(ConfigKey.Shared).map(setting => setting.fullyQualifiedId);74const advancedPublicKeys = Object.values(ConfigKey.Advanced).map(setting => setting.fullyQualifiedId);75const otherPublicKeys = (Object.values(ConfigKey).filter(key => key !== ConfigKey.TeamInternal && key !== ConfigKey.Shared && key !== ConfigKey.Advanced && key !== ConfigKey.Deprecated) as Config<any>[]).map(setting => setting.fullyQualifiedId);76const registered = [...otherPublicKeys, ...advancedPublicKeys];77const unregistered = [...internalKeys, ...sharedKeys];7879// Validate unregistered settings are not in package.json80unregistered.forEach(key => {81expect(configurationsInPackageJson, 'unregistered settings should not be defined in the package.json').not.toContain(key);82});8384// Validate Internal settings have the correct prefix85internalKeys.forEach(key => {86expect(key, 'Internal settings must start with github.copilot.chat.advanced.').toMatch(/^github\.copilot\.chat\.advanced\./);87});8889// Validate public settings in code are in package.json90otherPublicKeys.forEach(key => {91expect(otherConfigurationsInPackageJson, 'Setting in code is not defined in the package.json').toContain(key);92});9394// Validate advanced settings in code are in the advanced section of package.json95advancedPublicKeys.forEach(key => {96expect(key, 'Advanced settings must not start wih github.copilot.chat.advanced.').not.toMatch(/^github\.copilot\.chat\.advanced\./);97if (key === ConfigKey.Advanced.DebugGitHubAuthFailWith.fullyQualifiedId) {98// This setting should be internal, but can't be made TeamInternal because we lose the team and internal flags as part of its testing.99return;100}101expect(advancedConfigurationsInPackageJson, `Advanced setting ${key} should be defined in the advanced section of package.json`).toContain(key);102});103104// Validate settings in package.json are in code105configurationsInPackageJson.forEach(key => {106expect(registered, 'Setting in package.json is not defined in code').toContain(key);107});108});109110it('prompt override string setting uses camelCase', () => {111const advancedSection = packageJson.contributes.configuration.find(section => section.id === 'advanced')!;112const promptOverrideStringKey = ConfigKey.Advanced.DebugPromptOverrideString;113114expect(promptOverrideStringKey.fullyQualifiedId).toBe('github.copilot.chat.debug.promptOverrideString');115expect(promptOverrideStringKey.fullyQualifiedOldId).toBeUndefined();116expect(Object.keys(advancedSection.properties)).toContain(promptOverrideStringKey.fullyQualifiedId);117});118119it('all localization strings in package.json are present in package.nls.json', async () => {120// Get all keys from package.nls.json121const packageJsonPath = path.join(__dirname, '../../../../package.json');122const packageNlsPath = path.join(__dirname, '../../../../package.nls.json');123const [packageJsonFileContents, packageNlsFileContents] = await Promise.all(124[125fs.promises.readFile(packageJsonPath, 'utf-8'),126fs.promises.readFile(packageNlsPath, 'utf-8'),127]128);129130const packageNls = JSON.parse(packageNlsFileContents);131const nlsKeys = Object.keys(packageNls);132133// Find all %key% references in package.json134const nlsReferences = Array.from(packageJsonFileContents.matchAll(/"%([^"]+)%"/g)).map(match => match[1]);135136// Validate all references exist in package.nls.json137const missingKeys = nlsReferences.filter(key => !nlsKeys.includes(key));138if (missingKeys.length > 0) {139throw new Error(`Missing localization keys in package.nls.json but present in package.json: ${missingKeys.map(key => `'%${key}%'`).join(', ')}`);140}141});142});143144145