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
2 changes: 1 addition & 1 deletion projects/components/src/link/link.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
.ht-link {
text-decoration-line: none;
text-decoration: none;
color: $gray-9;
color: inherit;
@include link-hover;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NavigationService } from '@hypertrace/common';
import { NavigationParamsType, NavigationService } from '@hypertrace/common';
import { createModelFactory } from '@hypertrace/dashboards/testing';
import { mockProvider } from '@ngneat/spectator/jest';
import { Span, spanIdKey } from '../../../../graphql/model/schema/span';
Expand All @@ -12,7 +12,7 @@ describe('Span Trace Navigation Handler Model', () => {
const buildModel = createModelFactory({
providers: [
mockProvider(NavigationService, {
navigateWithinApp: jest.fn()
navigate: jest.fn()
})
]
});
Expand All @@ -23,11 +23,14 @@ describe('Span Trace Navigation Handler Model', () => {

spectator.model.execute(span);

expect(navService.navigateWithinApp).not.toHaveBeenCalled();
expect(navService.navigate).not.toHaveBeenCalled();

span.traceId = 'test-trace-id';
spectator.model.execute(span);

expect(navService.navigateWithinApp).toHaveBeenLastCalledWith(['trace', 'test-trace-id', { spanId: 'test-id' }]);
expect(navService.navigate).toHaveBeenLastCalledWith({
navType: NavigationParamsType.InApp,
path: ['/trace', 'test-trace-id', { spanId: 'test-id' }]
});
});
});
Original file line number Diff line number Diff line change
@@ -1,56 +1,87 @@
import { NavigationService } from '@hypertrace/common';
import { NavigationParamsType, NavigationService } from '@hypertrace/common';
import { createServiceFactory, mockProvider, SpectatorService } from '@ngneat/spectator/jest';
import { TracingNavigationService } from './tracing-navigation.service';

describe('Tracing Navigation Service', () => {
let spectator: SpectatorService<TracingNavigationService>;

const buildService = createServiceFactory({
service: TracingNavigationService
service: TracingNavigationService,
providers: [
mockProvider(NavigationService, {
navigate: jest.fn(),
isRelativePathActive: () => true
})
]
});

test('can navigate correctly to trace detail', () => {
spectator = buildService({
providers: [
mockProvider(NavigationService, {
navigateWithinApp: jest.fn(),
isRelativePathActive: () => true
})
]
});
spectator = buildService();
const navigationService = spectator.inject(NavigationService);
spectator.service.navigateToTraceDetail('trace-id', 'span-id', '1608150110610');
expect(navigationService.navigateWithinApp).toHaveBeenLastCalledWith([
'trace',
'trace-id',
{ spanId: 'span-id', startTime: '1608150110610' }
]);
expect(navigationService.navigate).toHaveBeenLastCalledWith({
navType: NavigationParamsType.InApp,
path: ['/trace', 'trace-id', { spanId: 'span-id', startTime: '1608150110610' }]
});

spectator.service.navigateToTraceDetail('trace-id', 'span-id');
expect(navigationService.navigateWithinApp).toHaveBeenLastCalledWith(['trace', 'trace-id', { spanId: 'span-id' }]);
expect(navigationService.navigate).toHaveBeenLastCalledWith({
navType: NavigationParamsType.InApp,
path: ['/trace', 'trace-id', { spanId: 'span-id' }]
});

spectator.service.navigateToTraceDetail('trace-id');
expect(navigationService.navigateWithinApp).toHaveBeenLastCalledWith(['trace', 'trace-id', {}]);
expect(navigationService.navigate).toHaveBeenLastCalledWith({
navType: NavigationParamsType.InApp,
path: ['/trace', 'trace-id', {}]
});
});

test('can navigate correctly to Api trace detail', () => {
spectator = buildService({
providers: [
mockProvider(NavigationService, {
navigateWithinApp: jest.fn(),
isRelativePathActive: () => true
})
]
test('builds correct trace detail navigation params', () => {
spectator = buildService();
expect(spectator.service.buildTraceDetailNavigationParam('trace-id', 'span-id', '1608150110610')).toEqual({
navType: NavigationParamsType.InApp,
path: ['/trace', 'trace-id', { spanId: 'span-id', startTime: '1608150110610' }]
});

expect(spectator.service.buildTraceDetailNavigationParam('trace-id', 'span-id')).toEqual({
navType: NavigationParamsType.InApp,
path: ['/trace', 'trace-id', { spanId: 'span-id' }]
});

expect(spectator.service.buildTraceDetailNavigationParam('trace-id')).toEqual({
navType: NavigationParamsType.InApp,
path: ['/trace', 'trace-id', {}]
});
});

test('builds correct Api trace detail navigation params', () => {
spectator = buildService();

expect(spectator.service.buildApiTraceDetailNavigationParam('trace-id', '1608150110610')).toEqual({
navType: NavigationParamsType.InApp,
path: ['/api-trace', 'trace-id', { startTime: '1608150110610' }]
});

expect(spectator.service.buildApiTraceDetailNavigationParam('trace-id')).toEqual({
navType: NavigationParamsType.InApp,
path: ['/api-trace', 'trace-id', {}]
});
});

test('can navigate correctly to Api trace detail', () => {
spectator = buildService();
const navigationService = spectator.inject(NavigationService);
spectator.service.navigateToApiTraceDetail('trace-id', '1608150110610');
expect(navigationService.navigateWithinApp).toHaveBeenLastCalledWith([
'api-trace',
'trace-id',
{ startTime: '1608150110610' }
]);
expect(navigationService.navigate).toHaveBeenLastCalledWith({
navType: NavigationParamsType.InApp,
path: ['/api-trace', 'trace-id', { startTime: '1608150110610' }]
});

spectator.service.navigateToApiTraceDetail('trace-id');
expect(navigationService.navigateWithinApp).toHaveBeenLastCalledWith(['api-trace', 'trace-id', {}]);
expect(navigationService.navigate).toHaveBeenLastCalledWith({
navType: NavigationParamsType.InApp,
path: ['/api-trace', 'trace-id', {}]
});
});
});
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import { Injectable } from '@angular/core';
import { Dictionary, NavigationService } from '@hypertrace/common';
import { Dictionary, NavigationParams, NavigationParamsType, NavigationService } from '@hypertrace/common';
import { Observable } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class TracingNavigationService {
public constructor(private readonly navigationService: NavigationService) {}

public navigateToTraceDetail(traceId: string, spanId?: string, startTime?: string | number): Observable<boolean> {
return this.navigationService.navigate(this.buildTraceDetailNavigationParam(traceId, spanId, startTime));
}

public buildTraceDetailNavigationParam(
traceId: string,
spanId?: string,
startTime?: string | number
): NavigationParams {
const optionalParams: Dictionary<string> = {};

if (startTime !== undefined) {
Expand All @@ -17,16 +25,26 @@ export class TracingNavigationService {
optionalParams.spanId = spanId;
}

return this.navigationService.navigateWithinApp(['trace', traceId, optionalParams]);
return {
navType: NavigationParamsType.InApp,
path: ['/trace', traceId, optionalParams]
};
}

public navigateToApiTraceDetail(traceId: string, startTime?: string | number): Observable<boolean> {
return this.navigationService.navigate(this.buildApiTraceDetailNavigationParam(traceId, startTime));
}

public buildApiTraceDetailNavigationParam(traceId: string, startTime?: string | number): NavigationParams {
const optionalParams: Dictionary<string> = {};

if (startTime !== undefined) {
optionalParams.startTime = `${String(startTime)}`;
}

return this.navigationService.navigateWithinApp(['api-trace', traceId, optionalParams]);
return {
navType: NavigationParamsType.InApp,
path: ['/api-trace', traceId, optionalParams]
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,23 @@
@import 'font';

.ht-entity-renderer {
@include body-1-regular(currentColor);
display: flex;
flex-direction: row;
align-items: center;
font-weight: inherit;
color: $gray-9;
.name-with-icon {
display: flex;
flex-direction: row;
align-items: center;
font-weight: inherit;

.icon {
padding-right: 12px;
}
.icon {
padding-right: 12px;
}

.name {
@include ellipsis-overflow();
.name {
@include ellipsis-overflow();
}
}
}

.navigable {
@include link-hover();
}

.inherit-text-color {
@include body-1-regular(inherit);
.default-text-style {
color: $gray-9;
@include body-1-regular(currentColor);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { IconType } from '@hypertrace/assets-library';
import { NavigationService } from '@hypertrace/common';
import { IconComponent } from '@hypertrace/components';
import { FormattingModule, NavigationParamsType, NavigationService } from '@hypertrace/common';
import { IconComponent, LinkComponent } from '@hypertrace/components';
import { createHostFactory, mockProvider, SpectatorHost } from '@ngneat/spectator/jest';
import { MockComponent } from 'ng-mocks';
import { entityIdKey, entityTypeKey, ObservabilityEntityType } from '../../graphql/model/schema/entity';
Expand All @@ -14,17 +14,21 @@ describe('Entity Renderer Component', () => {

const createHost = createHostFactory({
component: EntityRendererComponent,
imports: [FormattingModule],
providers: [
mockProvider(EntityNavigationService, {
navigateToEntity: jest.fn()
buildEntityDetailNavigationParams: jest.fn().mockReturnValue({
navType: NavigationParamsType.InApp,
path: ['/endpoint', 'test-id']
})
}),
mockProvider(NavigationService),
mockProvider(EntityIconLookupService, {
forEntity: jest.fn().mockReturnValue(ObservabilityIconType.Api)
})
],
shallow: true,
declarations: [MockComponent(IconComponent)]
declarations: [MockComponent(IconComponent), MockComponent(LinkComponent)]
});

test('renders a basic entity without navigation', () => {
Expand All @@ -49,12 +53,12 @@ describe('Entity Renderer Component', () => {
const entityNavService = spectator.inject(EntityNavigationService);
const rendererElement = spectator.query('.ht-entity-renderer')!;

expect(rendererElement).toExist();

expect(spectator.query('.name')).toHaveText('test api');
expect(spectator.query(IconComponent)!.icon).toBe(ObservabilityIconType.Api);

expect(rendererElement).not.toHaveClass('navigable');
spectator.dispatchFakeEvent(rendererElement, 'click');
expect(entityNavService.navigateToEntity).toHaveBeenCalledTimes(0);
expect(entityNavService.buildEntityDetailNavigationParams).not.toHaveBeenCalled();
});

test('renders a basic entity with navigation', () => {
Expand All @@ -75,15 +79,19 @@ describe('Entity Renderer Component', () => {
}
}
);
const entityNavService = spectator.inject(EntityNavigationService);
const rendererElement = spectator.query('.ht-entity-renderer')!;

expect(spectator.query('.name')).toHaveText('test api');
expect(spectator.query(IconComponent)!.icon).toBe(ObservabilityIconType.Api);

expect(rendererElement).toHaveClass('navigable');
spectator.dispatchFakeEvent(rendererElement, 'click');
expect(entityNavService.navigateToEntity).toHaveBeenCalledWith(entity, false);
expect(spectator.inject(EntityNavigationService).buildEntityDetailNavigationParams).toHaveBeenCalledWith(
entity,
false
);

expect(spectator.query(LinkComponent)?.paramsOrUrl).toEqual({
navType: NavigationParamsType.InApp,
path: ['/endpoint', 'test-id']
});
});

test('renders an entity without icon by default', () => {
Expand Down Expand Up @@ -143,23 +151,24 @@ describe('Entity Renderer Component', () => {
};

spectator = createHost(
`<ht-entity-renderer [entity]="entity" [inheritTextColor]="inheritTextColor">
`<ht-entity-renderer [entity]="entity" [inheritTextStyle]="inheritTextStyle">
</ht-entity-renderer>`,
{
hostProps: {
entity: entity,
inheritTextColor: false
inheritTextStyle: false
}
}
);

expect(spectator.query('.inherit-text-color')).not.toExist();
expect(spectator.query('.default-text-style')).toExist();
expect(spectator.query('.ht-entity-renderer')).toBe(spectator.query('.default-text-style'));

spectator.setHostInput({
inheritTextColor: true
inheritTextStyle: true
});
expect(spectator.query('.inherit-text-color')).toExist();
expect(spectator.query('.default-text-style')).not.toExist();

expect(spectator.query('.ht-entity-renderer')).toEqual(spectator.query('.inherit-text-color'));
expect(spectator.query('.ht-entity-renderer')).not.toBe(spectator.query('.default-text-style'));
});
});
Loading