fix(ci): repair validate and windows checks
This commit is contained in:
parent
f92b77e3af
commit
a5c79518fb
4 changed files with 50 additions and 47 deletions
|
|
@ -125,13 +125,7 @@ export function hasInstallationInScope(
|
|||
return installations.some((installation) => installation.scope === scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a concise install-status label for plugin badges.
|
||||
*/
|
||||
export function getInstallationSummaryLabel(
|
||||
installations: Pick<InstalledPluginEntry, 'scope'>[]
|
||||
): string | null {
|
||||
const scopes = Array.from(new Set(installations.map((installation) => installation.scope)));
|
||||
function summarizeInstallationScopes(scopes: InstallScope[]): string | null {
|
||||
if (scopes.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -152,6 +146,16 @@ export function getInstallationSummaryLabel(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a concise install-status label for plugin badges.
|
||||
*/
|
||||
export function getInstallationSummaryLabel(
|
||||
installations: Pick<InstalledPluginEntry, 'scope'>[]
|
||||
): string | null {
|
||||
const scopes = Array.from(new Set(installations.map((installation) => installation.scope)));
|
||||
return summarizeInstallationScopes(scopes);
|
||||
}
|
||||
|
||||
const MCP_SCOPE_PRIORITY: Record<InstalledMcpEntry['scope'], number> = {
|
||||
local: 0,
|
||||
project: 1,
|
||||
|
|
@ -181,24 +185,7 @@ export function getMcpInstallationSummaryLabel(
|
|||
installations: Pick<InstalledMcpEntry, 'scope'>[]
|
||||
): string | null {
|
||||
const scopes = Array.from(new Set(installations.map((installation) => installation.scope)));
|
||||
if (scopes.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (scopes.length > 1) {
|
||||
return `Installed in ${scopes.length} scopes`;
|
||||
}
|
||||
|
||||
switch (scopes[0]) {
|
||||
case 'user':
|
||||
return 'Installed globally';
|
||||
case 'project':
|
||||
return 'Installed in project';
|
||||
case 'local':
|
||||
return 'Installed locally';
|
||||
default:
|
||||
return 'Installed';
|
||||
}
|
||||
return summarizeInstallationScopes(scopes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,10 +1,20 @@
|
|||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import * as fs from 'node:fs/promises';
|
||||
import * as path from 'node:path';
|
||||
|
||||
import { McpInstallationStateService } from '@main/services/extensions/state/McpInstallationStateService';
|
||||
|
||||
const TEST_ROOT = path.parse(process.cwd()).root || path.sep;
|
||||
const MOCK_HOME_PATH = path.join(TEST_ROOT, 'tmp', 'mock-home');
|
||||
const PROJECT_A_PATH = path.join(TEST_ROOT, 'tmp', 'project-a');
|
||||
const PROJECT_B_PATH = path.join(TEST_ROOT, 'tmp', 'project-b');
|
||||
|
||||
function normalizeMockPath(filePath: unknown): string {
|
||||
return String(filePath).replaceAll('\\', '/');
|
||||
}
|
||||
|
||||
vi.mock('@main/utils/pathDecoder', () => ({
|
||||
getHomeDir: () => '/tmp/mock-home',
|
||||
getHomeDir: () => MOCK_HOME_PATH,
|
||||
}));
|
||||
|
||||
vi.mock('node:fs/promises');
|
||||
|
|
@ -25,14 +35,14 @@ describe('McpInstallationStateService', () => {
|
|||
describe('getInstalled', () => {
|
||||
it('includes local scope from the current project entry in ~/.claude.json', async () => {
|
||||
mockedFs.readFile.mockImplementation(async (filePath) => {
|
||||
const normalizedPath = String(filePath);
|
||||
if (normalizedPath === '/tmp/mock-home/.claude.json') {
|
||||
const normalizedPath = normalizeMockPath(filePath);
|
||||
if (normalizedPath === normalizeMockPath(path.join(MOCK_HOME_PATH, '.claude.json'))) {
|
||||
return JSON.stringify({
|
||||
mcpServers: {
|
||||
context7: { command: 'npx -y @upstash/context7-mcp' },
|
||||
},
|
||||
projects: {
|
||||
'/tmp/project-a': {
|
||||
[PROJECT_A_PATH]: {
|
||||
mcpServers: {
|
||||
stripe: { url: 'https://mcp.stripe.com' },
|
||||
},
|
||||
|
|
@ -41,7 +51,7 @@ describe('McpInstallationStateService', () => {
|
|||
});
|
||||
}
|
||||
|
||||
if (normalizedPath === '/tmp/project-a/.mcp.json') {
|
||||
if (normalizedPath === normalizeMockPath(path.join(PROJECT_A_PATH, '.mcp.json'))) {
|
||||
return JSON.stringify({
|
||||
mcpServers: {
|
||||
paypal: { url: 'https://mcp.paypal.com/mcp' },
|
||||
|
|
@ -52,7 +62,7 @@ describe('McpInstallationStateService', () => {
|
|||
throw Object.assign(new Error('ENOENT'), { code: 'ENOENT' });
|
||||
});
|
||||
|
||||
const entries = await service.getInstalled('/tmp/project-a');
|
||||
const entries = await service.getInstalled(PROJECT_A_PATH);
|
||||
|
||||
expect(entries).toEqual([
|
||||
{ name: 'context7', scope: 'user', transport: 'stdio' },
|
||||
|
|
@ -63,8 +73,8 @@ describe('McpInstallationStateService', () => {
|
|||
|
||||
it('caches results within TTL for the same project path', async () => {
|
||||
mockedFs.readFile.mockImplementation(async (filePath) => {
|
||||
const normalizedPath = String(filePath);
|
||||
if (normalizedPath === '/tmp/mock-home/.claude.json') {
|
||||
const normalizedPath = normalizeMockPath(filePath);
|
||||
if (normalizedPath === normalizeMockPath(path.join(MOCK_HOME_PATH, '.claude.json'))) {
|
||||
return JSON.stringify({
|
||||
mcpServers: {
|
||||
context7: { command: 'npx -y @upstash/context7-mcp' },
|
||||
|
|
@ -72,7 +82,7 @@ describe('McpInstallationStateService', () => {
|
|||
});
|
||||
}
|
||||
|
||||
if (normalizedPath === '/tmp/project-a/.mcp.json') {
|
||||
if (normalizedPath === normalizeMockPath(path.join(PROJECT_A_PATH, '.mcp.json'))) {
|
||||
return JSON.stringify({
|
||||
mcpServers: {
|
||||
'repo-a-server': { url: 'https://repo-a.example.com/mcp' },
|
||||
|
|
@ -83,27 +93,27 @@ describe('McpInstallationStateService', () => {
|
|||
throw Object.assign(new Error('ENOENT'), { code: 'ENOENT' });
|
||||
});
|
||||
|
||||
await service.getInstalled('/tmp/project-a');
|
||||
await service.getInstalled('/tmp/project-a');
|
||||
await service.getInstalled(PROJECT_A_PATH);
|
||||
await service.getInstalled(PROJECT_A_PATH);
|
||||
|
||||
expect(mockedFs.readFile).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('caches results independently per project path', async () => {
|
||||
mockedFs.readFile.mockImplementation(async (filePath) => {
|
||||
const normalizedPath = String(filePath);
|
||||
if (normalizedPath === '/tmp/mock-home/.claude.json') {
|
||||
const normalizedPath = normalizeMockPath(filePath);
|
||||
if (normalizedPath === normalizeMockPath(path.join(MOCK_HOME_PATH, '.claude.json'))) {
|
||||
return JSON.stringify({
|
||||
mcpServers: {
|
||||
context7: { command: 'npx -y @upstash/context7-mcp' },
|
||||
},
|
||||
projects: {
|
||||
'/tmp/project-a': {
|
||||
[PROJECT_A_PATH]: {
|
||||
mcpServers: {
|
||||
stripe: { url: 'https://mcp.stripe.com' },
|
||||
},
|
||||
},
|
||||
'/tmp/project-b': {
|
||||
[PROJECT_B_PATH]: {
|
||||
mcpServers: {
|
||||
github: { command: 'uvx github-mcp' },
|
||||
},
|
||||
|
|
@ -112,7 +122,7 @@ describe('McpInstallationStateService', () => {
|
|||
});
|
||||
}
|
||||
|
||||
if (normalizedPath === '/tmp/project-a/.mcp.json') {
|
||||
if (normalizedPath === normalizeMockPath(path.join(PROJECT_A_PATH, '.mcp.json'))) {
|
||||
return JSON.stringify({
|
||||
mcpServers: {
|
||||
'repo-a-server': { url: 'https://repo-a.example.com/mcp' },
|
||||
|
|
@ -120,7 +130,7 @@ describe('McpInstallationStateService', () => {
|
|||
});
|
||||
}
|
||||
|
||||
if (normalizedPath === '/tmp/project-b/.mcp.json') {
|
||||
if (normalizedPath === normalizeMockPath(path.join(PROJECT_B_PATH, '.mcp.json'))) {
|
||||
return JSON.stringify({
|
||||
mcpServers: {
|
||||
'repo-b-server': { command: 'uvx repo-b-mcp' },
|
||||
|
|
@ -131,8 +141,8 @@ describe('McpInstallationStateService', () => {
|
|||
throw Object.assign(new Error('ENOENT'), { code: 'ENOENT' });
|
||||
});
|
||||
|
||||
const projectAEntries = await service.getInstalled('/tmp/project-a');
|
||||
const projectBEntries = await service.getInstalled('/tmp/project-b');
|
||||
const projectAEntries = await service.getInstalled(PROJECT_A_PATH);
|
||||
const projectBEntries = await service.getInstalled(PROJECT_B_PATH);
|
||||
|
||||
expect(projectAEntries).toEqual([
|
||||
{ name: 'context7', scope: 'user', transport: 'stdio' },
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ const MOCK_CLAUDE_BASE_PATH = path.join(TEST_ROOT, 'tmp', 'mock-claude');
|
|||
const PROJECT_A_PATH = path.join(TEST_ROOT, 'tmp', 'project-a');
|
||||
const PROJECT_B_PATH = path.join(TEST_ROOT, 'tmp', 'project-b');
|
||||
|
||||
function normalizeMockPath(filePath: string | URL): string {
|
||||
function normalizeMockPath(filePath: unknown): string {
|
||||
return String(filePath).replaceAll('\\', '/');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -257,9 +257,15 @@ describe('PluginDetailDialog project context', () => {
|
|||
});
|
||||
|
||||
const scopeSelect = host.querySelector('[data-testid="scope-select"]') as HTMLSelectElement;
|
||||
const projectOption = scopeSelect.querySelector(
|
||||
'option[value="project"]'
|
||||
) as HTMLOptionElement | null;
|
||||
const localOption = scopeSelect.querySelector(
|
||||
'option[value="local"]'
|
||||
) as HTMLOptionElement | null;
|
||||
expect(scopeSelect).not.toBeNull();
|
||||
expect(scopeSelect.querySelector('option[value="project"]')?.disabled).toBe(true);
|
||||
expect(scopeSelect.querySelector('option[value="local"]')?.disabled).toBe(true);
|
||||
expect(projectOption?.disabled).toBe(true);
|
||||
expect(localOption?.disabled).toBe(true);
|
||||
|
||||
await act(async () => {
|
||||
root.unmount();
|
||||
|
|
|
|||
Loading…
Reference in a new issue