qwen-code/scripts/tests/dev.test.js
Shaojin Wen da22360c25
feat(web-shell): show the qwen-code version in the sidebar footer (#6222)
* feat(web-shell): show the qwen-code version in the sidebar footer

The Web Shell had no visible version. Show the running qwen-code version (from the daemon capabilities) in the sidebar footer, inline with the Settings button so it stays visible without taking its own row.

Render the version consistently wherever it appears:
- Prefix "v" only for a real semver release; a non-semver fallback such as "unknown" is shown as-is, so we never render a bogus "vunknown". Applied to the Web Shell badge and the TUI header.
- Dev builds (scripts/dev.js) now report the real package version instead of the "dev" sentinel, matching scripts/start.js, so the UI shows the actual version (e.g. v0.19.4). DEV=true / NODE_ENV=development remain the signals that mark a dev build.

* test: add readFileSync to node:fs mock in dev.test.js

scripts/dev.js now reads package.json via readFileSync at module load to
report the real CLI_VERSION, but the node:fs mock in dev.test.js did not
export readFileSync, causing vitest to throw "No readFileSync export is
defined on the node:fs mock" and failing the suite.
2026-07-03 08:56:08 +00:00

98 lines
2.9 KiB
JavaScript

/**
* @license
* Copyright 2025 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*/
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
const { spawnMock, platformMock, existsSyncMock } = vi.hoisted(() => ({
spawnMock: vi.fn(() => ({ on: vi.fn() })),
platformMock: vi.fn(() => 'darwin'),
existsSyncMock: vi.fn(() => false),
}));
vi.mock('node:child_process', () => ({
spawn: spawnMock,
}));
vi.mock('node:os', async (importOriginal) => {
const actual = await importOriginal();
return {
...actual,
platform: platformMock,
tmpdir: vi.fn(() => '/tmp'),
};
});
vi.mock('node:fs', () => ({
writeFileSync: vi.fn(),
mkdtempSync: vi.fn(() => '/tmp/qwen-dev-test'),
rmSync: vi.fn(),
existsSync: existsSyncMock,
symlinkSync: vi.fn(),
mkdirSync: vi.fn(),
readFileSync: vi.fn(() => JSON.stringify({ version: '0.0.0-test' })),
}));
const normalizePath = (path) => String(path).replaceAll('\\', '/');
describe('scripts/dev.js launcher', () => {
const originalArgv = process.argv;
const execPathDescriptor = Object.getOwnPropertyDescriptor(
process,
'execPath',
);
beforeEach(() => {
vi.resetModules();
vi.clearAllMocks();
process.argv = ['node', 'scripts/dev.js'];
});
afterEach(() => {
process.argv = originalArgv;
if (execPathDescriptor) {
Object.defineProperty(process, 'execPath', execPathDescriptor);
}
});
it('spawns Node without a shell on Windows when local tsx cli.mjs exists', async () => {
platformMock.mockReturnValue('win32');
existsSyncMock.mockImplementation((filePath) =>
normalizePath(filePath).endsWith('node_modules/tsx/dist/cli.mjs'),
);
Object.defineProperty(process, 'execPath', {
configurable: true,
value: 'C:\\Program Files\\nodejs\\node.exe',
});
process.argv = ['node', 'scripts/dev.js', '--help'];
await import('../dev.js?direct-node');
const [command, args, options] = spawnMock.mock.calls[0];
expect(command).toBe('C:\\Program Files\\nodejs\\node.exe');
expect(args.map(normalizePath)).toEqual([
expect.stringContaining('node_modules/tsx/dist/cli.mjs'),
expect.stringContaining('packages/cli/index.ts'),
'--help',
]);
expect(options).toEqual(expect.objectContaining({ shell: false }));
});
it('keeps shell fallback for Windows tsx.cmd resolution', async () => {
platformMock.mockReturnValue('win32');
existsSyncMock.mockImplementation((filePath) =>
normalizePath(filePath).endsWith('node_modules/.bin/tsx.cmd'),
);
await import('../dev.js?cmd-fallback');
const [command, args, options] = spawnMock.mock.calls[0];
expect(normalizePath(command)).toContain('tsx.cmd');
expect(args.map(normalizePath)).toEqual([
expect.stringContaining('packages/cli/index.ts'),
]);
expect(options).toEqual(expect.objectContaining({ shell: true }));
});
});