META-9929, META-9868, resolves related ticket issues and misc tasks

This commit is contained in:
Seyi Adebajo 2019-09-18 10:07:04 -07:00
parent 162d52a421
commit f66f81dc4c
41 changed files with 261 additions and 182 deletions

View File

@ -1,10 +1,20 @@
import { getJSON, cacheApi } from '@datahub/utils/api/fetcher';
import { ApiVersion, getApiRoot } from '@datahub/utils/api/shared';
import buildUrl from '@datahub/utils/api/build-url';
import { IBrowseParams, IBrowseResponse } from '@datahub/data-models/types/entity/browse';
import { IBrowseParams, IBrowseResponse, IBrowsePathParams } from '@datahub/data-models/types/entity/browse';
/**
* URL for browse
* @param version api version
*/
export const browseUrlRoot = (version: ApiVersion): string => `${getApiRoot(version)}/browse`;
/**
* URL for browse paths
* @param version api version
*/
export const browsePathUrlRoot = (version: ApiVersion): string => `${getApiRoot(version)}/browsePaths`;
/**
* Will return the string url for the browse api
* @param params GET paramaters required for this API call, see IBrowseParams type
@ -14,6 +24,14 @@ const browseUrl = <T>(params: IBrowseParams<T>): string => {
return buildUrl(`${urlRoot}`, params);
};
/**
* Build GET request for browse
* @param params entity type and URN
*/
const browsePathUrl = (params: IBrowsePathParams): string => {
const urlRoot = browsePathUrlRoot(ApiVersion.v2);
return buildUrl(`${urlRoot}`, params);
};
/**
* Will fetch browse information for a specific path and entity.
* Will return a paginated return of elements and a fixed set of groups/folders.
@ -25,3 +43,13 @@ export const readBrowse = cacheApi(
return getJSON<IBrowseResponse>({ url });
}
);
/**
* Read the path to reach to the specified entity
*/
export const readBrowsePath = cacheApi(
(params: IBrowsePathParams): Promise<Array<string>> => {
const url = browsePathUrl(params);
return getJSON<Array<string>>({ url });
}
);

View File

@ -5,7 +5,7 @@ import { PersonEntity } from '@datahub/data-models/entity/person/person-entity';
* Defines the interface for the DataModelEntity enum below.
* This allows each entry in the enum to be indexable
*/
interface IDataModelEntity {
export interface IDataModelEntity {
[DatasetEntity.displayName]: typeof DatasetEntity;
[PersonEntity.displayName]: typeof PersonEntity;
}

View File

@ -1,7 +0,0 @@
/**
* Constant base for the user profile link. We append the username to this to reach their
* profile
* @type {string}
* @deprecated - should be moved to internal version
*/
export const profileLinkBase = 'https://cinco.linkedin.biz/people/';

View File

@ -13,7 +13,7 @@ import {
IEntityLinkAttrsWithCount
} from '@datahub/data-models/types/entity/shared';
import { NotImplementedError } from '@datahub/data-models/constants/entity/shared/index';
import { readBrowse } from '@datahub/data-models/api/browse';
import { readBrowse, readBrowsePath } from '@datahub/data-models/api/browse';
import { getFacetDefaultValueForEntity } from '@datahub/data-models/entity/utils/facets';
import { InstitutionalMemory } from '@datahub/data-models/models/aspects/institutional-memory';
@ -197,6 +197,31 @@ export abstract class BaseEntity<T extends IBaseEntity> {
throw new Error(NotImplementedError);
}
/**
* Workaround to get the current static instance
* This makes sense if you want to get a static property only
* implemented in a subclass, therefore, the same static
* class is needed
*/
get staticInstance(): IBaseEntityStatics<T> {
return (this.constructor as unknown) as IBaseEntityStatics<T>;
}
/**
* Will read the current path for an entity
*/
get readPath(): Promise<Array<string>> {
const entityName = this.staticInstance.renderProps.search.apiName;
return readBrowsePath({
type: entityName,
urn: this.urn
}).then(
(paths): Array<string> => {
return paths && paths.length > 0 ? paths[0].split('/').filter(Boolean) : [];
}
);
}
/**
* Asynchronously resolves with an instance of T
* @readonly
@ -219,15 +244,6 @@ export abstract class BaseEntity<T extends IBaseEntity> {
throw new Error(NotImplementedError);
}
/**
*
* @readonly
* @type {Array<string>}
* @memberof BaseEntity
*/
get hierarchySegments(): Array<string> {
throw new Error(NotImplementedError);
}
/**
* Class properties common across instances
* Dictates how visual ui components should be rendered

View File

@ -10,9 +10,6 @@ import { oneWay } from '@ember/object/computed';
import { DatasetPlatform } from '@datahub/metadata-types/constants/entity/dataset/platform';
import { decodeUrn } from '@datahub/utils/validators/urn';
import { readCategories } from '@datahub/data-models/entity/dataset/read-categories';
import { computed } from '@ember/object';
import { IDataPlatform } from '@datahub/metadata-types/types/entity/dataset/platform';
import { readDataPlatforms } from '@datahub/data-models/api/dataset/platforms';
import { getPrefix } from '@datahub/data-models/entity/dataset/utils/segments';
import { readDatasetsCount } from '@datahub/data-models/api/dataset/count';
import { setProperties } from '@ember/object';
@ -71,6 +68,17 @@ export class DatasetEntity extends BaseEntity<IDatasetApiView> {
return decodeUrn(this.urn);
}
/**
* Creates a link for this specific entity instance, useful for generating a dynamic link from a
* single particular dataset entity
*/
get linkForEntity(): IEntityLinkAttrs {
return DatasetEntity.getLinkForEntity({
entityUrn: this.urn,
displayName: this.name
});
}
/**
* Reads the snapshots for a list of Dataset urns
* @static
@ -109,15 +117,10 @@ export class DatasetEntity extends BaseEntity<IDatasetApiView> {
/**
* Reference to the data entity's native name, should not be something that is editable but gives us a
* more human readable form for the dataset vs the urn
* @type {string}
*/
@oneWay('entity.nativeName')
name!: string;
/**
* Reference to the constructed data platform once it is fetched from api
*/
currentDataPlatform?: IDataPlatform;
get name(): string {
return this.entity ? this.entity.nativeName : '';
}
/**
* Retrieves the value of the Dataset entity identified by this.urn
@ -204,26 +207,6 @@ export class DatasetEntity extends BaseEntity<IDatasetApiView> {
));
}
/**
* Produces an array of strings depicting the hierarchy of a Feature Entity
* @readonly
* @type {Array<string>}
* @memberof FeatureEntity
*/
@computed('entity')
get hierarchySegments(): Array<string> {
const { entity } = this;
const { currentDataPlatform } = this;
const separator = (currentDataPlatform && currentDataPlatform.datasetNameDelimiter) || '.';
if (entity) {
const { platform, nativeName } = entity;
return [platform, ...nativeName.split(separator).filter(Boolean)];
}
return [];
}
/**
* Interim implementation to read categories for datasets
* TODO META-8863
@ -287,14 +270,10 @@ export class DatasetEntity extends BaseEntity<IDatasetApiView> {
export const createDatasetEntity = async (urn: string, fetchedEntity?: IDatasetApiView): Promise<DatasetEntity> => {
const dataset = new DatasetEntity(urn);
const entity = fetchedEntity || (await dataset.readEntity);
// TODO: [META-8652] Find way to separate this from the createDatasetEntity as the concern of creating a dataset
// entity should not be related to the concern of reading data platforms
const dataPlatforms: Array<IDataPlatform> = await readDataPlatforms();
const currentDataPlatform = dataPlatforms.find((platform): boolean => platform.name === entity.platform);
setProperties(dataset, {
entity,
currentDataPlatform
entity
});
return dataset;
};

View File

@ -6,7 +6,6 @@ import { getTabPropertiesFor } from '@datahub/data-models/entity/utils';
/**
* Class properties common across instances
* Dictates how visual ui components should be rendered
* Implemented as a getter to ensure that reads are idempotent
* @readonly
* @static
* @type {IEntityRenderProps}

View File

@ -1,6 +1,5 @@
import getActorFromUrn from '@datahub/data-models/utils/get-actor-from-urn';
import { computed } from '@ember/object';
import { profileLinkBase } from '@datahub/data-models/constants/entity/person/links';
import { NotImplementedError } from '@datahub/data-models/constants/entity/shared';
import {
getRenderProps,
@ -51,7 +50,7 @@ export class PersonEntity extends BaseEntity<IBaseEntity> {
* TODO: [META-9698] Migrate to using LiPersonEntity
*/
static profileLinkFromUsername(username: string): string {
return `${profileLinkBase}${username}`;
return `${username}`;
}
/**

View File

@ -26,3 +26,11 @@ export interface IBrowseResponse {
groups: Array<{ name: string; count: number }>;
};
}
/**
* Path for bowse paths
*/
export interface IBrowsePathParams {
type: string;
urn: string;
}

View File

@ -107,4 +107,4 @@
</form>
{{yield}}
{{yield}}

View File

@ -0,0 +1,47 @@
import Component from '@ember/component';
// @ts-ignore: Ignore import of compiled template
import template from '../templates/components/wait-promise-container';
import { containerDataSource } from '@datahub/utils/api/data-source';
import { ETaskPromise } from '@datahub/utils/types/concurrency';
import { task } from 'ember-concurrency';
import { set } from '@ember/object';
import { layout } from '@ember-decorators/component';
/**
* WaitPromiseContainer will show spinner while the promise passed is being resolved
*
* usage:
*
* const promise = Promise.resolve(3);
*
* <WaitPromiseContainer @promise={{promise}} as |resolved|>
* {{resolved}}
* </WaitPromiseContainer>
*
* should show '3' as the promise resolved '3'
*/
@layout(template)
@containerDataSource('getContainerDataTask', ['promise'])
export default class WaitPromiseContainer<T> extends Component {
/**
* Input promise that we want to wait
*/
promise?: Promise<T>;
/**
* Value for the resolved promise that will be yielded
*/
resolved?: T;
/**
* Will fetch and transform api data into status table input format
*/
@task(function*(this: WaitPromiseContainer<T>): IterableIterator<Promise<T>> {
const { promise } = this;
if (promise) {
const resolved: T = yield promise;
set(this, 'resolved', resolved);
}
})
getContainerDataTask!: ETaskPromise<T>;
}

View File

@ -0,0 +1,3 @@
<ConcurrencyTaskStateHandler @task={{getContainerDataTask}}>
{{yield this.resolved}}
</ConcurrencyTaskStateHandler>

View File

@ -0,0 +1 @@
export { default } from '@datahub/shared/components/wait-promise-container';

View File

@ -47,6 +47,7 @@
"ember-cli-sri": "^2.1.1",
"ember-cli-typescript-blueprints": "^2.0.0",
"ember-cli-uglify": "^2.1.0",
"ember-concurrency": "^1.0.0",
"ember-export-application-global": "^2.0.0",
"ember-load-initializers": "^2.0.0",
"ember-maybe-import-regenerator": "^0.1.6",

View File

@ -1,2 +1 @@
const testemConf = require('../../configs/testem-base');
module.exports = testemConf;
module.exports = require('../../configs/testem-base');

View File

@ -0,0 +1,31 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, settled } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
module('Integration | Component | wait-promise-container', function(hooks): void {
setupRenderingTest(hooks);
test('it renders', async function(assert): Promise<void> {
let testResolve: (value?: unknown) => void = (): void => {};
this.set(
'promise',
new Promise((resolve): void => {
testResolve = resolve;
})
);
await render(hbs`
<WaitPromiseContainer @promise={{promise}} as |resolved|>
{{resolved}}
</WaitPromiseContainer>
`);
assert.equal(this.element.textContent && this.element.textContent.trim(), '');
testResolve('test');
// we need to wait to the rerender to happen as setting a property will trigger an ember loop
await settled();
assert.equal(this.element.textContent && this.element.textContent.trim(), 'test');
});
});

View File

@ -1,6 +1,6 @@
import { ITrackingConfig } from '@datahub/shared/types/configurator/tracking';
import Service from '@ember/service';
import { ApiStatus } from '@datahub/utils/addon/api/shared';
import { ApiStatus } from '@datahub/utils/api/shared';
/**
* Describes the interface for the configuration endpoint response object.

View File

@ -19,6 +19,7 @@ export function populateMockPersonEntity(
entity.linkedinProfile = 'https://www.linkedin.com/in/ash-ketchum-b212502a/';
entity.slackLink = 'aketchum';
entity.skills = ['training', 'catching', 'battling'];
const managerEntity = new personEntity(personEntity.urnFromUsername('pikachu'));
managerEntity.name = 'Pikachu';
managerEntity.profilePictureUrl = 'https://pokemonletsgo.pokemon.com/assets/img/common/char-pikachu.png';

View File

@ -1,12 +1,12 @@
import Component from '@ember/component';
// @ts-ignore: Ignore import of compiled template
import template from '../templates/components/concurrency-task-state-handler';
import { layout, classNames } from '@ember-decorators/component';
import { layout, tagName } from '@ember-decorators/component';
import { action } from '@ember/object';
import { Task, task } from 'ember-concurrency';
@layout(template)
@classNames('concurrency-task-state-handler')
@tagName('')
export default class ConcurrencyTaskStateHandler<T> extends Component {
/**
* List of arguments to be passed to the task when performed. The array is spread into the task invocation

View File

@ -30,7 +30,9 @@
{{else}}
{{#if @task.isRunning}}
{{pendulum-ellipsis-animation}}
<div class="concurrency-task-state-handler">
{{pendulum-ellipsis-animation}}
</div>
{{else}}
{{yield}}
{{/if}}

View File

@ -2,6 +2,6 @@ export interface IDynamicLinkNode<T, Z = string, P extends {} = {}> {
title: string;
text: string;
route: Z;
model: T;
model?: T;
queryParams?: P;
}

View File

@ -1,5 +1,5 @@
import Component from '@ember/component';
import { IDynamicLinkNode } from 'wherehows-web/typings/app/browse/dynamic-link';
import { IDynamicLinkNode } from '@datahub/utils/types/vendor/dynamic-link';
/**
* Card component. It is used in the home page of the app to

View File

@ -2,7 +2,7 @@ import Component from '@ember/component';
import { tagName } from '@ember-decorators/component';
import { computed } from '@ember/object';
import { DataModelEntity } from '@datahub/data-models/constants/entity';
import { IDynamicLinkNode } from 'wherehows-web/typings/app/browse/dynamic-link';
import { IDynamicLinkNode } from '@datahub/utils/types/vendor/dynamic-link';
/**
* Indicates the total number of entities within a category and on user interaction

View File

@ -296,7 +296,7 @@ export default class DatasetAuthors extends Component {
@action
confirmSuggestedOwner(this: DatasetAuthors, owner: IOwner): Array<IOwner> | void {
const suggestedOwner = { ...owner, source: OwnerSource.Ui };
return this.actions.addOwner.call(this, suggestedOwner);
return this.addOwner.call(this, suggestedOwner);
}
/**

View File

@ -200,14 +200,30 @@ export default class DatasetMainContainer extends Component {
// TODO should fetch the dataset itself, but right now dataset is fetched at route level
const { dataset } = this;
//TODO: META-8267 Container notifications decorator
if (dataset) {
const entity: DatasetEntity = yield createDatasetEntity(this.dataset.uri, this.dataset as IDatasetApiView);
set(this, 'entity', entity);
}
}).restartable())
reifyEntityTask!: ETaskPromise<DatasetEntity>;
/**
* This will return the paths for an entity. We should be able to consume entity.readPath
* direcly but since datasets are not migrated we need to flag guard it.
*
* TODO META-8863 Interim implementation to read categories for datasets
*/
@computed('entity')
get paths(): Promise<Array<string>> {
const { entity } = this;
if (!entity) {
return Promise.resolve([]);
}
return entity.readPath;
}
/**
* Converts the uri on a model to a usable URN format
* @type {ComputedProperty<string>}

View File

@ -1,7 +1,7 @@
import Component from '@ember/component';
import { computed } from '@ember/object';
import { tagName } from '@ember-decorators/component';
import { IDynamicLinkNode } from 'wherehows-web/typings/app/browse/dynamic-link';
import { IDynamicLinkNode } from '@datahub/utils/types/vendor/dynamic-link';
/**
* Query params with pages for routes

View File

@ -4,6 +4,7 @@ import SearchService from 'wherehows-web/services/search';
import { alias } from '@ember/object/computed';
import { grammarProcessingSteps, typeaheadQueryProcessor } from 'wherehows-web/utils/parsers/autocomplete';
import { ISuggestionGroup } from 'wherehows-web/utils/parsers/autocomplete/types';
import { DatasetEntity } from '@datahub/data-models/entity/dataset/dataset-entity';
import { tagName } from '@ember-decorators/component';
import { computed } from '@ember/object';
import { DataModelEntity, DataModelName } from '@datahub/data-models/constants/entity';
@ -68,7 +69,7 @@ export default class SearchBoxContainer extends Component {
* @param {Entity} [entity]
* @memberof SearchBoxContainer
*/
onSearch(text: string = '', entity: DataModelEntity['displayName'] = 'datasets'): void {
onSearch(text: string = '', entity: DataModelName = DatasetEntity.displayName): void {
// entity (dropdown value) might be different than this.entity (Page that you are in)
const dataModelEntity = DataModelEntity[entity];
const { attributes } = dataModelEntity.renderProps.search;

View File

@ -17,13 +17,18 @@ const fallback = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAA
const makeAvatar = ({ aviUrlPrimary, aviUrlFallback = fallback }: IAppConfig['userEntityProps']): AvatarCreatorFunc => (
object: Partial<IAvatar>
): IAvatar => {
const props = pick(object, ['email', 'userName', 'name', 'imageUrl']);
const { userName } = props;
const imageFallback = aviUrlFallback || fallback;
const props = pick(object, ['email', 'userName', 'name', 'imageUrl', 'pictureLink']);
const { userName, pictureLink } = props;
const imageUrlFallback = aviUrlFallback || fallback;
const imageUrl = pictureLink
? pictureLink
: userName && aviUrlPrimary
? aviUrlPrimary.replace('[username]', userName)
: imageUrlFallback;
return {
imageUrl: userName && aviUrlPrimary ? aviUrlPrimary.replace('[username]', userName) : imageFallback,
imageUrlFallback: imageFallback,
imageUrlFallback,
imageUrl,
...props
};
};

View File

@ -5,7 +5,7 @@ import Search from 'wherehows-web/services/search';
import { set } from '@ember/object';
import { DataModelEntity } from '@datahub/data-models/constants/entity';
import { unGuardedEntities } from 'wherehows-web/utils/entity/flag-guard';
import { IDynamicLinkNode } from 'wherehows-web/typings/app/browse/dynamic-link';
import { IDynamicLinkNode } from '@datahub/utils/types/vendor/dynamic-link';
import { capitalize } from '@ember/string';
import { getConfig } from 'wherehows-web/services/configurator';

View File

@ -115,6 +115,7 @@ $nacho-breadcrumbs-arrow-padding: 20px;
top: $application-navbar-static-height;
z-index: z('nav') + z('below'); // To give precendence to tooltip and dropdown
width: 100%;
min-height: $nacho-breadcrumbs-height;
&--with-banner-offset {
top: $banner-alerts-height + $application-navbar-static-height;

View File

@ -2,11 +2,11 @@
<section class="browse-category-container {{if (eq model.segments.length 0) "browse-category-container--no-breadcrumbs"}}">
{{!-- Dont show breadcrumbs first level since we already have the title and there is real action --}}
{{#if (gt model.segments.length 0)}}
{{browser/entity-breadcrumbs
class=(with-banner-offset "nacho-breadcrumbs-container")
entity=(lowercase model.displayName)
segments=model.segments
}}
<Browser::EntityBreadcrumbs
class={{with-banner-offset "nacho-breadcrumbs-container"}}
@entity={{lowercase model.displayName}}
@segments={{model.segments}}
/>
{{/if}}
<div class="container">

View File

@ -69,17 +69,6 @@
</table>
</section>
<section class="dataset-author">
{{datasets/owners/suggested-owners
owners=systemGeneratedOwnersWithAvatars
ownerTypes=ownerTypes
commonOwners=commonOwners
removeOwner=(action "removeOwner")
confirmSuggestedOwner=(action "confirmSuggestedOwner")
updateOwnerType=(action "updateOwnerType")
}}
</section>
<section class="action-bar">
<div class="container action-bar__content">
<button
@ -113,4 +102,4 @@
</div>
{{/if}}
</div>
</section>
</section>

View File

@ -21,6 +21,7 @@
notifications=notificationsMap
headerComponent=headerComponent
fields=fields
paths=this.paths
tabSelectionChanged=(action tabSelectionChanged)
setOwnershipRuleChange=(action setOwnershipRuleChange)
)

View File

@ -48,22 +48,6 @@
{{/if}}
</div>
<div class="dataset-header-meta">
{{#datasets/containers/dataset-fabrics
class="dataset-fabric-row-container"
urn=model.uri
as |fabricsContainer|
}}
<div class="dataset-fabric-entity-container">
<strong>Fabric:</strong>
{{datasets/dataset-fabric-switcher
urn=fabricsContainer.urn
fabrics=fabricsContainer.fabrics
}}
</div>
{{/datasets/containers/dataset-fabrics}}
</div>
{{datasets/containers/dataset-owner-list
urn=encodedUrn
avatarEntityProps=avatarEntityProps

View File

@ -8,11 +8,16 @@ as |container|
as |snapshot|
}}
<div id="dataset" class="entity-header">
{{browser/entity-breadcrumbs
class=(with-banner-offset "nacho-breadcrumbs-container")
entity=(or (lowercase container.entity.displayName) "--")
segments=container.entity.hierarchySegments
}}
<WaitPromiseContainer
class={{with-banner-offset "nacho-breadcrumbs-container"}}
@promise={{container.paths}} as |paths|>
<Browser::EntityBreadcrumbs
@tagName=""
@entity={{or (lowercase container.entity.displayName) "--"}}
@segments={{paths}}
/>
</WaitPromiseContainer>
<div class="container">
{{component
container.headerComponent

View File

@ -9,6 +9,9 @@ export interface IAvatar {
imageUrl: string;
// Fallback url for avatar image on error
imageUrlFallback: string;
// Alternate url for avatar image
pictureLink?: string;
// Email address for the associated entity if available
email?: null | string;
// Handle for the avatar
userName?: string;

View File

@ -23,6 +23,7 @@ import { browse } from 'wherehows-web/mirage/helpers/browse';
import searchResponse from 'wherehows-web/mirage/fixtures/search-response';
import { getSamplePageViewResponse } from 'wherehows-web/mirage/helpers/search/pageview-response';
import { getEntitySearchResults } from 'wherehows-web/mirage/helpers/search/entity';
import { browsePaths } from 'wherehows-web/mirage/helpers/browse-paths';
export default function(this: IMirageServer): void {
this.get('/config', getConfig);
@ -37,6 +38,8 @@ export default function(this: IMirageServer): void {
this.get('/browse', browse);
this.get('/browsePaths', browsePaths);
this.get('/datasets/:identifier/', getDatasetView);
this.get('/datasets/:identifier/owners', getDatasetOwners);

View File

@ -47,8 +47,9 @@ export default Factory.extend({
userEntityProps() {
return {
aviUrlPrimary:
'https://cinco.corp.linkedin.com/api/profile/[username]/picture?access_token=2rzmbzEMGlHsszQktFY-B1TxUic',
aviUrlFallback: 'https://static.licdn-ei.com/sc/h/djzv59yelk5urv2ujlazfyvrk'
'https://raw.githubusercontent.com/linkedin/WhereHows/datahub/datahub-web/packages/data-portal/public/assets/images/default_avatar.png',
aviUrlFallback:
'https://raw.githubusercontent.com/linkedin/WhereHows/datahub/datahub-web/packages/data-portal/public/assets/images/default_avatar.png'
};
}
});

View File

@ -0,0 +1,11 @@
import { getDatasetUrnParts } from '@datahub/data-models/entity/dataset/utils/urn';
import { FabricType } from '@datahub/metadata-types/constants/common/fabric-type';
export const browsePaths = (_schema: any, request: any): Array<string> => {
const {
queryParams: { urn }
} = request;
const { platform, prefix = '', fabric = FabricType.PROD } = getDatasetUrnParts(urn);
return [`${fabric}/${platform}/${prefix}`];
};

View File

@ -11,14 +11,15 @@ module('Acceptance | breadcrumbs-smoke-test', function(hooks) {
test('Breadcrumbs Smoke Test', async function(this: IMirageTestContext, assert) {
const categoryLinkClass = '.browse-category__link';
const breadcrumbsClass = '.nacho-breadcrumbs__crumb';
const breadCrumbClass = '.nacho-breadcrumbs';
const breadCrumbsClass = `${breadCrumbClass}__crumb`;
defaultScenario(this.server);
await appLogin();
await visit('/');
await visit('/browse/datasets?path=hdfs');
await waitFor(categoryLinkClass);
let categoryLinks = findAll(categoryLinkClass);
let breadcrumbs = findAll(breadcrumbsClass);
let breadcrumbs = findAll(breadCrumbsClass);
assert.equal(currentURL(), '/browse/datasets?path=hdfs');
assert.equal(categoryLinks.length, 1, 'There is 1 folder in root hdfs');
@ -30,7 +31,7 @@ module('Acceptance | breadcrumbs-smoke-test', function(hooks) {
await click(`${categoryLinkClass}:first-child`);
await waitFor(categoryLinkClass);
categoryLinks = findAll(categoryLinkClass);
breadcrumbs = findAll(breadcrumbsClass);
breadcrumbs = findAll(breadCrumbsClass);
assert.equal(currentURL(), '/browse/datasets?path=hdfs%2Fsome');
assert.equal(categoryLinks.length, 1, 'There is 1 folder in path');
@ -42,7 +43,7 @@ module('Acceptance | breadcrumbs-smoke-test', function(hooks) {
await click(`${categoryLinkClass}:first-child`);
await waitFor(categoryLinkClass);
categoryLinks = findAll(categoryLinkClass);
breadcrumbs = findAll(breadcrumbsClass);
breadcrumbs = findAll(breadCrumbsClass);
assert.equal(currentURL(), '/browse/datasets?path=hdfs%2Fsome%2Fpath');
assert.equal(categoryLinks.length, 1, 'There is 1 folder in path');
@ -54,7 +55,7 @@ module('Acceptance | breadcrumbs-smoke-test', function(hooks) {
await click(`${categoryLinkClass}:first-child`);
await waitFor(categoryLinkClass, { count: 2 });
categoryLinks = findAll(categoryLinkClass);
breadcrumbs = findAll(breadcrumbsClass);
breadcrumbs = findAll(breadCrumbsClass);
assert.equal(currentURL(), '/browse/datasets?path=hdfs%2Fsome%2Fpath%2Fwith');
assert.equal(categoryLinks.length, 2, 'There are 2 folder in path');
@ -66,7 +67,7 @@ module('Acceptance | breadcrumbs-smoke-test', function(hooks) {
await click(`${categoryLinkClass}:first-child`);
await waitFor(categoryLinkClass, { count: 2 });
categoryLinks = findAll(categoryLinkClass);
breadcrumbs = findAll(breadcrumbsClass);
breadcrumbs = findAll(breadCrumbsClass);
assert.equal(currentURL(), '/browse/datasets?path=hdfs%2Fsome%2Fpath%2Fwith%2Fdirectories');
assert.equal(categoryLinks.length, 2, 'There is only 2 datasets in path');
@ -76,29 +77,13 @@ module('Acceptance | breadcrumbs-smoke-test', function(hooks) {
// path adataset1
await click(`${categoryLinkClass}:first-child`);
breadcrumbs = findAll(breadcrumbsClass);
breadcrumbs = findAll(breadCrumbsClass);
assert.equal(
currentURL(),
'/datasets/urn:li:dataset:(urn:li:dataPlatform:hdfs,%2Fsome%2Fpath%2Fwith%2Fdirectories%2Fadataset1,PROD)/schema'
);
assert.equal(breadcrumbs.length, 7, 'There are 7 links in breadcrumb');
assert.equal(
getTextNoSpacesFromElements(breadcrumbs),
'Datasetshdfssomepathwithdirectoriesadataset1',
'Text match'
);
// path /some/path/with/directories
await click(`${breadcrumbsClass}:nth-child(6) a`);
await waitFor(categoryLinkClass, { count: 2 });
categoryLinks = findAll(categoryLinkClass);
breadcrumbs = findAll(breadcrumbsClass);
assert.equal(currentURL(), '/browse/datasets?page=1&path=hdfs%2Fsome%2Fpath%2Fwith%2Fdirectories');
assert.equal(categoryLinks.length, 2, 'There is only 2 datasets in path');
assert.equal(getTextNoSpacesFromElements(categoryLinks), 'adataset1adataset2', 'Text match');
assert.equal(breadcrumbs.length, 6, 'There are 6 links in breadcrumb');
assert.equal(getTextNoSpacesFromElements(breadcrumbs), 'Datasetshdfssomepathwithdirectories', 'Text match');
assert.equal(breadcrumbs.length, 8, 'There are 8 links in breadcrumb');
assert.dom(breadCrumbClass).hasText('Datasets PROD hdfs some path with directories adataset1');
});
});

View File

@ -6,17 +6,17 @@ import hbs from 'htmlbars-inline-precompile';
const componentClassName = '.nacho-breadcrumbs-container';
const breadcrumbsListClassName = '.nacho-breadcrumbs';
module('Integration | Component | browser/entity-breadcrumbs', function(hooks) {
module('Integration | Component | browser/entity-breadcrumbs', function(hooks): void {
setupRenderingTest(hooks);
test('Breadcrumbs component rendering', async function(assert) {
test('Breadcrumbs component rendering', async function(assert): Promise<void> {
const entity = 'Test Entity';
let segments: Array<string> = [];
this.setProperties({ segments, entity });
await render(hbs`
{{browser/entity-breadcrumbs segments=segments entity=entity}}
<Browser::EntityBreadcrumbs @segments={{segments}} @entity={{entity}} />
`);
assert.dom(componentClassName).isVisible();

View File

@ -4,7 +4,7 @@ import { render, findAll, click } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import { noop } from 'wherehows-web/utils/helpers/functions';
import { OwnerType, OwnerSource } from 'wherehows-web/utils/api/datasets/owners';
import { OwnerType } from 'wherehows-web/utils/api/datasets/owners';
import owners from 'wherehows-web/mirage/fixtures/owners';
import userStub from 'wherehows-web/tests/stubs/services/current-user';
import { minRequiredConfirmedOwners } from 'wherehows-web/constants/datasets/owner';
@ -29,7 +29,7 @@ module('Integration | Component | dataset authors', function(hooks) {
this.set('saveOwnerChanges', noop);
await render(hbs`{{dataset-authors owners=owners ownerTypes=ownerTypes save=(action saveOwnerChanges)}}`);
assert.equal(findAll('.dataset-author').length, 2, 'expected two dataset author components to be rendered');
assert.equal(findAll('.dataset-author').length, 1, 'expected two dataset author components to be rendered');
});
test('it should remove an owner when removeOwner is invoked', async function(assert) {
@ -43,39 +43,6 @@ module('Integration | Component | dataset authors', function(hooks) {
assert.equal(this.get('owners').length, 0);
});
test('it should update a suggested owner to confirmed', async function(assert) {
assert.expect(3);
const [owner, suggestedOwner] = owners;
const resolvedOwners = [owner];
const suggestedOwners = [suggestedOwner];
const initialLength = resolvedOwners.length;
let userName, confirmedOwner;
this.set('owners', resolvedOwners);
this.set('ownerTypes', ownerTypes);
this.set('saveOwnerChanges', noop);
this.set('suggestedOwners', suggestedOwners);
await render(
hbs`{{dataset-authors owners=owners suggestedOwners=suggestedOwners ownerTypes=ownerTypes save=(action saveOwnerChanges)}}`
);
assert.equal(
this.get('owners.length'),
initialLength,
`the list of owners is ${initialLength} before adding confirmed owner`
);
await click('.dataset-authors-suggested__info__trigger');
await click('.suggested-owner-card__owner-info__add button');
userName = this.get('current-user.currentUser.userName');
confirmedOwner = this.get('owners').findBy('confirmedBy', userName);
assert.equal(this.get('owners.length'), initialLength + 1, 'the list of owner contains one more new owner');
assert.equal(confirmedOwner.source, OwnerSource.Ui, 'contains a new owner with ui source');
});
test('it should disable the save button when confirmedOwners is less than required minimum', async function(assert) {
assert.expect(2);
this.set('owners', [confirmedOwner]);