export const description = ` Tests for queries/filtering, loading, and running. `; import { Fixture } from '../common/framework/fixture.js'; import { makeTestGroup } from '../common/framework/test_group.js'; import { TestFileLoader, SpecFile } from '../common/internal/file_loader.js'; import { Logger } from '../common/internal/logging/logger.js'; import { Status } from '../common/internal/logging/result.js'; import { parseQuery } from '../common/internal/query/parseQuery.js'; import { TestQuery, TestQuerySingleCase, TestQueryMultiCase, TestQueryMultiTest, TestQueryMultiFile, TestQueryWithExpectation, } from '../common/internal/query/query.js'; import { makeTestGroupForUnitTesting } from '../common/internal/test_group.js'; import { TestSuiteListing, TestSuiteListingEntry } from '../common/internal/test_suite_listing.js'; import { ExpandThroughLevel, TestTreeLeaf } from '../common/internal/tree.js'; import { assert, objectEquals } from '../common/util/util.js'; import { UnitTest } from './unit_test.js'; const listingData: { [k: string]: TestSuiteListingEntry[] } = { suite1: [ { file: [], readme: 'desc 1a' }, { file: ['foo'] }, { file: ['bar'], readme: 'desc 1h' }, { file: ['bar', 'biz'] }, { file: ['bar', 'buzz', 'buzz'] }, { file: ['baz'] }, { file: ['empty'], readme: 'desc 1z' }, // directory with no files ], suite2: [{ file: [], readme: 'desc 2a' }, { file: ['foof'] }], }; const specsData: { [k: string]: SpecFile } = { 'suite1/foo.spec.js': { description: 'desc 1b', g: (() => { const g = makeTestGroupForUnitTesting(UnitTest); g.test('hello').fn(() => {}); g.test('bonjour').fn(() => {}); g.test('hola') .desc('TODO TODO') .fn(() => {}); return g; })(), }, 'suite1/bar/biz.spec.js': { description: 'desc 1f TODO TODO', g: makeTestGroupForUnitTesting(UnitTest), // file with no tests }, 'suite1/bar/buzz/buzz.spec.js': { description: 'desc 1d TODO', g: (() => { const g = makeTestGroupForUnitTesting(UnitTest); g.test('zap').fn(() => {}); return g; })(), }, 'suite1/baz.spec.js': { description: 'desc 1e', g: (() => { const g = makeTestGroupForUnitTesting(UnitTest); g.test('wye') .paramsSimple([{}, { x: 1 }]) .fn(() => {}); g.test('zed') .paramsSimple([ { a: 1, b: 2, _c: 0 }, { b: 3, a: 1, _c: 0 }, ]) .fn(() => {}); return g; })(), }, 'suite2/foof.spec.js': { description: 'desc 2b', g: (() => { const g = makeTestGroupForUnitTesting(UnitTest); g.test('blah').fn(t => { t.debug('OK'); }); g.test('bleh') .paramsSimple([{ a: 1 }]) .fn(t => { t.debug('OK'); t.debug('OK'); }); g.test('bluh,a').fn(t => { t.fail('goodbye'); }); return g; })(), }, }; class FakeTestFileLoader extends TestFileLoader { async listing(suite: string): Promise { return listingData[suite]; } async import(path: string): Promise { assert(path in specsData, '[test] mock file ' + path + ' does not exist'); return specsData[path]; } } class LoadingTest extends UnitTest { loader: FakeTestFileLoader = new FakeTestFileLoader(); events: (string | null)[] = []; private isListenersAdded = false; collectEvents(): void { this.events = []; if (!this.isListenersAdded) { this.isListenersAdded = true; this.loader.addEventListener('import', ev => this.events.push(ev.data.url)); this.loader.addEventListener('finish', ev => this.events.push(null)); } } async load(query: string): Promise { return Array.from(await this.loader.loadCases(parseQuery(query))); } async loadNames(query: string): Promise { return (await this.load(query)).map(c => c.query.toString()); } } export const g = makeTestGroup(LoadingTest); g.test('suite').fn(async t => { t.shouldReject('Error', t.load('suite1')); t.shouldReject('Error', t.load('suite1:')); }); g.test('group').fn(async t => { t.collectEvents(); t.expect((await t.load('suite1:*')).length === 8); t.expect( objectEquals(t.events, [ 'suite1/foo.spec.js', 'suite1/bar/biz.spec.js', 'suite1/bar/buzz/buzz.spec.js', 'suite1/baz.spec.js', null, ]) ); t.collectEvents(); t.expect((await t.load('suite1:foo,*')).length === 3); // x:foo,* matches x:foo: t.expect(objectEquals(t.events, ['suite1/foo.spec.js', null])); t.collectEvents(); t.expect((await t.load('suite1:bar,*')).length === 1); t.expect( objectEquals(t.events, ['suite1/bar/biz.spec.js', 'suite1/bar/buzz/buzz.spec.js', null]) ); t.collectEvents(); t.expect((await t.load('suite1:bar,buzz,buzz,*')).length === 1); t.expect(objectEquals(t.events, ['suite1/bar/buzz/buzz.spec.js', null])); t.shouldReject('Error', t.load('suite1:f*')); { const s = new TestQueryMultiFile('suite1', ['bar', 'buzz']).toString(); t.collectEvents(); t.expect((await t.load(s)).length === 1); t.expect(objectEquals(t.events, ['suite1/bar/buzz/buzz.spec.js', null])); } }); g.test('test').fn(async t => { t.shouldReject('Error', t.load('suite1::')); t.shouldReject('Error', t.load('suite1:bar:')); t.shouldReject('Error', t.load('suite1:bar,:')); t.shouldReject('Error', t.load('suite1::*')); t.shouldReject('Error', t.load('suite1:bar,:*')); t.shouldReject('Error', t.load('suite1:bar:*')); t.expect((await t.load('suite1:foo:*')).length === 3); t.expect((await t.load('suite1:bar,buzz,buzz:*')).length === 1); t.expect((await t.load('suite1:baz:*')).length === 4); t.expect((await t.load('suite2:foof:bluh,*')).length === 1); t.expect((await t.load('suite2:foof:bluh,a,*')).length === 1); { const s = new TestQueryMultiTest('suite2', ['foof'], ['bluh']).toString(); t.expect((await t.load(s)).length === 1); } }); g.test('case').fn(async t => { t.shouldReject('Error', t.load('suite1:foo::')); t.shouldReject('Error', t.load('suite1:bar:zed,:')); t.shouldReject('Error', t.load('suite1:foo:h*')); t.shouldReject('Error', t.load('suite1:foo::*')); t.shouldReject('Error', t.load('suite1:baz::*')); t.shouldReject('Error', t.load('suite1:baz:zed,:*')); t.shouldReject('Error', t.load('suite1:baz:zed:')); t.shouldReject('Error', t.load('suite1:baz:zed:a=1;b=2*')); t.shouldReject('Error', t.load('suite1:baz:zed:a=1;b=2;')); t.shouldReject('SyntaxError', t.load('suite1:baz:zed:a=1;b=2,')); // tries to parse '2,' as JSON t.shouldReject('Error', t.load('suite1:baz:zed:a=1,b=2')); // '=' not allowed in value '1,b=2' t.shouldReject('Error', t.load('suite1:baz:zed:b=2*')); t.shouldReject('Error', t.load('suite1:baz:zed:b=2;a=1;_c=0')); t.shouldReject('Error', t.load('suite1:baz:zed:a=1,*')); t.expect((await t.load('suite1:baz:zed:*')).length === 2); t.expect((await t.load('suite1:baz:zed:a=1;*')).length === 2); t.expect((await t.load('suite1:baz:zed:a=1;b=2')).length === 1); t.expect((await t.load('suite1:baz:zed:a=1;b=2;*')).length === 1); t.expect((await t.load('suite1:baz:zed:b=2;*')).length === 1); t.expect((await t.load('suite1:baz:zed:b=2;a=1')).length === 1); t.expect((await t.load('suite1:baz:zed:b=2;a=1;*')).length === 1); t.expect((await t.load('suite1:baz:zed:b=3;a=1')).length === 1); t.expect((await t.load('suite1:baz:zed:a=1;b=3')).length === 1); t.expect((await t.load('suite1:foo:hello:')).length === 1); { const s = new TestQueryMultiCase('suite1', ['baz'], ['zed'], { a: 1, b: 2 }).toString(); t.expect((await t.load(s)).length === 1); } { const s = new TestQuerySingleCase('suite1', ['baz'], ['zed'], { a: 1, b: 2 }).toString(); t.expect((await t.load(s)).length === 1); } }); async function runTestcase( t: Fixture, log: Logger, testcases: TestTreeLeaf[], i: number, query: TestQuery, expectations: TestQueryWithExpectation[], status: Status, logs: (s: string[]) => boolean ) { t.expect(objectEquals(testcases[i].query, query)); const name = testcases[i].query.toString(); const [rec, res] = log.record(name); await testcases[i].run(rec, expectations); t.expect(log.results.get(name) === res); t.expect(res.status === status); t.expect(res.timems >= 0); assert(res.logs !== undefined); // only undefined while pending t.expect(logs(res.logs.map(l => JSON.stringify(l)))); } g.test('end2end').fn(async t => { const l = await t.load('suite2:foof:*'); assert(l.length === 3, 'listing length'); const log = new Logger({ overrideDebugMode: true }); await runTestcase( t, log, l, 0, new TestQuerySingleCase('suite2', ['foof'], ['blah'], {}), [], 'pass', logs => objectEquals(logs, ['"DEBUG: OK"']) ); await runTestcase( t, log, l, 1, new TestQuerySingleCase('suite2', ['foof'], ['bleh'], { a: 1 }), [], 'pass', logs => objectEquals(logs, ['"DEBUG: OK"', '"DEBUG: OK"']) ); await runTestcase( t, log, l, 2, new TestQuerySingleCase('suite2', ['foof'], ['bluh', 'a'], {}), [], 'fail', logs => logs.length === 1 && logs[0].startsWith('"EXPECTATION FAILED: goodbye\\n') && logs[0].indexOf('loaders_and_trees.spec.') !== -1 ); }); g.test('expectations,single_case').fn(async t => { const log = new Logger({ overrideDebugMode: true }); const zedCases = await t.load('suite1:baz:zed:*'); // Single-case. Covers one case. const zedExpectationsSkipA1B2 = [ { query: new TestQuerySingleCase('suite1', ['baz'], ['zed'], { a: 1, b: 2 }), expectation: 'skip' as const, }, ]; await runTestcase( t, log, zedCases, 0, new TestQuerySingleCase('suite1', ['baz'], ['zed'], { a: 1, b: 2 }), zedExpectationsSkipA1B2, 'skip', logs => logs.length === 1 && logs[0].startsWith('"SKIP: Skipped by expectations"') ); await runTestcase( t, log, zedCases, 1, new TestQuerySingleCase('suite1', ['baz'], ['zed'], { a: 1, b: 3 }), zedExpectationsSkipA1B2, 'pass', logs => logs.length === 0 ); }); g.test('expectations,single_case,none').fn(async t => { const log = new Logger({ overrideDebugMode: true }); const zedCases = await t.load('suite1:baz:zed:*'); // Single-case. Doesn't cover any cases. const zedExpectationsSkipA1B0 = [ { query: new TestQuerySingleCase('suite1', ['baz'], ['zed'], { a: 1, b: 0 }), expectation: 'skip' as const, }, ]; await runTestcase( t, log, zedCases, 0, new TestQuerySingleCase('suite1', ['baz'], ['zed'], { a: 1, b: 2 }), zedExpectationsSkipA1B0, 'pass', logs => logs.length === 0 ); await runTestcase( t, log, zedCases, 1, new TestQuerySingleCase('suite1', ['baz'], ['zed'], { a: 1, b: 3 }), zedExpectationsSkipA1B0, 'pass', logs => logs.length === 0 ); }); g.test('expectations,multi_case').fn(async t => { const log = new Logger({ overrideDebugMode: true }); const zedCases = await t.load('suite1:baz:zed:*'); // Multi-case, not all cases covered. const zedExpectationsSkipB3 = [ { query: new TestQueryMultiCase('suite1', ['baz'], ['zed'], { b: 3 }), expectation: 'skip' as const, }, ]; await runTestcase( t, log, zedCases, 0, new TestQuerySingleCase('suite1', ['baz'], ['zed'], { a: 1, b: 2 }), zedExpectationsSkipB3, 'pass', logs => logs.length === 0 ); await runTestcase( t, log, zedCases, 1, new TestQuerySingleCase('suite1', ['baz'], ['zed'], { a: 1, b: 3 }), zedExpectationsSkipB3, 'skip', logs => logs.length === 1 && logs[0].startsWith('"SKIP: Skipped by expectations"') ); }); g.test('expectations,multi_case_all').fn(async t => { const log = new Logger({ overrideDebugMode: true }); const zedCases = await t.load('suite1:baz:zed:*'); // Multi-case, all cases covered. const zedExpectationsSkipA1 = [ { query: new TestQueryMultiCase('suite1', ['baz'], ['zed'], { a: 1 }), expectation: 'skip' as const, }, ]; await runTestcase( t, log, zedCases, 0, new TestQuerySingleCase('suite1', ['baz'], ['zed'], { a: 1, b: 2 }), zedExpectationsSkipA1, 'skip', logs => logs.length === 1 && logs[0].startsWith('"SKIP: Skipped by expectations"') ); await runTestcase( t, log, zedCases, 1, new TestQuerySingleCase('suite1', ['baz'], ['zed'], { a: 1, b: 3 }), zedExpectationsSkipA1, 'skip', logs => logs.length === 1 && logs[0].startsWith('"SKIP: Skipped by expectations"') ); }); g.test('expectations,multi_case_none').fn(async t => { const log = new Logger({ overrideDebugMode: true }); const zedCases = await t.load('suite1:baz:zed:*'); // Multi-case, no params, all cases covered. const zedExpectationsSkipZed = [ { query: new TestQueryMultiCase('suite1', ['baz'], ['zed'], {}), expectation: 'skip' as const, }, ]; await runTestcase( t, log, zedCases, 0, new TestQuerySingleCase('suite1', ['baz'], ['zed'], { a: 1, b: 2 }), zedExpectationsSkipZed, 'skip', logs => logs.length === 1 && logs[0].startsWith('"SKIP: Skipped by expectations"') ); await runTestcase( t, log, zedCases, 1, new TestQuerySingleCase('suite1', ['baz'], ['zed'], { a: 1, b: 3 }), zedExpectationsSkipZed, 'skip', logs => logs.length === 1 && logs[0].startsWith('"SKIP: Skipped by expectations"') ); }); g.test('expectations,multi_test').fn(async t => { const log = new Logger({ overrideDebugMode: true }); const suite1Cases = await t.load('suite1:*'); // Multi-test, all cases covered. const expectationsSkipAllInBaz = [ { query: new TestQueryMultiTest('suite1', ['baz'], []), expectation: 'skip' as const, }, ]; await runTestcase( t, log, suite1Cases, 4, new TestQuerySingleCase('suite1', ['baz'], ['wye'], {}), expectationsSkipAllInBaz, 'skip', logs => logs.length === 1 && logs[0].startsWith('"SKIP: Skipped by expectations"') ); await runTestcase( t, log, suite1Cases, 6, new TestQuerySingleCase('suite1', ['baz'], ['zed'], { a: 1, b: 2 }), expectationsSkipAllInBaz, 'skip', logs => logs.length === 1 && logs[0].startsWith('"SKIP: Skipped by expectations"') ); }); g.test('expectations,multi_test,none').fn(async t => { const log = new Logger({ overrideDebugMode: true }); const suite1Cases = await t.load('suite1:*'); // Multi-test, no cases covered. const expectationsSkipAllInFoo = [ { query: new TestQueryMultiTest('suite1', ['foo'], []), expectation: 'skip' as const, }, ]; await runTestcase( t, log, suite1Cases, 4, new TestQuerySingleCase('suite1', ['baz'], ['wye'], {}), expectationsSkipAllInFoo, 'pass', logs => logs.length === 0 ); await runTestcase( t, log, suite1Cases, 6, new TestQuerySingleCase('suite1', ['baz'], ['zed'], { a: 1, b: 2 }), expectationsSkipAllInFoo, 'pass', logs => logs.length === 0 ); }); g.test('expectations,multi_file').fn(async t => { const log = new Logger({ overrideDebugMode: true }); const suite1Cases = await t.load('suite1:*'); // Multi-file const expectationsSkipAll = [ { query: new TestQueryMultiFile('suite1', []), expectation: 'skip' as const, }, ]; await runTestcase( t, log, suite1Cases, 0, new TestQuerySingleCase('suite1', ['foo'], ['hello'], {}), expectationsSkipAll, 'skip', logs => logs.length === 1 && logs[0].startsWith('"SKIP: Skipped by expectations"') ); await runTestcase( t, log, suite1Cases, 3, new TestQuerySingleCase('suite1', ['bar', 'buzz', 'buzz'], ['zap'], {}), expectationsSkipAll, 'skip', logs => logs.length === 1 && logs[0].startsWith('"SKIP: Skipped by expectations"') ); }); g.test('expectations,catches_failure').fn(async t => { const log = new Logger({ overrideDebugMode: true }); const suite2Cases = await t.load('suite2:*'); // Catches failure const expectedFailures = [ { query: new TestQueryMultiCase('suite2', ['foof'], ['bluh', 'a'], {}), expectation: 'fail' as const, }, ]; await runTestcase( t, log, suite2Cases, 0, new TestQuerySingleCase('suite2', ['foof'], ['blah'], {}), expectedFailures, 'pass', logs => objectEquals(logs, ['"DEBUG: OK"']) ); // Status is passed, but failure is logged. await runTestcase( t, log, suite2Cases, 2, new TestQuerySingleCase('suite2', ['foof'], ['bluh', 'a'], {}), expectedFailures, 'pass', logs => logs.length === 1 && logs[0].startsWith('"EXPECTATION FAILED: goodbye\\n') ); }); g.test('expectations,skip_dominates_failure').fn(async t => { const log = new Logger({ overrideDebugMode: true }); const suite2Cases = await t.load('suite2:*'); const expectedFailures = [ { query: new TestQueryMultiCase('suite2', ['foof'], ['bluh', 'a'], {}), expectation: 'fail' as const, }, { query: new TestQueryMultiCase('suite2', ['foof'], ['bluh', 'a'], {}), expectation: 'skip' as const, }, ]; await runTestcase( t, log, suite2Cases, 2, new TestQuerySingleCase('suite2', ['foof'], ['bluh', 'a'], {}), expectedFailures, 'skip', logs => logs.length === 1 && logs[0].startsWith('"SKIP: Skipped by expectations"') ); }); g.test('expectations,skip_inside_failure').fn(async t => { const log = new Logger({ overrideDebugMode: true }); const suite2Cases = await t.load('suite2:*'); const expectedFailures = [ { query: new TestQueryMultiFile('suite2', []), expectation: 'fail' as const, }, { query: new TestQueryMultiCase('suite2', ['foof'], ['blah'], {}), expectation: 'skip' as const, }, ]; await runTestcase( t, log, suite2Cases, 0, new TestQuerySingleCase('suite2', ['foof'], ['blah'], {}), expectedFailures, 'skip', logs => logs.length === 1 && logs[0].startsWith('"SKIP: Skipped by expectations"') ); await runTestcase( t, log, suite2Cases, 2, new TestQuerySingleCase('suite2', ['foof'], ['bluh', 'a'], {}), expectedFailures, 'pass', logs => logs.length === 1 && logs[0].startsWith('"EXPECTATION FAILED: goodbye\\n') ); }); async function testIterateCollapsed( t: LoadingTest, alwaysExpandThroughLevel: ExpandThroughLevel, expectations: string[], expectedResult: 'throws' | string[] | [string, number | undefined][], includeEmptySubtrees = false ) { t.debug(`expandThrough=${alwaysExpandThroughLevel} expectations=${expectations}`); const treePromise = t.loader.loadTree(new TestQueryMultiFile('suite1', []), expectations); if (expectedResult === 'throws') { t.shouldReject('Error', treePromise, 'loadTree should have thrown Error'); return; } const tree = await treePromise; const actualIter = tree.iterateCollapsedNodes({ includeEmptySubtrees, alwaysExpandThroughLevel, }); const testingTODOs = expectedResult.length > 0 && expectedResult[0] instanceof Array; const actual = Array.from(actualIter, ({ query, subtreeCounts }) => testingTODOs ? [query.toString(), subtreeCounts?.nodesWithTODO] : query.toString() ); if (!objectEquals(actual, expectedResult)) { t.fail( `iterateCollapsed failed: got ${JSON.stringify(actual)} exp ${JSON.stringify(expectedResult)} ${tree.toString()}` ); } } g.test('print').fn(async t => { const tree = await t.loader.loadTree(new TestQueryMultiFile('suite1', [])); tree.toString(); }); g.test('iterateCollapsed').fn(async t => { await testIterateCollapsed( t, 1, [], [ ['suite1:foo:*', 1], // to-do propagated up from foo:hola ['suite1:bar,buzz,buzz:*', 1], // to-do in file description ['suite1:baz:*', 0], ] ); await testIterateCollapsed( t, 2, [], [ ['suite1:foo:hello:*', 0], ['suite1:foo:bonjour:*', 0], ['suite1:foo:hola:*', 1], // to-do in test description ['suite1:bar,buzz,buzz:zap:*', 0], ['suite1:baz:wye:*', 0], ['suite1:baz:zed:*', 0], ] ); await testIterateCollapsed( t, 3, [], [ ['suite1:foo:hello:', undefined], ['suite1:foo:bonjour:', undefined], ['suite1:foo:hola:', undefined], ['suite1:bar,buzz,buzz:zap:', undefined], ['suite1:baz:wye:', undefined], ['suite1:baz:wye:x=1', undefined], ['suite1:baz:zed:a=1;b=2', undefined], ['suite1:baz:zed:b=3;a=1', undefined], ] ); // Expectations lists that have no effect await testIterateCollapsed( t, 1, ['suite1:foo:*'], ['suite1:foo:*', 'suite1:bar,buzz,buzz:*', 'suite1:baz:*'] ); await testIterateCollapsed( t, 1, ['suite1:bar,buzz,buzz:*'], ['suite1:foo:*', 'suite1:bar,buzz,buzz:*', 'suite1:baz:*'] ); await testIterateCollapsed( t, 2, ['suite1:baz:wye:*'], [ 'suite1:foo:hello:*', 'suite1:foo:bonjour:*', 'suite1:foo:hola:*', 'suite1:bar,buzz,buzz:zap:*', 'suite1:baz:wye:*', 'suite1:baz:zed:*', ] ); // Test with includeEmptySubtrees=true await testIterateCollapsed( t, 1, [], [ 'suite1:foo:*', 'suite1:bar,biz:*', 'suite1:bar,buzz,buzz:*', 'suite1:baz:*', 'suite1:empty,*', ], true ); await testIterateCollapsed( t, 2, [], [ 'suite1:foo:hello:*', 'suite1:foo:bonjour:*', 'suite1:foo:hola:*', 'suite1:bar,biz:*', 'suite1:bar,buzz,buzz:zap:*', 'suite1:baz:wye:*', 'suite1:baz:zed:*', 'suite1:empty,*', ], true ); // Expectations lists that have some effect await testIterateCollapsed( t, 1, ['suite1:baz:wye:*'], ['suite1:foo:*', 'suite1:bar,buzz,buzz:*', 'suite1:baz:wye:*', 'suite1:baz:zed,*'] ); await testIterateCollapsed( t, 1, ['suite1:baz:zed:*'], ['suite1:foo:*', 'suite1:bar,buzz,buzz:*', 'suite1:baz:wye,*', 'suite1:baz:zed:*'] ); await testIterateCollapsed( t, 1, ['suite1:baz:wye:*', 'suite1:baz:zed:*'], ['suite1:foo:*', 'suite1:bar,buzz,buzz:*', 'suite1:baz:wye:*', 'suite1:baz:zed:*'] ); await testIterateCollapsed( t, 1, ['suite1:baz:wye:'], [ 'suite1:foo:*', 'suite1:bar,buzz,buzz:*', 'suite1:baz:wye:', 'suite1:baz:wye:x=1;*', 'suite1:baz:zed,*', ] ); await testIterateCollapsed( t, 1, ['suite1:baz:wye:x=1'], [ 'suite1:foo:*', 'suite1:bar,buzz,buzz:*', 'suite1:baz:wye:', 'suite1:baz:wye:x=1', 'suite1:baz:zed,*', ] ); await testIterateCollapsed( t, 1, ['suite1:foo:*', 'suite1:baz:wye:'], [ 'suite1:foo:*', 'suite1:bar,buzz,buzz:*', 'suite1:baz:wye:', 'suite1:baz:wye:x=1;*', 'suite1:baz:zed,*', ] ); await testIterateCollapsed( t, 2, ['suite1:baz:wye:'], [ 'suite1:foo:hello:*', 'suite1:foo:bonjour:*', 'suite1:foo:hola:*', 'suite1:bar,buzz,buzz:zap:*', 'suite1:baz:wye:', 'suite1:baz:wye:x=1;*', 'suite1:baz:zed:*', ] ); await testIterateCollapsed( t, 2, ['suite1:baz:wye:x=1'], [ 'suite1:foo:hello:*', 'suite1:foo:bonjour:*', 'suite1:foo:hola:*', 'suite1:bar,buzz,buzz:zap:*', 'suite1:baz:wye:', 'suite1:baz:wye:x=1', 'suite1:baz:zed:*', ] ); await testIterateCollapsed( t, 2, ['suite1:foo:hello:*', 'suite1:baz:wye:'], [ 'suite1:foo:hello:*', 'suite1:foo:bonjour:*', 'suite1:foo:hola:*', 'suite1:bar,buzz,buzz:zap:*', 'suite1:baz:wye:', 'suite1:baz:wye:x=1;*', 'suite1:baz:zed:*', ] ); // Invalid expectation queries await testIterateCollapsed(t, 1, ['*'], 'throws'); await testIterateCollapsed(t, 1, ['garbage'], 'throws'); await testIterateCollapsed(t, 1, ['garbage*'], 'throws'); await testIterateCollapsed(t, 1, ['suite1*'], 'throws'); await testIterateCollapsed(t, 1, ['suite1:foo*'], 'throws'); await testIterateCollapsed(t, 1, ['suite1:foo:he*'], 'throws'); // Valid expectation queries but they don't match anything await testIterateCollapsed(t, 1, ['garbage:*'], 'throws'); await testIterateCollapsed(t, 1, ['suite1:doesntexist:*'], 'throws'); await testIterateCollapsed(t, 1, ['suite2:foo:*'], 'throws'); // Can't expand subqueries bigger than one file. await testIterateCollapsed(t, 1, ['suite1:*'], 'throws'); await testIterateCollapsed(t, 1, ['suite1:bar,*'], 'throws'); await testIterateCollapsed(t, 1, ['suite1:*'], 'throws'); await testIterateCollapsed(t, 1, ['suite1:bar:hello,*'], 'throws'); await testIterateCollapsed(t, 1, ['suite1:baz,*'], 'throws'); });