testing: lazily expand root level items, too
This commit is contained in:
parent
fc528bc5c1
commit
cf77875d5c
11
src/vs/vscode.proposed.d.ts
vendored
11
src/vs/vscode.proposed.d.ts
vendored
|
@ -2018,21 +2018,20 @@ declare module 'vscode' {
|
|||
|
||||
/**
|
||||
* A function provided by the extension that the editor may call to request
|
||||
* children of a test item, if the {@link TestItem.canExpand} is `true`.
|
||||
* When called, the item should discover children and call
|
||||
* children of a test item, if the {@link TestItem.canResolveChildren} is
|
||||
* `true`. When called, the item should discover children and call
|
||||
* {@link vscode.test.createTestItem} as children are discovered.
|
||||
*
|
||||
* The item in the explorer will automatically be marked as "busy" until
|
||||
* the function returns or the returned thenable resolves.
|
||||
*
|
||||
* The controller may wish to set up listeners or watchers to update the
|
||||
* children as files and documents change.
|
||||
* The handler will be called `undefined` to resolve the controller's
|
||||
* initial children.
|
||||
*
|
||||
* @param item An unresolved test item for which
|
||||
* children are being requested
|
||||
*/
|
||||
// todo@API maybe just `resolveHandler` so that we could extends its usage in the future?
|
||||
resolveChildrenHandler?: (item: TestItem) => Thenable<void> | void;
|
||||
resolveChildrenHandler?: (item: TestItem | undefined) => Thenable<void> | void;
|
||||
|
||||
/**
|
||||
* Creates a {@link TestRun<T>}. This should be called by the
|
||||
|
|
|
@ -137,6 +137,6 @@ export class HierarchicalByNameProjection extends HierarchicalByLocationProjecti
|
|||
* @override
|
||||
*/
|
||||
protected override getRevealDepth(element: ByLocationTestItemElement) {
|
||||
return element.depth === 1 ? Infinity : undefined;
|
||||
return element.depth === 0 ? Infinity : undefined;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -428,6 +428,12 @@ export class TestingExplorerViewModel extends Disposable {
|
|||
}
|
||||
}));
|
||||
|
||||
this._register(onDidChangeVisibility(visible => {
|
||||
if (visible) {
|
||||
this.ensureProjection();
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(this.tree.onContextMenu(e => this.onContextMenu(e)));
|
||||
|
||||
this._register(Event.any(
|
||||
|
@ -532,16 +538,14 @@ export class TestingExplorerViewModel extends Disposable {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!this.projection.value) {
|
||||
return;
|
||||
}
|
||||
const projection = this.ensureProjection();
|
||||
|
||||
// If the item itself is visible in the tree, show it. Otherwise, expand
|
||||
// its closest parent.
|
||||
let expandToLevel = 0;
|
||||
const idPath = [...TestId.fromString(id).idsFromRoot()];
|
||||
for (let i = idPath.length - 1; i >= expandToLevel; i--) {
|
||||
const element = this.projection.value.getElementByTestId(idPath[i]);
|
||||
const element = projection.getElementByTestId(idPath[i]);
|
||||
// Skip all elements that aren't in the tree.
|
||||
if (!element || !this.tree.hasElement(element)) {
|
||||
continue;
|
||||
|
@ -665,6 +669,10 @@ export class TestingExplorerViewModel extends Disposable {
|
|||
}
|
||||
}
|
||||
|
||||
private ensureProjection() {
|
||||
return this.projection.value ?? this.updatePreferredProjection();
|
||||
}
|
||||
|
||||
private updatePreferredProjection() {
|
||||
this.projection.clear();
|
||||
|
||||
|
@ -682,6 +690,7 @@ export class TestingExplorerViewModel extends Disposable {
|
|||
});
|
||||
|
||||
this.applyProjectionChanges();
|
||||
return this.projection.value;
|
||||
}
|
||||
|
||||
private applyProjectionChanges() {
|
||||
|
|
|
@ -42,7 +42,7 @@ export interface OwnedCollectionTestItem {
|
|||
export class SingleUseTestCollection extends Disposable {
|
||||
private readonly debounceSendDiff = this._register(new RunOnceScheduler(() => this.flushDiff(), 200));
|
||||
private readonly diffOpEmitter = this._register(new Emitter<TestsDiff>());
|
||||
private _resolveHandler?: (item: TestItemRaw) => Promise<void> | void;
|
||||
private _resolveHandler?: (item: TestItemRaw | undefined) => Promise<void> | void;
|
||||
|
||||
public readonly root = new TestItemRootImpl(this.controllerId, this.controllerId);
|
||||
public readonly tree = new Map</* full test id */string, OwnedCollectionTestItem>();
|
||||
|
@ -50,13 +50,14 @@ export class SingleUseTestCollection extends Disposable {
|
|||
|
||||
constructor(private readonly controllerId: string) {
|
||||
super();
|
||||
this.root.canResolveChildren = true;
|
||||
this.upsertItem(this.root, undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler used for expanding test items.
|
||||
*/
|
||||
public set resolveHandler(handler: undefined | ((item: TestItemRaw) => void)) {
|
||||
public set resolveHandler(handler: undefined | ((item: TestItemRaw | undefined) => void)) {
|
||||
this._resolveHandler = handler;
|
||||
for (const test of this.tree.values()) {
|
||||
this.updateExpandability(test);
|
||||
|
@ -201,13 +202,11 @@ export class SingleUseTestCollection extends Disposable {
|
|||
let internal = this.tree.get(fullId.toString());
|
||||
// Case 1: a brand new item
|
||||
if (!internal) {
|
||||
// always expand root node to know if there are tests (and whether to show the welcome view)
|
||||
const pExpandLvls = parent ? parent.expandLevels : 1;
|
||||
internal = {
|
||||
fullId,
|
||||
actual,
|
||||
parent: parent ? fullId.parentId : null,
|
||||
expandLevels: pExpandLvls /* intentionally undefined or 0 */ ? pExpandLvls - 1 : undefined,
|
||||
expandLevels: parent?.expandLevels /* intentionally undefined or 0 */ ? parent.expandLevels - 1 : undefined,
|
||||
expand: TestItemExpandState.NotExpandable, // updated by `connectItemAndChildren`
|
||||
};
|
||||
|
||||
|
@ -351,7 +350,7 @@ export class SingleUseTestCollection extends Disposable {
|
|||
|
||||
let r: Thenable<void> | void;
|
||||
try {
|
||||
r = this._resolveHandler(internal.actual);
|
||||
r = this._resolveHandler(internal.actual === this.root ? undefined : internal.actual);
|
||||
} catch (err) {
|
||||
internal.actual.error = err.stack || err.message;
|
||||
}
|
||||
|
|
|
@ -76,9 +76,6 @@ export const getCollectionItemParents = function* (collection: IMainThreadTestCo
|
|||
}
|
||||
};
|
||||
|
||||
const expandFirstLevel = (collection: IMainThreadTestCollection) =>
|
||||
Promise.all([...collection.rootItems].map(r => collection.expand(r.item.extId, 0)));
|
||||
|
||||
export const testCollectionIsEmpty = (collection: IMainThreadTestCollection) =>
|
||||
!Iterable.some(collection.rootItems, r => r.children.size > 0);
|
||||
|
||||
|
@ -103,7 +100,11 @@ export const expandAndGetTestById = async (collection: IMainThreadTestCollection
|
|||
return existing;
|
||||
}
|
||||
|
||||
await collection.expand(id, 0);
|
||||
// expand children only if it looks like it's necessary
|
||||
if (!existing.children.has(idPath[i + 1])) {
|
||||
await collection.expand(id, 0);
|
||||
}
|
||||
|
||||
expandToLevel = i + 1; // avoid an infinite loop if the test does not exist
|
||||
i = idPath.length - 1;
|
||||
}
|
||||
|
@ -131,10 +132,7 @@ export const getAllTestsInHierarchy = async (collection: IMainThreadTestCollecti
|
|||
* Iterator that expands to and iterates through tests in the file. Iterates
|
||||
* in strictly descending order.
|
||||
*/
|
||||
export const testsInFile = async function* (collection: IMainThreadTestCollection, uri: URI) {
|
||||
// Expand all direct children since roots will not have URIs, but children should.
|
||||
await expandFirstLevel(collection);
|
||||
|
||||
export const testsInFile = async function* (collection: IMainThreadTestCollection, uri: URI): AsyncIterable<IncrementalTestCollectionItem> {
|
||||
const demandUriStr = uri.toString();
|
||||
for (const test of collection.all) {
|
||||
if (!test.item.uri) {
|
||||
|
|
|
@ -26,13 +26,12 @@ export const testStubs = {
|
|||
nested: (idPrefix = 'id-') => {
|
||||
const collection = new TestSingleUseCollection('ctrlId');
|
||||
collection.root.label = 'root';
|
||||
collection.root.canResolveChildren = true;
|
||||
collection.resolveHandler = item => {
|
||||
if (item === collection.root) {
|
||||
if (item === undefined) {
|
||||
const a = new TestItemImpl(idPrefix + 'a', 'a', URI.file('/'));
|
||||
a.canResolveChildren = true;
|
||||
const b = new TestItemImpl(idPrefix + 'b', 'b', URI.file('/'));
|
||||
item.children.set([a, b]);
|
||||
collection.root.children.set([a, b]);
|
||||
} else if (item.id === idPrefix + 'a') {
|
||||
item.children.set([
|
||||
new TestItemImpl(idPrefix + 'aa', 'aa', URI.file('/')),
|
||||
|
|
|
@ -83,19 +83,7 @@ suite('ExtHost Testing', () => {
|
|||
],
|
||||
[
|
||||
TestDiffOpType.Add,
|
||||
{ controllerId: 'ctrlId', parent: single.root.id, expand: TestItemExpandState.Expandable, item: { ...convert.TestItem.from(a, 'ctrlId') } }
|
||||
],
|
||||
[
|
||||
TestDiffOpType.Add,
|
||||
{ controllerId: 'ctrlId', parent: single.root.id, expand: TestItemExpandState.NotExpandable, item: convert.TestItem.from(b, 'ctrlId') }
|
||||
],
|
||||
[
|
||||
TestDiffOpType.Update,
|
||||
{ extId: single.root.id, expand: TestItemExpandState.Expanded }
|
||||
],
|
||||
[
|
||||
TestDiffOpType.Update,
|
||||
{ extId: new TestId(['ctrlId', 'id-a']).toString(), expand: TestItemExpandState.BusyExpanding }
|
||||
{ controllerId: 'ctrlId', parent: single.root.id, expand: TestItemExpandState.BusyExpanding, item: { ...convert.TestItem.from(a, 'ctrlId') } }
|
||||
],
|
||||
[
|
||||
TestDiffOpType.Add,
|
||||
|
@ -109,6 +97,14 @@ suite('ExtHost Testing', () => {
|
|||
TestDiffOpType.Update,
|
||||
{ extId: new TestId(['ctrlId', 'id-a']).toString(), expand: TestItemExpandState.Expanded }
|
||||
],
|
||||
[
|
||||
TestDiffOpType.Add,
|
||||
{ controllerId: 'ctrlId', parent: single.root.id, expand: TestItemExpandState.NotExpandable, item: convert.TestItem.from(b, 'ctrlId') }
|
||||
],
|
||||
[
|
||||
TestDiffOpType.Update,
|
||||
{ extId: single.root.id, expand: TestItemExpandState.Expanded }
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -248,7 +244,8 @@ suite('ExtHost Testing', () => {
|
|||
assert.deepStrictEqual(newA.parent, undefined);
|
||||
});
|
||||
|
||||
test('moves an item to be a new child', () => {
|
||||
test('moves an item to be a new child', async () => {
|
||||
await single.expand(single.root.id, 0);
|
||||
single.collectDiff();
|
||||
const b = single.root.children.get('id-b')!;
|
||||
const a = single.root.children.get('id-a')!;
|
||||
|
@ -424,13 +421,16 @@ suite('ExtHost Testing', () => {
|
|||
|
||||
let dto: TestRunDto;
|
||||
|
||||
setup(() => {
|
||||
setup(async () => {
|
||||
proxy = mockObject();
|
||||
cts = new CancellationTokenSource();
|
||||
c = new TestRunCoordinator(proxy);
|
||||
|
||||
configuration = new TestRunProfileImpl(mockObject<MainThreadTestingShape, {}>(), 'ctrlId', 42, 'Do Run', TestRunProfileGroup.Run, () => { }, false);
|
||||
|
||||
await single.expand(single.root.id, Infinity);
|
||||
single.collectDiff();
|
||||
|
||||
req = {
|
||||
include: undefined,
|
||||
exclude: [single.root.children.get('id-b')!],
|
||||
|
@ -503,7 +503,6 @@ suite('ExtHost Testing', () => {
|
|||
const tracker = Iterable.first(c.trackers)!;
|
||||
const expectedArgs: unknown[][] = [];
|
||||
assert.deepStrictEqual(proxy.$addTestsToRun.args, expectedArgs);
|
||||
single.expand(single.root.id, Infinity);
|
||||
|
||||
task1.setState(single.root.children.get('id-a')!.children.get('id-aa')!, TestResultState.Passed);
|
||||
expectedArgs.push([
|
||||
|
@ -547,8 +546,6 @@ suite('ExtHost Testing', () => {
|
|||
});
|
||||
|
||||
test('excludes tests outside tree or explicitly excluded', () => {
|
||||
single.expand(single.root.id, Infinity);
|
||||
|
||||
const task = c.createTestRun('ctrlId', single, {
|
||||
profile: configuration,
|
||||
include: [single.root.children.get('id-a')!],
|
||||
|
|
Loading…
Reference in a new issue