diff --git a/src/utilities/timeline.test.ts b/src/utilities/timeline.test.ts index 2ef2f8d169..34ac812ecf 100644 --- a/src/utilities/timeline.test.ts +++ b/src/utilities/timeline.test.ts @@ -7,9 +7,10 @@ import { } from '../constants/view'; import type { ActivityDirective, ActivityType } from '../types/activity'; import type { ExternalEvent } from '../types/external-event'; +import type { DefaultEffectiveArgumentsMap } from '../types/parameter'; import type { Resource, ResourceType, Span, SpanUtilityMaps, SpansMap } from '../types/simulation'; import type { Tag } from '../types/tags'; -import type { ActivityLayerFilter, DiscreteTreeNode, TimeRange, Timeline, XRangeLayer } from '../types/timeline'; +import type { DiscreteTreeNode, TimeRange, Timeline, XRangeLayer } from '../types/timeline'; import { createSpanUtilityMaps } from './activities'; import { convertUTCToMs } from './time'; import { @@ -41,6 +42,126 @@ import { spanInView, } from './timeline'; +const testActivityTypes: ActivityType[] = [ + { + computed_attributes_value_schema: { + items: {}, + type: 'struct', + }, + name: 'child', + parameters: { + counter: { + order: 0, + schema: { + type: 'int', + }, + }, + }, + required_parameters: [], + subsystem_tag: null, + }, + { + computed_attributes_value_schema: { + items: {}, + type: 'struct', + }, + name: 'parent', + parameters: { + label: { + order: 0, + schema: { + type: 'string', + }, + }, + }, + required_parameters: [], + subsystem_tag: { + color: '#FFFFFF', + created_at: '2022-08-03T18:21:51', + id: 1, + name: 'subsystem1', + owner: 'user1', + }, + }, + { + computed_attributes_value_schema: { + items: { + biteSizeWasBig: { + type: 'boolean', + }, + newFlag: { + type: 'variant', + variants: [ + { + key: 'A', + label: 'A', + }, + { + key: 'B', + label: 'B', + }, + ], + }, + }, + type: 'struct', + }, + name: 'BiteBanana', + parameters: { + biteSize: { + order: 0, + schema: { + metadata: { + banannotation: { + value: 'Specifies the size of bite to take', + }, + unit: { + value: 'm', + }, + }, + type: 'real', + }, + }, + }, + required_parameters: [], + subsystem_tag: null, + }, + { + computed_attributes_value_schema: { + type: 'int', + }, + name: 'BakeBananaBread', + parameters: { + glutenFree: { + order: 2, + schema: { + type: 'boolean', + }, + }, + tbSugar: { + order: 1, + schema: { + type: 'int', + }, + }, + temperature: { + order: 0, + schema: { + type: 'real', + }, + }, + }, + required_parameters: ['tbSugar', 'glutenFree'], + subsystem_tag: null, + }, +]; + +const testDefaultArgumentsMap: DefaultEffectiveArgumentsMap = { + BakeBananaBread: { temperature: 350 }, + BiteBanana: { biteSize: 1 }, + child: { counter: 0 }, + parent: { label: 'unlabeled' }, +}; + const testSpans: Span[] = [ generateSpan({ duration: '03:00:00', @@ -50,7 +171,7 @@ const testSpans: Span[] = [ span_id: 2, startMs: 0, start_offset: '00:10:00', - type: 'Child', + type: 'child', }), generateSpan({ duration: '02:00:00', @@ -60,7 +181,7 @@ const testSpans: Span[] = [ span_id: 1, startMs: 0, start_offset: '00:00:00', - type: 'Parent', + type: 'parent', }), generateSpan({ duration: '04:00:00', @@ -70,7 +191,7 @@ const testSpans: Span[] = [ span_id: 3, startMs: 0, start_offset: '00:05:00', - type: 'Child', + type: 'child', }), generateSpan({ attributes: { @@ -342,7 +463,9 @@ test('getYAxisBounds', () => { populateTimelineLayers(timelines); const layer1 = timelines[0].rows[0].layers[1]; - layer1.filter.resource = { names: ['resourceWithValues', 'resourceWithNoValues'] }; + const layer2 = timelines[0].rows[0].layers[2]; + layer1.filter.resource = 'resourceWithValues'; + layer2.filter.resource = 'resourceWithNoValues'; const yAxis = timelines[0].rows[0].yAxes[0]; const layers = timelines[0].rows[0].layers; const resourceWithValues: Resource = { @@ -425,11 +548,10 @@ test('filterResourcesByLayer', () => { expect(filterResourcesByLayer(layer, [resourceA, resourceB])).to.deep.equal([]); const layer2 = createTimelineLineLayer([], []); - layer2.filter.resource = { names: [] }; expect(filterResourcesByLayer(layer2, [])).to.deep.equal([]); const layer3 = createTimelineLineLayer([], []); - layer3.filter.resource = { names: ['resourceA'] }; + layer3.filter.resource = 'resourceA'; expect(filterResourcesByLayer(layer3, [resourceA, resourceB])).to.deep.equal([resourceA]); }); @@ -577,7 +699,7 @@ test('generateDiscreteTree', () => { testDirectives, testSpans, testExternalEvents, - { BiteBanana: true, BiteBanana_1: true, Parent: true, Parent_1: true, Parent_1_Child: true }, + { BiteBanana: true, BiteBanana_1: true, parent: true, parent_1: true, parent_1_child: true }, 'flat', 'event_type_name', false, @@ -778,7 +900,7 @@ test('generateDiscreteTree', () => { activity_type: 'aggregation', children: [], expanded: false, - id: 'Child', + id: 'child', isLeaf: false, items: [ { @@ -795,7 +917,7 @@ test('generateDiscreteTree', () => { span_id: 2, startMs: 0, start_offset: '00:10:00', - type: 'Child', + type: 'child', }, }, { @@ -812,11 +934,11 @@ test('generateDiscreteTree', () => { span_id: 3, startMs: 0, start_offset: '00:05:00', - type: 'Child', + type: 'child', }, }, ], - label: 'Child', + label: 'child', type: 'Activity', }, { @@ -832,7 +954,7 @@ test('generateDiscreteTree', () => { activity_type: 'span', children: [], expanded: false, - id: 'Parent_1_Child_2', + id: 'parent_1_child_2', isLeaf: true, items: [ { @@ -849,18 +971,18 @@ test('generateDiscreteTree', () => { span_id: 2, startMs: 0, start_offset: '00:10:00', - type: 'Child', + type: 'child', }, }, ], - label: 'Child', + label: 'child', type: 'Activity', }, { activity_type: 'span', children: [], expanded: false, - id: 'Parent_1_Child_3', + id: 'parent_1_child_3', isLeaf: true, items: [ { @@ -877,16 +999,16 @@ test('generateDiscreteTree', () => { span_id: 3, startMs: 0, start_offset: '00:05:00', - type: 'Child', + type: 'child', }, }, ], - label: 'Child', + label: 'child', type: 'Activity', }, ], expanded: true, - id: 'Parent_1_Child', + id: 'parent_1_child', isLeaf: false, items: [ { @@ -903,7 +1025,7 @@ test('generateDiscreteTree', () => { span_id: 2, startMs: 0, start_offset: '00:10:00', - type: 'Child', + type: 'child', }, }, { @@ -920,16 +1042,16 @@ test('generateDiscreteTree', () => { span_id: 3, startMs: 0, start_offset: '00:05:00', - type: 'Child', + type: 'child', }, }, ], - label: 'Child', + label: 'child', type: 'Activity', }, ], expanded: true, - id: 'Parent_1', + id: 'parent_1', isLeaf: false, items: [ { @@ -946,16 +1068,16 @@ test('generateDiscreteTree', () => { span_id: 1, startMs: 0, start_offset: '00:00:00', - type: 'Parent', + type: 'parent', }, }, ], - label: 'Parent', + label: 'parent', type: 'Activity', }, ], expanded: true, - id: 'Parent', + id: 'parent', isLeaf: false, items: [ { @@ -972,11 +1094,11 @@ test('generateDiscreteTree', () => { span_id: 1, startMs: 0, start_offset: '00:00:00', - type: 'Parent', + type: 'parent', }, }, ], - label: 'Parent', + label: 'parent', type: 'Activity', }, ], @@ -1070,7 +1192,7 @@ test('generateDiscreteTree', () => { span_id: 2, startMs: 0, start_offset: '00:10:00', - type: 'Child', + type: 'child', }, }, { @@ -1087,7 +1209,7 @@ test('generateDiscreteTree', () => { span_id: 3, startMs: 0, start_offset: '00:05:00', - type: 'Child', + type: 'child', }, }, { @@ -1104,7 +1226,7 @@ test('generateDiscreteTree', () => { span_id: 1, startMs: 0, start_offset: '00:00:00', - type: 'Parent', + type: 'parent', }, }, ], @@ -1266,52 +1388,225 @@ describe('getTimeRangeAroundTime', () => { }); }); -test('applyActivityLayerFilter', () => { - const activityTypes: ActivityType[] = [ - { - computed_attributes_value_schema: { items: {}, type: 'struct' }, - name: 'foo', - parameters: {}, - required_parameters: [], - }, - { - computed_attributes_value_schema: { items: {}, type: 'struct' }, - name: 'bar', - parameters: {}, - required_parameters: [], - subsystem_tag: { - color: '#FFFFFF', - created_at: '2022-08-03T18:21:51', - id: 1, - name: 'subsystem 1', - owner: 'frog', - }, - }, - ]; +describe('applyActivityLayerFilter', () => { const tags: Tag[] = [generateTag({ id: 1 }), generateTag({ id: 2 })]; - const filter: ActivityLayerFilter = { - dynamic_type_filters: [ - { field: 'Type', operator: 'includes', value: 'oo' }, - // { field: 'Type', operator: 'includes', value: 't' }, - // { field: 'Type', operator: 'does not equal', value: 'bat' }, - // { field: 'Subsystem', operator: 'does not include', value: [1] }, - ], - // global_filters: [{ field: 'Tag', operator: 'includes', value: [1] }], - static_types: [], - type_subfilters: { - bat: [{ field: 'Tag', operator: 'includes', value: [1] }], - }, - }; const directives: ActivityDirective[] = [ - generateDirective({ id: 1, type: 'foo' }), - generateDirective({ id: 2, type: 'foo' }), - generateDirective({ id: 3, type: 'bar' }), - generateDirective({ id: 4, tags: [{ tag: generateTag({ id: 1 }) }], type: 'bat' }), - generateDirective({ id: 5, tags: [{ tag: generateTag({ id: 2 }) }], type: 'bat' }), - generateDirective({ id: 6, tags: [{ tag: generateTag({ id: 1 }) }], type: 'bop' }), + generateActivityDirective({ id: 1, name: 'Foo', source_scheduling_goal_id: 1, type: 'parent' }), + generateActivityDirective({ + arguments: { newFlag: 'A' }, + id: 2, + source_scheduling_goal_id: 2, + tags: [{ tag: tags[1] }], + type: 'BiteBanana', + }), + generateActivityDirective({ id: 3, tags: tags.map(tag => ({ tag })), type: 'PeelBanana' }), + generateActivityDirective({ arguments: { newFlag: 'B' }, id: 4, type: 'BiteBanana' }), + ]; + const spans: Span[] = [ + generateSpan({ parent_id: 1, span_id: 2, type: 'child' }), + generateSpan({ parent_id: null, span_id: 1, type: 'parent' }), + generateSpan({ parent_id: 1, span_id: 3, type: 'child' }), + generateSpan({ + attributes: { arguments: { newFlag: 'A' }, computedAttributes: {} }, + parent_id: null, + span_id: 4, + type: 'BiteBanana', + }), + generateSpan({ parent_id: null, span_id: 5, type: 'PeelBanana' }), + generateSpan({ + attributes: { arguments: { newFlag: 'B' }, computedAttributes: {} }, + parent_id: null, + span_id: 6, + type: 'BiteBanana', + }), ]; - expect(applyActivityLayerFilter(filter, directives, [], activityTypes).directives.map(d => d.id)).to.deep.eq([ - 1, 2, 3, 4, - ]); + test('Should return all directives and spans if no filters applied', () => { + expect( + applyActivityLayerFilter(undefined, testDirectives, testSpans, testActivityTypes, testDefaultArgumentsMap), + ).to.deep.eq({ + directives: testDirectives, + spans: testSpans, + }); + expect( + applyActivityLayerFilter({}, testDirectives, testSpans, testActivityTypes, testDefaultArgumentsMap), + ).to.deep.eq({ + directives: testDirectives, + spans: testSpans, + }); + }); + + test('Should apply static type filters', () => { + expect( + applyActivityLayerFilter( + { static_types: ['parent', 'BiteBanana'] }, + directives, + spans, + testActivityTypes, + testDefaultArgumentsMap, + ), + ).to.deep.eq({ + directives: [directives[0], directives[1], directives[3]], + spans: [spans[1], spans[3], spans[5]], + }); + }); + + test('Should apply dynamic type filters', () => { + expect( + applyActivityLayerFilter( + { + dynamic_type_filters: [ + { + field: 'Type', + id: 1, + operator: 'includes', + value: 'banana', + }, + ], + }, + directives, + spans, + testActivityTypes, + testDefaultArgumentsMap, + ), + ).to.deep.eq({ + directives: [directives[1], directives[2], directives[3]], + spans: [spans[3], spans[4], spans[5]], + }); + + expect( + applyActivityLayerFilter( + { + dynamic_type_filters: [ + { + field: 'Type', + id: 1, + operator: 'equals', + value: 'BiteBanana', + }, + ], + }, + directives, + spans, + testActivityTypes, + testDefaultArgumentsMap, + ), + ).to.deep.eq({ + directives: [directives[1], directives[3]], + spans: [spans[3], spans[5]], + }); + + expect( + applyActivityLayerFilter( + { + dynamic_type_filters: [ + { + field: 'Subsystem', + id: 1, + operator: 'equals', + value: 1, + }, + ], + }, + directives, + spans, + testActivityTypes, + testDefaultArgumentsMap, + ), + ).to.deep.eq({ + directives: [directives[0]], + spans: [spans[1]], + }); + }); + + test('Should apply global filters', () => { + expect( + applyActivityLayerFilter( + { + global_filters: [ + { + field: 'Name', + id: 1, + operator: 'includes', + value: 'oo', + }, + ], + }, + directives, + spans, + testActivityTypes, + testDefaultArgumentsMap, + ), + ).to.deep.eq({ + directives: [directives[0]], + spans: [], + }); + + expect( + applyActivityLayerFilter( + { + global_filters: [ + { + field: 'Parameter', + id: 1, + operator: 'equals', + subfield: { name: 'newFlag', type: 'variant' }, + value: 'A', + }, + ], + }, + directives, + spans, + testActivityTypes, + testDefaultArgumentsMap, + ), + ).to.deep.eq({ + directives: [directives[1]], + spans: [spans[3]], + }); + + expect( + applyActivityLayerFilter( + { + global_filters: [ + { + field: 'SchedulingGoalId', + id: 1, + operator: 'equals', + value: 1, + }, + ], + }, + directives, + spans, + testActivityTypes, + testDefaultArgumentsMap, + ), + ).to.deep.eq({ + directives: [directives[0]], + spans: [], + }); + + expect( + applyActivityLayerFilter( + { + global_filters: [ + { + field: 'Tag', + id: 1, + operator: 'includes', + value: [1], + }, + ], + }, + directives, + spans, + testActivityTypes, + testDefaultArgumentsMap, + ), + ).to.deep.eq({ + directives: [directives[2]], + spans: [], + }); + }); });