mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-08-11 01:36:35 +00:00
* feat(review): add Web Shell review artifacts Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(web-shell): add code review artifact visual scenario (#8402) * fix(review): address Web Shell review artifact feedback (#8402) * save-artifact: document why paths resolve against the daemon workspace root (QWEN_CODE_PROJECT_DIR) instead of cwd, and cover the relative-path form the skill documents with a test where the two roots differ. * CLI/renderer contract: the renderer hand-duplicates the findings vocabulary and fails closed on unknown values, so name the renderer as a second consumer beside the CLI's lists and check in a contract fixture generated through the real pipeline (validateFindings -> buildReport -> save-artifact) that exercises every source, severity, confidence and outcome. Exporting the vocabulary through the SDK stays deferred: it is a public cross-package API change beyond this PR's seam. * resolve-anchors now validates `line` exactly like `findings` does (positive safe integer); the two validators in one pipeline no longer disagree. Note: an in-flight `.qwen/tmp` findings file carrying `line: 0` fails where it previously did not. * The renderer validates markdownReportPath (relative, no ".." segments, .md suffix) before it becomes a readWorkspaceFile call, resets the severity/confidence filters when switching artifacts, and surfaces heldByMeasurement so a nonzero Held count is attributable. * save-artifact refuses low effort structurally (choices and library guard) instead of by prose, stats the Markdown report before reading it so a directory reports "not a file", and the component no longer shadows the DOM `document` global. * The case-insensitive alias test now skips visibly on case-sensitive filesystems instead of passing vacuously. * Comment the kept `turnOutputs.review` key and document the JSON companion in the user docs. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(review): address second Web Shell review artifact feedback round (#8402) --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
97 lines
3.6 KiB
TypeScript
97 lines
3.6 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2026 Qwen Team
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
// The CLI/renderer contract: the fixture is genuine `qwen review save-artifact`
|
|
// output, generated through `buildReport(validateFindings(...))` — not a
|
|
// hand-written copy. If the CLI's document shape or vocabulary changes,
|
|
// regenerate the fixture and bring this parser along.
|
|
|
|
import { readFileSync } from 'node:fs';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { describe, expect, it } from 'vitest';
|
|
import { parseCodeReviewDocument } from './CodeReviewArtifactDetail';
|
|
|
|
const fixture = readFileSync(
|
|
fileURLToPath(
|
|
new URL('./__fixtures__/code-review-artifact-v1.json', import.meta.url),
|
|
),
|
|
'utf8',
|
|
);
|
|
|
|
describe('code review artifact contract', () => {
|
|
it('parses the CLI-generated document without losing fields', () => {
|
|
const reviewDocument = parseCodeReviewDocument(fixture);
|
|
|
|
expect(reviewDocument.schemaVersion).toBe(1);
|
|
expect(reviewDocument.target).toBe('pr-8402');
|
|
expect(reviewDocument.effort).toBe('high');
|
|
// The parser validates only what the renderer displays; persisted-but-
|
|
// unrendered fields (downgraded, outcomesRecorded, byOutcome, ...) pass
|
|
// through unchecked and are asserted nowhere on purpose.
|
|
expect(reviewDocument.verdict).toMatchObject({
|
|
event: 'COMMENT',
|
|
baseEvent: 'REQUEST_CHANGES',
|
|
cappedBy: ['no release-blocker evidence'],
|
|
verdictLine: 'Verdict: Comment — Request changes was downgraded',
|
|
});
|
|
expect(reviewDocument.markdownReportPath).toBe(
|
|
'.qwen/reviews/contract-v1.md',
|
|
);
|
|
|
|
// The fixture exercises the whole vocabulary — every source, severity,
|
|
// confidence and outcome the CLI can canonicalize — so this parse proves
|
|
// the renderer accepts all of it.
|
|
const findings = reviewDocument.findings;
|
|
expect(findings.map((finding) => finding.id)).toEqual([
|
|
'f-critical-review',
|
|
'f-suggestion-build',
|
|
'f-suggestion-test-held',
|
|
'f-suggestion-lint',
|
|
'f-nice-probe',
|
|
]);
|
|
expect(new Set(findings.map((finding) => finding.source))).toEqual(
|
|
new Set(['review', 'build', 'test', 'probe', 'lint']),
|
|
);
|
|
expect(new Set(findings.map((finding) => finding.severity))).toEqual(
|
|
new Set(['Critical', 'Suggestion', 'Nice to have']),
|
|
);
|
|
expect(new Set(findings.map((finding) => finding.confidence))).toEqual(
|
|
new Set(['high', 'low']),
|
|
);
|
|
expect(new Set(findings.map((finding) => finding.outcome))).toEqual(
|
|
new Set(['fixed', 'skipped', 'no_change_needed']),
|
|
);
|
|
|
|
expect(reviewDocument.counts).toEqual({
|
|
total: 5,
|
|
bySeverity: { Critical: 1, Suggestion: 3, 'Nice to have': 1 },
|
|
byConfidence: { high: 4, low: 1 },
|
|
held: 1,
|
|
});
|
|
|
|
const held = findings.find((f) => f.id === 'f-suggestion-test-held');
|
|
expect(held?.heldByMeasurement).toEqual({
|
|
file: 'packages/cli/src/ui/pagination.test.ts',
|
|
});
|
|
|
|
const aggregate = findings.find((f) => f.id === 'f-suggestion-lint');
|
|
expect(aggregate?.locations).toHaveLength(2);
|
|
});
|
|
|
|
it('fails closed on a vocabulary value the renderer does not know yet', () => {
|
|
// The drift mode the contract guards: the CLI adds a value the renderer
|
|
// copy has not been taught, and documents carrying it refuse to render
|
|
// until the renderer is updated with it.
|
|
const drifted = JSON.parse(fixture) as {
|
|
findings: Array<{ source: string }>;
|
|
};
|
|
drifted.findings[0]!.source = 'typecheck';
|
|
|
|
expect(() => parseCodeReviewDocument(JSON.stringify(drifted))).toThrow(
|
|
'findings[0].source has an unsupported value.',
|
|
);
|
|
});
|
|
});
|