Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
# Change Log



## v0.9.5 (prerelease)

Date: 2026-05-28

* adds check for no test cases
* enhances CQL Explorer multiple-projects support
* adds CqlSolution
* fixes issue with default test config settings
* bump version to 0.9.5


## v0.9.4 (prerelease)

Date: 2026-05-14
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

66 changes: 44 additions & 22 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cql",
"version": "0.9.4",
"version": "0.9.5",
"displayName": "Clinical Quality Language (CQL)",
"description": "Syntax highlighting, linting, and execution for the HL7 Clinical Quality Language (CQL) for VS Code",
"publisher": "cqframework",
Expand Down Expand Up @@ -228,6 +228,18 @@
"icon": "$(run-all)",
"category": "CQL Explorer"
},
{
"command": "cql.explorer.project.execute-select",
"title": "Select and Execute Libraries",
"category": "CQL Explorer",
"icon": "$(play)"
},
{
"command": "cql.explorer.project.execute-all",
"title": "Execute All Libraries",
"icon": "$(run-all)",
"category": "CQL Explorer"
},
{
"command": "cql.explorer.test-case.filter",
"title": "Filter Test Cases",
Expand Down Expand Up @@ -405,6 +417,14 @@
"command": "cql.explorer.test-case.execute-all",
"when": "false"
},
{
"command": "cql.explorer.project.execute-all",
"when": "false"
},
{
"command": "cql.explorer.project.execute-select",
"when": "false"
},
{
"command": "cql.explorer.expand-all",
"when": "false"
Expand Down Expand Up @@ -540,12 +560,32 @@
"command": "cql.explorer.test-case.clear-filter",
"when": "view == cql-libraries && viewItem =~ /cql-testcase-root-filtered/",
"group": "inline@3"
},
{
"command": "cql.explorer.project.execute-select",
"when": "view == cql-libraries && viewItem == cql-project-root",
"group": "1:project"
},
{
"command": "cql.explorer.project.execute-all",
"when": "view == cql-libraries && viewItem == cql-project-root",
"group": "1:project"
},
{
"command": "cql.explorer.project.execute-select",
"when": "view == cql-libraries && viewItem == cql-project-root",
"group": "inline@1"
},
{
"command": "cql.explorer.project.execute-all",
"when": "view == cql-libraries && viewItem == cql-project-root",
"group": "inline@2"
}
],
"view/title": [
{
"command": "cql.execute.select-libraries",
"when": "view == cql-libraries",
"when": "view == cql-libraries && cql.solutionLoaded",
"group": "navigation@1"
},
{
Expand Down Expand Up @@ -623,25 +663,7 @@
"fileMatch": "**/input/tests/config.jsonc",
"url": "./schemas/cql-config.schema.json"
}
],
"configuration": {
"title": "CQL",
"properties": {
"cql.execute.resultFormat": {
"type": "string",
"enum": [
"individual",
"flat"
],
"enumDescriptions": [
"Save one JSON result file per test case to input/tests/results/{libraryName}/TestCaseResult-{patientId}.json",
"Save all test case results for a library to a single text file at input/tests/results/{libraryName}.txt"
],
"default": "individual",
"description": "Controls how Execute CQL results are saved."
}
}
}
]
},
"resolutions": {
"color-name": "1.1.3"
Expand All @@ -658,7 +680,7 @@
"cql-language-server": {
"groupId": "org.opencds.cqf.cql.ls",
"artifactId": "cql-ls-server",
"version": "4.6.0"
"version": "4.7.0"
}
},
"devDependencies": {
Expand Down
4 changes: 4 additions & 0 deletions src/__test__/resources/simple-project/input/tests/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"testCasesToExclude": [],
"resultFormat": "flat"
}
2 changes: 1 addition & 1 deletion src/__test__/suite/commands/execute-cql.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ suite('loadTestConfig()', () => {
const configPath = path.join(tmpDir, 'config.json');
fs.writeFileSync(configPath, JSON.stringify(config));
const result = loadTestConfig(Uri.file(configPath));
expect(result.resultFormat).to.be.undefined;
expect(result.resultFormat).to.equal('flat');
});

test('returns individual resultFormat when config specifies individual', () => {
Expand Down
71 changes: 69 additions & 2 deletions src/__test__/suite/cql-explorer/buildTree.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { expect } from 'chai';
import * as path from 'path';
import * as vscode from 'vscode';
import { Uri, workspace } from 'vscode';
import { CqlLibrary, CqlProject, CqlTestCase } from '../../../cql-explorer/cqlProject';
import { CqlLibrary, CqlProject, CqlTestCase } from '../../../model/cqlProject';
import {
CqlLibraryRootTreeItem,
CqlLibraryTreeItem,
Expand All @@ -12,7 +12,7 @@ import {
CqlTestCaseTreeItem,
buildTree,
} from '../../../cql-explorer/cqlProjectTreeDataProvider';
import { DeviationKind } from '../../../cql-explorer/igLayoutDetector';
import { DeviationKind } from '../../../model/igLayoutDetector';

suite('CqlProjectTreeDataProvider.nodeId()', () => {
// Expected nodeId values per platform:
Expand Down Expand Up @@ -245,6 +245,73 @@ suite('buildTree — multi-project and deviations', () => {
});
});

suite('buildTree — hideEmpty project filtering (multi-project)', () => {
const wsRoot = workspace.workspaceFolders![0].uri;

function makeLib(name: string): CqlLibrary {
return new CqlLibrary(Uri.joinPath(wsRoot, `input/cql/${name}.cql`));
}

function makeLoadedLib(name: string): CqlLibrary {
const lib = makeLib(name);
lib.testCaseLoadState = 'loaded';
return lib;
}

function fakeProject(name: string, libs: CqlLibrary[]): CqlProject {
return {
igRoot: `/fake/${name}`,
name,
projectDeviations: new Set<DeviationKind>(),
Libraries: libs,
} as unknown as CqlProject;
}

test('hideEmpty=false keeps projects with no test cases', () => {
const emptyLib = makeLoadedLib('EmptyLib');
const p1 = fakeProject('ProjectA', [emptyLib]);
const p2 = fakeProject('ProjectB', [makeLib('OtherLib')]);
const items = buildTree([p1, p2], false);
expect(items).to.have.length(2);
});

test('hideEmpty=true hides a project whose only library has no test cases', () => {
const emptyLib = makeLoadedLib('EmptyLib');
const p1 = fakeProject('EmptyProject', [emptyLib]);
const libWithTests = makeLoadedLib('LibWithTests');
libWithTests.addTestCase(
new CqlTestCase(Uri.joinPath(wsRoot, 'input/tests/Measure/LibWithTests/1111')),
);
const p2 = fakeProject('NonEmptyProject', [libWithTests]);
const items = buildTree([p1, p2], true);
expect(items).to.have.length(1);
expect((items[0] as CqlProjectRootTreeItem).cqlProject.name).to.equal('NonEmptyProject');
});

test('hideEmpty=true keeps a project that has at least one library with test cases', () => {
const emptyLib = makeLoadedLib('EmptyLib');
const libWithTests = makeLoadedLib('LibWithTests');
libWithTests.addTestCase(
new CqlTestCase(Uri.joinPath(wsRoot, 'input/tests/Measure/LibWithTests/1111')),
);
const p1 = fakeProject('MixedProject', [emptyLib, libWithTests]);
const p2 = fakeProject('OtherProject', [makeLoadedLib('OtherEmpty')]);
const items = buildTree([p1, p2], true);
expect(items).to.have.length(1);
expect((items[0] as CqlProjectRootTreeItem).cqlProject.name).to.equal('MixedProject');
});

test('hideEmpty=true keeps a project whose library testCaseLoadState is not loaded', () => {
const loadingLib = makeLib('LoadingLib'); // testCaseLoadState = 'not-loaded'
const p1 = fakeProject('LoadingProject', [loadingLib]);
const p2 = fakeProject('EmptyProject', [makeLoadedLib('EmptyLib')]);
const items = buildTree([p1, p2], true);
// p1 is kept because its library hasn't finished loading
expect(items).to.have.length(1);
expect((items[0] as CqlProjectRootTreeItem).cqlProject.name).to.equal('LoadingProject');
});
});

suite('CqlProjectTreeDataProvider.nodeId() — CqlProjectRootTreeItem', () => {
test('CqlProjectRootTreeItem ID equals igRoot', () => {
const wsRoot = workspace.workspaceFolders![0].uri;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { expect } from 'chai';
import * as vscode from 'vscode';
import { Uri, workspace } from 'vscode';
import { CqlLibrary, CqlTestCase } from '../../../cql-explorer/cqlProject';
import { CqlLibrary, CqlTestCase } from '../../../model/cqlProject';
import {
CqlLibraryRootTreeItem,
CqlResultsRootTreeItem,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { expect } from 'chai';
import * as vscode from 'vscode';
import { Uri, workspace } from 'vscode';
import { CqlLibrary, CqlTestCase } from '../../../cql-explorer/cqlProject';
import { CqlLibrary, CqlTestCase } from '../../../model/cqlProject';
import {
CqlTestCaseRootTreeItem,
} from '../../../cql-explorer/cqlProjectTreeDataProvider';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect } from 'chai';
import { Uri, workspace } from 'vscode';
import { CqlTestCase } from '../../../cql-explorer/cqlProject';
import { CqlTestCase } from '../../../model/cqlProject';
import { CqlTestCaseTreeItem } from '../../../cql-explorer/cqlProjectTreeDataProvider';

suite('CqlTestCaseTreeItem', () => {
Expand Down
4 changes: 2 additions & 2 deletions src/__test__/suite/cql-explorer/getChildren.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { expect } from 'chai';
import * as vscode from 'vscode';
import { Uri, workspace } from 'vscode';
import { CqlLibrary, CqlProject, CqlTestCase } from '../../../cql-explorer/cqlProject';
import { CqlLibrary, CqlProject, CqlTestCase } from '../../../model/cqlProject';
import {
CqlLibraryRootTreeItem,
CqlProjectRootTreeItem,
CqlProjectTreeDataProvider,
CqlTestCasesLoadingTreeItem,
} from '../../../cql-explorer/cqlProjectTreeDataProvider';
import { DeviationKind } from '../../../cql-explorer/igLayoutDetector';
import { DeviationKind } from '../../../model/igLayoutDetector';

function fakeProject(name: string, libs: CqlLibrary[]): CqlProject {
return {
Expand Down
3 changes: 3 additions & 0 deletions src/__test__/suite/extension/commands.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ const EXPECTED_COMMANDS = [
'cql.explorer.sort-desc',
'cql.explorer.show-layout-warnings',
'cql.explorer.hide-layout-warnings',
// Explorer project node commands
'cql.explorer.project.execute-all',
'cql.explorer.project.execute-select',
// Explorer library node commands
'cql.explorer.library.execute-all',
'cql.explorer.library.execute',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { expect } from 'chai';
import * as vscode from 'vscode';
import { Uri, workspace } from 'vscode';
import { CqlLibrary, CqlProject } from '../../../cql-explorer/cqlProject';
import { CqlLibrary, CqlProject } from '../../../model/cqlProject';
import { CqlTestCasesLoadingTreeItem } from '../../../cql-explorer/cqlProjectTreeDataProvider';

suite('CqlLibrary — initial load state', () => {
Expand Down Expand Up @@ -51,6 +51,18 @@ suite('CqlProject.loadTestCasesForLibrary', () => {
});
});

suite('CqlProject.loadResults', () => {
test('resolves without error when results folder does not exist', async () => {
const wsRoot = workspace.workspaceFolders![0].uri;
const project = new CqlProject(wsRoot.fsPath, []);
// loadResults is private; call via cast. The results folder won't exist in
// the test workspace, so the inner readdir catch fires and we return early.
// Before the fix this returned without logging; after the fix the finally
// block always logs. Either way the promise must resolve, not reject.
await (project as any).loadResults();
});
});

suite('CqlTestCasesLoadingTreeItem', () => {
test('has spinner icon', () => {
const item = new CqlTestCasesLoadingTreeItem();
Expand Down
Loading
Loading