Skip to content

Commit bfe4401

Browse files
Fran McDadeFran McDade
authored andcommitted
Hyperlink Analysis Protocol to corresponding Pipelines page in the portal. Resolves #950.
1 parent 72c141c commit bfe4401

15 files changed

+575
-149
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<ng-container *ngFor="let analysisProtocol of getAnalysisProtocols(); last as lastAnalysisProtocol">
2+
<ng-container [ngSwitch]="isAnalysisProtocolLinked(analysisProtocol)">
3+
<a href="{{getPipelineLink(analysisProtocol)}}" rel="noopener noreferrer" target="_blank" *ngSwitchDefault>{{analysisProtocol}}<span class="comma" *ngIf="!lastAnalysisProtocol">, </span></a>
4+
<span *ngSwitchCase="false">{{analysisProtocol}}<span class="comma" *ngIf="!lastAnalysisProtocol">, </span></span>
5+
</ng-container>
6+
</ng-container>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Human Cell Atlas
3+
* https://www.humancellatlas.org/
4+
*
5+
* Styles for analysis protocol pipeline linker.
6+
*/
7+
@import "../../site/theme/cgl.vars";
8+
9+
/* Tables */
10+
/* HCATableFilesComponent */
11+
/* HCATableProjectsComponent */
12+
/* HCATableSamplesComponent */
13+
:host-context(.hca-table) {
14+
15+
.comma {
16+
color: $hca-gray-dark;
17+
}
18+
}
19+
20+
/* Project detail page */
21+
/* HCAProjectComponent */
22+
:host-context(.explore-project) {
23+
24+
.comma {
25+
color: black;
26+
}
27+
}
Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
/*
2+
* Human Cell Atlas
3+
* https://www.humancellatlas.org/
4+
*
5+
* Test suite for AnalysisProtocolPipelineLinker.
6+
*/
7+
8+
// Core dependencies
9+
import { DebugElement } from "@angular/core";
10+
import { async, ComponentFixture, TestBed } from "@angular/core/testing";
11+
import { By } from "@angular/platform-browser";
12+
13+
// App dependencies
14+
import { ConfigService } from "../../config/config.service";
15+
import { AnalysisProtocolPipelineLinkerComponent } from "./analysis-protocol-pipeline-linker.component";
16+
17+
describe("AnalysisProtocolPipelineLinkerComponent", () => {
18+
19+
let component: AnalysisProtocolPipelineLinkerComponent;
20+
let fixture: ComponentFixture<AnalysisProtocolPipelineLinkerComponent>;
21+
22+
const testConfig = jasmine.createSpyObj("ConfigService", ["getPortalURL"]);
23+
24+
// Create response for testConfig.getPortalUrl()
25+
testConfig.getPortalURL.and.returnValue("https://test.com");
26+
27+
// Template values
28+
const TEMPLATE_VALUE_DATA_PORTAL_LINK_SMARTSEQ2 = "/pipelines/smart-seq2-workflow";
29+
const TEMPLATE_VALUE_DATA_PORTAL_LINK_OPTIMUS = "/pipelines/optimus-workflow";
30+
31+
// Test values
32+
const TEST_VALUE_CELL_RANGER = "cellranger_v1.0.1";
33+
const TEST_VALUE_OPTIMUS = "optimus_v1.3.2";
34+
const TEST_VALUE_SMARTSEQ2 = "smartseq2_v2.4.0";
35+
const TEST_VALUE_ANALYSIS_PROTOCOLS_MULTIPLE_MIXED_VALUES = [TEST_VALUE_CELL_RANGER, TEST_VALUE_OPTIMUS, TEST_VALUE_SMARTSEQ2];
36+
37+
// Input values
38+
const INPUT_VALUE_WORKFLOW_MULTIPLE_LINKED_VALUES = "optimus_v2.0.2, optimus_v1.3.2, smartseq2_v2.4.0";
39+
const INPUT_VALUE_WORKFLOW_MULTIPLE_MIXED_VALUES = "cellranger_v1.0.1, optimus_v1.3.2, smartseq2_v2.4.0";
40+
const INPUT_VALUE_WORKFLOW_MULTIPLE_UNLINKED_VALUES = "cellranger_v1.0.1, cellranger_v1.0.4, se-rm-pipeline-version-output";
41+
const INPUT_VALUE_WORKFLOW_SINGLE_OPTIMUS_VALUE = TEST_VALUE_OPTIMUS;
42+
const INPUT_VALUE_WORKFLOW_SINGLE_SMARTSEQ2_VALUE = TEST_VALUE_SMARTSEQ2;
43+
44+
beforeEach(async(() => {
45+
46+
TestBed.configureTestingModule({
47+
declarations: [
48+
AnalysisProtocolPipelineLinkerComponent
49+
],
50+
imports: [],
51+
providers: [{
52+
provide: ConfigService,
53+
useValue: testConfig
54+
}]
55+
}).compileComponents();
56+
57+
fixture = TestBed.createComponent(AnalysisProtocolPipelineLinkerComponent);
58+
component = fixture.componentInstance;
59+
}));
60+
61+
/**
62+
* Smoke test
63+
*/
64+
it("should create an instance", () => {
65+
66+
expect(component).toBeTruthy();
67+
});
68+
69+
/**
70+
* Confirm get analysis protocols returns a concatenated string array when multiple workflow values.
71+
*/
72+
it("should get analysis protocols returns a concatenated string array when multiple workflow values", () => {
73+
74+
component.workflow = INPUT_VALUE_WORKFLOW_MULTIPLE_MIXED_VALUES;
75+
fixture.detectChanges();
76+
77+
// Confirm get analysis protocols returns a concatenated string array
78+
const getAnalysisProtocols = component.getAnalysisProtocols();
79+
expect(getAnalysisProtocols).toEqual(TEST_VALUE_ANALYSIS_PROTOCOLS_MULTIPLE_MIXED_VALUES);
80+
});
81+
82+
/**
83+
* Confirm get analysis protocols returns a string array when single workflow values.
84+
*/
85+
it("should get analysis protocols returns a string array when single workflow values", () => {
86+
87+
component.workflow = INPUT_VALUE_WORKFLOW_SINGLE_OPTIMUS_VALUE;
88+
fixture.detectChanges();
89+
90+
// Confirm get analysis protocols returns a string array
91+
const getAnalysisProtocols = component.getAnalysisProtocols();
92+
expect(getAnalysisProtocols).toEqual([TEST_VALUE_OPTIMUS]);
93+
});
94+
95+
/**
96+
* Confirm get analysis protocols returns an empty array when empty workflow values.
97+
*/
98+
it("should get analysis protocols returns an empty array when empty workflow values", () => {
99+
100+
component.workflow = "";
101+
fixture.detectChanges();
102+
103+
// Confirm get analysis protocols returns a concatenated string array
104+
const getAnalysisProtocols = component.getAnalysisProtocols();
105+
expect(getAnalysisProtocols).toEqual([]);
106+
});
107+
/**
108+
* Confirm get analysis protocols returns a string array when single workflow values.
109+
*/
110+
it("should get analysis protocols returns a string array when single workflow values", () => {
111+
112+
component.workflow = INPUT_VALUE_WORKFLOW_SINGLE_OPTIMUS_VALUE;
113+
fixture.detectChanges();
114+
115+
// Confirm get analysis protocols returns a string array
116+
const getAnalysisProtocols = component.getAnalysisProtocols();
117+
expect(getAnalysisProtocols).toEqual([TEST_VALUE_OPTIMUS]);
118+
});
119+
120+
/**
121+
* Confirm get pipeline link returns corresponding data portal optimus link when analysis protocol includes "optimus".
122+
*/
123+
it(`should get pipeline link returns corresponding data portal optimus link when analysis protocol includes "optimus"`, () => {
124+
125+
// Confirm link is for data portal optimus
126+
const getPipelineLink = component.getPipelineLink(TEST_VALUE_OPTIMUS);
127+
expect(getPipelineLink).toEqual(testConfig.getPortalURL() + TEMPLATE_VALUE_DATA_PORTAL_LINK_OPTIMUS);
128+
});
129+
130+
/**
131+
* Confirm get pipeline link returns corresponding data portal smartseq2 link when analysis protocol includes "smartseq2".
132+
*/
133+
it(`should get pipeline link returns corresponding data portal smartseq2 link when analysis protocol includes "smartseq2"`, () => {
134+
135+
// Confirm link is for data portal smartseq2
136+
const getPipelineLink = component.getPipelineLink(TEST_VALUE_SMARTSEQ2);
137+
expect(getPipelineLink).toEqual(testConfig.getPortalURL() + TEMPLATE_VALUE_DATA_PORTAL_LINK_SMARTSEQ2);
138+
});
139+
140+
/**
141+
* Confirm get pipeline link returns the root base path when there is no corresponding pipeline.
142+
*/
143+
it("should get pipeline link returns the root base path when there is no corresponding pipeline", () => {
144+
145+
// Confirm no link is returned
146+
const getPipelineLink = component.getPipelineLink(TEST_VALUE_CELL_RANGER);
147+
expect(getPipelineLink).toEqual("/");
148+
});
149+
150+
/**
151+
* Confirm is analysis protocol linked returns true when analysis protocol includes "optimus".
152+
*/
153+
it(`should is analysis protocol linked returns true when analysis protocol includes "optimus"`, () => {
154+
155+
// Confirm true is returned
156+
const isAnalysisProtocolLinked = component.isAnalysisProtocolLinked(TEST_VALUE_OPTIMUS);
157+
expect(isAnalysisProtocolLinked).toEqual(true);
158+
});
159+
160+
/**
161+
* Confirm is analysis protocol linked returns true when analysis protocol includes "smartseq2".
162+
*/
163+
it(`should is analysis protocol linked returns true when analysis protocol includes "smartseq2"`, () => {
164+
165+
// Confirm true is returned
166+
const isAnalysisProtocolLinked = component.isAnalysisProtocolLinked(TEST_VALUE_SMARTSEQ2);
167+
expect(isAnalysisProtocolLinked).toEqual(true);
168+
});
169+
170+
/**
171+
* Confirm is analysis protocol linked returns false when analysis protocol neither includes "smartseq2" nor "optimus".
172+
*/
173+
it(`should is analysis protocol linked returns true when analysis protocol neither includes "smartseq2" nor "optimus"`, () => {
174+
175+
// Confirm true is returned
176+
const isAnalysisProtocolLinked = component.isAnalysisProtocolLinked(TEST_VALUE_CELL_RANGER);
177+
expect(isAnalysisProtocolLinked).toEqual(false);
178+
});
179+
180+
/**
181+
* Confirm all unlinked analysis protocols are displayed, when multiple unlinked workflow.
182+
*/
183+
it("should display all unlinked analysis protocols when multiple unlinked workflow", () => {
184+
185+
// Set up initial component state
186+
component.workflow = INPUT_VALUE_WORKFLOW_MULTIPLE_UNLINKED_VALUES;
187+
188+
fixture.detectChanges();
189+
190+
// Confirm all protocols are displayed
191+
expect(getAnalysisProtocolsDisplayed("span").length).toEqual(3);
192+
});
193+
194+
/**
195+
* Confirm all linked analysis protocols are displayed, when multiple linked workflow.
196+
*/
197+
it("should display all linked analysis protocols when multiple linked workflow", () => {
198+
199+
// Set up initial component state
200+
component.workflow = INPUT_VALUE_WORKFLOW_MULTIPLE_LINKED_VALUES;
201+
202+
fixture.detectChanges();
203+
204+
// Confirm all protocols are displayed
205+
expect(getAnalysisProtocolsDisplayed("a").length).toEqual(3);
206+
});
207+
208+
/**
209+
* Confirm all linked analysis protocols are displayed, when multiple mixed workflow.
210+
*/
211+
it("should display all linked analysis protocols when multiple mixed workflow", () => {
212+
213+
// Set up initial component state
214+
component.workflow = INPUT_VALUE_WORKFLOW_MULTIPLE_MIXED_VALUES;
215+
216+
fixture.detectChanges();
217+
218+
// Confirm linked protocols are displayed
219+
expect(getAnalysisProtocolsDisplayed("a").length).toEqual(2);
220+
});
221+
222+
/**
223+
* Confirm all unlinked analysis protocols are displayed, when multiple mixed workflow.
224+
*/
225+
it("should display all unlinked analysis protocols when multiple mixed workflow", () => {
226+
227+
// Set up initial component state
228+
component.workflow = INPUT_VALUE_WORKFLOW_MULTIPLE_MIXED_VALUES;
229+
230+
fixture.detectChanges();
231+
232+
// Confirm unlinked protocols are displayed
233+
expect(getAnalysisProtocolsDisplayed("span").length).toEqual(1);
234+
});
235+
236+
/**
237+
* Confirm no analysis protocols are displayed, when workflow is empty.
238+
*/
239+
it("should display no analysis protocols when workflow is empty", () => {
240+
241+
// Set up initial component state
242+
component.workflow = "";
243+
244+
fixture.detectChanges();
245+
246+
// Confirm no protocols are displayed
247+
expect(getAnalysisProtocolsDisplayed("span").length).toEqual(0);
248+
expect(getAnalysisProtocolsDisplayed("a").length).toEqual(0);
249+
});
250+
251+
/**
252+
* Confirm data portal optimus pipeline link is added to href attribute when workflow includes "optimus".
253+
*/
254+
it(`should data portal optimus pipeline link is added to href attribute when workflow includes "optimus"`, () => {
255+
256+
// Set up initial component state
257+
component.workflow = INPUT_VALUE_WORKFLOW_SINGLE_OPTIMUS_VALUE;
258+
259+
fixture.detectChanges();
260+
261+
const analysisPortalDEs = getAnalysisProtocolsDisplayed("a");
262+
263+
// Confirm href link
264+
expect(getHrefValue(analysisPortalDEs[0])).toEqual(testConfig.getPortalURL() + TEMPLATE_VALUE_DATA_PORTAL_LINK_OPTIMUS);
265+
});
266+
267+
/**
268+
* Confirm data portal smartseq2 pipeline link is added to href attribute when workflow includes "smartseq2".
269+
*/
270+
it(`should data portal smartseq2 pipeline link is added to href attribute when workflow includes "smartseq2"`, () => {
271+
272+
// Set up initial component state
273+
component.workflow = INPUT_VALUE_WORKFLOW_SINGLE_SMARTSEQ2_VALUE;
274+
275+
fixture.detectChanges();
276+
277+
const analysisPortalDEs = getAnalysisProtocolsDisplayed("a");
278+
279+
// Confirm href link
280+
expect(getHrefValue(analysisPortalDEs[0])).toEqual(testConfig.getPortalURL() + TEMPLATE_VALUE_DATA_PORTAL_LINK_SMARTSEQ2);
281+
});
282+
283+
/**
284+
* Confirm comma is not displayed last when multiple workflow.
285+
*/
286+
it("should not display comma last when multiple workflow", () => {
287+
288+
// Set up initial component state
289+
component.workflow = INPUT_VALUE_WORKFLOW_MULTIPLE_MIXED_VALUES;
290+
291+
fixture.detectChanges();
292+
293+
const componentChildren = fixture.debugElement.children;
294+
const componentChildLast = componentChildren[componentChildren.length - 1];
295+
296+
// Confirm comma is not displayed last
297+
expect(componentChildLast.children.length).toEqual(0);
298+
});
299+
300+
/**
301+
* Returns all analysis protocols displayed, specified by class name.
302+
*
303+
* @param {string} className
304+
* @returns {DebugElement[]}
305+
*/
306+
function getAnalysisProtocolsDisplayed(className: string): DebugElement[] {
307+
308+
const debugElements = getDebugElements(className);
309+
310+
if ( !debugElements ) {
311+
312+
return;
313+
}
314+
315+
return debugElements.filter(spanDE => spanDE.nativeElement.innerHTML !== ", ");
316+
}
317+
318+
/**
319+
* Returns the href attribute value for the specified debug element.
320+
*
321+
* @param {DebugElement} de
322+
* @returns {string}
323+
*/
324+
function getHrefValue(de: DebugElement): string {
325+
326+
if ( !de ) {
327+
328+
return;
329+
}
330+
331+
return de.properties.href;
332+
}
333+
334+
/**
335+
* Returns all debug elements specified by class name.
336+
*
337+
* @param {string} className
338+
* @returns {DebugElement[]}
339+
*/
340+
function getDebugElements(className: string): DebugElement[] {
341+
342+
return fixture.debugElement.queryAll(By.css(className));
343+
}
344+
});

0 commit comments

Comments
 (0)