From 406e738a24aeb8e24bd19e73d9bfceab1350ee0d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 28 Mar 2024 07:35:14 +0000 Subject: [PATCH 01/30] Bump express from 4.18.1 to 4.19.2 Bumps [express](https://github.com/expressjs/express) from 4.18.1 to 4.19.2. - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/master/History.md) - [Commits](https://github.com/expressjs/express/compare/4.18.1...4.19.2) --- updated-dependencies: - dependency-name: express dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- package-lock.json | 110 +++++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/package-lock.json b/package-lock.json index 977ce37f..146fd0e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,7 +29,7 @@ "dc": "^4.2.7", "debug": "^4.3.4", "dom-to-image-more": "^2.10.1", - "express": "^4.18.1", + "express": "^4.19.2", "file-saver": "^2.0.5", "helmet": "^5.1.0", "lodash": "^4.17.21", @@ -6948,20 +6948,20 @@ "dev": true }, "node_modules/body-parser": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", - "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dependencies": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.10.3", - "raw-body": "2.5.1", + "qs": "6.11.0", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -7003,9 +7003,9 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/body-parser/node_modules/qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dependencies": { "side-channel": "^1.0.4" }, @@ -8020,9 +8020,9 @@ ] }, "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "engines": { "node": ">= 0.6" } @@ -8037,9 +8037,9 @@ } }, "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "engines": { "node": ">= 0.6" } @@ -11225,16 +11225,16 @@ } }, "node_modules/express": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", - "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.0", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -11250,7 +11250,7 @@ "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", - "qs": "6.10.3", + "qs": "6.11.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.18.0", @@ -11279,9 +11279,9 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/express/node_modules/qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dependencies": { "side-channel": "^1.0.4" }, @@ -17978,9 +17978,9 @@ } }, "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -27891,20 +27891,20 @@ "dev": true }, "body-parser": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", - "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "requires": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.10.3", - "raw-body": "2.5.1", + "qs": "6.11.0", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -27936,9 +27936,9 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "requires": { "side-channel": "^1.0.4" } @@ -28720,9 +28720,9 @@ } }, "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" }, "convert-source-map": { "version": "1.8.0", @@ -28734,9 +28734,9 @@ } }, "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==" }, "cookie-signature": { "version": "1.0.6", @@ -31127,16 +31127,16 @@ } }, "express": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", - "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.0", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -31152,7 +31152,7 @@ "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", - "qs": "6.10.3", + "qs": "6.11.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.18.0", @@ -31178,9 +31178,9 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "requires": { "side-channel": "^1.0.4" } @@ -36187,9 +36187,9 @@ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "requires": { "bytes": "3.1.2", "http-errors": "2.0.0", diff --git a/package.json b/package.json index ff5d75aa..630d4b0d 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "dc": "^4.2.7", "debug": "^4.3.4", "dom-to-image-more": "^2.10.1", - "express": "^4.18.1", + "express": "^4.19.2", "file-saver": "^2.0.5", "helmet": "^5.1.0", "lodash": "^4.17.21", From 94cc375a34a6e69c5f9fe18ecd0dc160618aefde Mon Sep 17 00:00:00 2001 From: sagely1 <114952739+sagely1@users.noreply.github.com> Date: Tue, 2 Apr 2024 12:49:10 -0700 Subject: [PATCH 02/30] AG-1412 fixed bug in border condition handling logic --- .../gene-comparison-tool.component.spec.ts | 20 +++++++++++++++++++ .../gene-comparison-tool.component.ts | 5 +++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.spec.ts b/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.spec.ts index ace93c52..add7e812 100644 --- a/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.spec.ts +++ b/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.spec.ts @@ -27,6 +27,7 @@ import { ApiService, HelperService } from '../../../../core/services'; import { GeneService } from '../../../../features/genes/services'; import { routes } from '../../../../app.routing'; import { comparisonGeneEmptyHGNCMock, comparisonGeneMock1, comparisonGeneMock2 } from '../../../../testing'; +import { GCTGeneTissue } from '../../../../models'; const DEFAULT_SIGNIFICANCE_THRESHOLD = 0.05; @@ -518,5 +519,24 @@ describe('Component: GeneComparisonToolComponent', () => { const expected2 = 'ENSG00000147065'; expect(label2).toBe(expected2); }); + + it('should set circle size to zero for null/undefined pValues', () => { + let tissue: GCTGeneTissue | undefined; + // null/undefined values should be zero + const result = component.getCircleSize(tissue?.adj_p_val); + expect(result).toBe(0); + }); + + it('should set circle size for pValues within acceptable ranges', () => { + let expectedSizeInPixels = 0; + let pValue = 0.5; + let result = component.getCircleSize(pValue); + expect(result).toBe(expectedSizeInPixels); + + expectedSizeInPixels = 33; + pValue = 0.04; + result = component.getCircleSize(pValue); + expect(result).toBe(expectedSizeInPixels); + }); }); }); diff --git a/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.ts b/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.ts index 47e3fd2f..3882c817 100644 --- a/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.ts +++ b/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.ts @@ -906,8 +906,9 @@ export class GeneComparisonToolComponent implements OnInit, AVI, OnDestroy { const MIN_SIZE = 6; const MAX_SIZE = 50; - // shouldn't be undefined but if it is, don't show a circle - if (pval === undefined) + // pval shouldn't be undefined but if it is, don't show a circle + // null means there is no data in which case, also don't show a circle + if (pval === null || pval === undefined) return 0; // if significance cutoff radio button selected and From 9604783c9f2501de7a34ed79d5f3de5a0703e342 Mon Sep 17 00:00:00 2001 From: sagely1 <114952739+sagely1@users.noreply.github.com> Date: Wed, 3 Apr 2024 13:59:54 -0700 Subject: [PATCH 03/30] AG-1412 since getSignificantFigures() can return emdash, need to use innerHTML --- ...gene-comparison-tool-details-panel.component.html | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/app/features/genes/components/gene-comparison-tool/components/gene-comparison-tool-details-panel/gene-comparison-tool-details-panel.component.html b/src/app/features/genes/components/gene-comparison-tool/components/gene-comparison-tool-details-panel/gene-comparison-tool-details-panel.component.html index ce33000e..7bf0eb02 100644 --- a/src/app/features/genes/components/gene-comparison-tool/components/gene-comparison-tool-details-panel/gene-comparison-tool-details-panel.component.html +++ b/src/app/features/genes/components/gene-comparison-tool/components/gene-comparison-tool-details-panel/gene-comparison-tool-details-panel.component.html @@ -18,10 +18,8 @@
P-Value
-
{{ getSignificantFigures(data.value, 3) }}
-
- {{ getSignificantFigures(data.pValue, 3) }} -
+
+
@@ -36,17 +34,17 @@ [style]="getIntervalPositions(data)" >
-
{{ getSignificantFigures(data.intervalMin, 3) }}
+
-
{{ getSignificantFigures(data.intervalMax, 3) }}
+
-
{{ getSignificantFigures(data.value, 3) }}
+
From 3b28333577dddbfc530c4b10e1c464da71e8eac2 Mon Sep 17 00:00:00 2001 From: sagely1 <114952739+sagely1@users.noreply.github.com> Date: Wed, 3 Apr 2024 17:58:20 -0700 Subject: [PATCH 04/30] AG-1412 update unit tests to test null/undefined pValues for getCircleSize() --- .../gene-comparison-tool.component.spec.ts | 11 +++++++++-- .../gene-comparison-tool.component.ts | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.spec.ts b/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.spec.ts index add7e812..ff31aab3 100644 --- a/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.spec.ts +++ b/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.spec.ts @@ -520,13 +520,20 @@ describe('Component: GeneComparisonToolComponent', () => { expect(label2).toBe(expected2); }); - it('should set circle size to zero for null/undefined pValues', () => { + it('should set circle size to zero for undefined pValues', () => { let tissue: GCTGeneTissue | undefined; - // null/undefined values should be zero + // undefined values should result in a circle size of zero + expect(tissue).toBeUndefined(); const result = component.getCircleSize(tissue?.adj_p_val); expect(result).toBe(0); }); + it('should set circle size to zero for null pValues', () => { + // null values should result in a circle size of zero pixels + const result = component.getCircleSize(null); + expect(result).toBe(0); + }); + it('should set circle size for pValues within acceptable ranges', () => { let expectedSizeInPixels = 0; let pValue = 0.5; diff --git a/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.ts b/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.ts index 3882c817..00856bcd 100644 --- a/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.ts +++ b/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.ts @@ -901,7 +901,7 @@ export class GeneComparisonToolComponent implements OnInit, AVI, OnDestroy { } } - getCircleSize(pval: number | undefined) { + getCircleSize(pval: number | null | undefined) { // define min and max size of possible circles in pixels const MIN_SIZE = 6; const MAX_SIZE = 50; From 1467ca8123255f62461435af7476537528e81a77 Mon Sep 17 00:00:00 2001 From: sagely1 <114952739+sagely1@users.noreply.github.com> Date: Wed, 3 Apr 2024 18:03:07 -0700 Subject: [PATCH 05/30] AG-1412 update unit test for clarity --- .../gene-comparison-tool.component.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.spec.ts b/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.spec.ts index ff31aab3..bc3b7f8f 100644 --- a/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.spec.ts +++ b/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.spec.ts @@ -530,7 +530,8 @@ describe('Component: GeneComparisonToolComponent', () => { it('should set circle size to zero for null pValues', () => { // null values should result in a circle size of zero pixels - const result = component.getCircleSize(null); + const pValue = null; + const result = component.getCircleSize(pValue); expect(result).toBe(0); }); From bc2612a39422bfdbc2aa1e828c39280f4f5c137b Mon Sep 17 00:00:00 2001 From: sagely1 <114952739+sagely1@users.noreply.github.com> Date: Fri, 5 Apr 2024 13:33:15 -0700 Subject: [PATCH 06/30] AG-1412 using unicode character instead of html escape sequence --- ...mparison-tool-details-panel.component.html | 20 ++++++++++++++----- ...comparison-tool-details-panel.component.ts | 2 +- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/app/features/genes/components/gene-comparison-tool/components/gene-comparison-tool-details-panel/gene-comparison-tool-details-panel.component.html b/src/app/features/genes/components/gene-comparison-tool/components/gene-comparison-tool-details-panel/gene-comparison-tool-details-panel.component.html index 7bf0eb02..c81ac286 100644 --- a/src/app/features/genes/components/gene-comparison-tool/components/gene-comparison-tool-details-panel/gene-comparison-tool-details-panel.component.html +++ b/src/app/features/genes/components/gene-comparison-tool/components/gene-comparison-tool-details-panel/gene-comparison-tool-details-panel.component.html @@ -18,8 +18,12 @@
P-Value
-
-
+
+ {{ getSignificantFigures(data.value, 3) }} +
+
+ {{ getSignificantFigures(data.pValue, 3) }} +
@@ -34,17 +38,23 @@ [style]="getIntervalPositions(data)" >
-
+
+ {{ getSignificantFigures(data.intervalMin, 3) }} +
-
+
+ {{ getSignificantFigures(data.intervalMax, 3) }} +
-
+
+ {{ getSignificantFigures(data.value, 3) }} +
diff --git a/src/app/features/genes/components/gene-comparison-tool/components/gene-comparison-tool-details-panel/gene-comparison-tool-details-panel.component.ts b/src/app/features/genes/components/gene-comparison-tool/components/gene-comparison-tool-details-panel/gene-comparison-tool-details-panel.component.ts index c3eb7e98..daec3512 100644 --- a/src/app/features/genes/components/gene-comparison-tool/components/gene-comparison-tool-details-panel/gene-comparison-tool-details-panel.component.ts +++ b/src/app/features/genes/components/gene-comparison-tool/components/gene-comparison-tool-details-panel/gene-comparison-tool-details-panel.component.ts @@ -96,7 +96,7 @@ export class GeneComparisonToolDetailsPanelComponent { } getSignificantFigures(n: any, b: any) { - const emdash = '—'; // Shift+Option+Hyphen + const emdash = '\u2014'; // Shift+Option+Hyphen if (n === null || n === undefined) return emdash; return this.helperService.getSignificantFigures(n, b); From a21839bcf68f11187b902ddb5ff245c2aa1f8f4d Mon Sep 17 00:00:00 2001 From: sagely1 <114952739+sagely1@users.noreply.github.com> Date: Tue, 16 Apr 2024 14:01:15 -0700 Subject: [PATCH 07/30] AG-1399 fix invalid ensg in url from hanging --- src/app/core/services/api.service.ts | 2 +- .../gene-details/gene-details.component.ts | 65 ++++++++++--------- .../gene-similar/gene-similar.component.ts | 8 ++- .../features/genes/services/gene.service.ts | 8 ++- src/app/testing/api-service-stub.ts | 2 +- src/app/testing/gene-service-stub.ts | 2 +- 6 files changed, 48 insertions(+), 39 deletions(-) diff --git a/src/app/core/services/api.service.ts b/src/app/core/services/api.service.ts index 401098ef..0420af61 100644 --- a/src/app/core/services/api.service.ts +++ b/src/app/core/services/api.service.ts @@ -50,7 +50,7 @@ export class ApiService { return url; } - getGene(id: string): Observable { + getGene(id: string): Observable { return this.http.get(this.getBaseUrl() + '/api/genes/' + id, { headers: new HttpHeaders(defaultHeaders), }); diff --git a/src/app/features/genes/components/gene-details/gene-details.component.ts b/src/app/features/genes/components/gene-details/gene-details.component.ts index d4b03a22..a37476d9 100644 --- a/src/app/features/genes/components/gene-details/gene-details.component.ts +++ b/src/app/features/genes/components/gene-details/gene-details.component.ts @@ -135,40 +135,45 @@ export class GeneDetailsComponent implements OnInit, AfterViewInit { if (params.get('id')) { this.geneService .getGene(params.get('id') as string) - .subscribe((gene: Gene) => { - this.gene = gene; - - this.panels.forEach((p: Panel) => { - if (p.name == 'nominations' && !this.gene?.total_nominations) { - p.disabled = true; - } else if ( - p.name == 'experimental-validation' && - !this.gene?.experimental_validation?.length - ) { - p.disabled = true; - } else { - p.disabled = false; + .subscribe((gene) => { + if (!gene) { + this.helperService.setLoading(false); + this.router.navigateByUrl('/404-not-found', { skipLocationChange: true }); + } else { + this.gene = gene; + + this.panels.forEach((p: Panel) => { + if (p.name == 'nominations' && !this.gene?.total_nominations) { + p.disabled = true; + } else if ( + p.name == 'experimental-validation' && + !this.gene?.experimental_validation?.length + ) { + p.disabled = true; + } else { + p.disabled = false; + } + }); + + const nominationsPanel = this.panels.find( + (p) => p.name == 'nominations' + ); + if (nominationsPanel) { + nominationsPanel.disabled = !this.gene.total_nominations ? true : false; } - }); - const nominationsPanel = this.panels.find( - (p) => p.name == 'nominations' - ); - if (nominationsPanel) { - nominationsPanel.disabled = !this.gene.total_nominations ? true : false; - } + const experimentalValidationPanel = this.panels.find( + (p) => p.name == 'experimental-validation' + ); + if (experimentalValidationPanel) { + experimentalValidationPanel.disabled = !this.gene + .experimental_validation?.length + ? true + : false; + } - const experimentalValidationPanel = this.panels.find( - (p) => p.name == 'experimental-validation' - ); - if (experimentalValidationPanel) { - experimentalValidationPanel.disabled = !this.gene - .experimental_validation?.length - ? true - : false; + this.helperService.setLoading(false); } - - this.helperService.setLoading(false); }); } diff --git a/src/app/features/genes/components/gene-similar/gene-similar.component.ts b/src/app/features/genes/components/gene-similar/gene-similar.component.ts index 8414a5ad..0e0c01d8 100644 --- a/src/app/features/genes/components/gene-similar/gene-similar.component.ts +++ b/src/app/features/genes/components/gene-similar/gene-similar.component.ts @@ -69,9 +69,11 @@ export class GeneSimilarComponent implements OnInit { this.helperService.setLoading(true); this.geneService .getGene(params.get('id') as string) - .subscribe((gene: Gene) => { - this.gene = gene; - this.init(); + .subscribe((gene: Gene | null) => { + if (gene) { + this.gene = gene; + this.init(); + } }); } }); diff --git a/src/app/features/genes/services/gene.service.ts b/src/app/features/genes/services/gene.service.ts index 01889bff..d6ffc12e 100644 --- a/src/app/features/genes/services/gene.service.ts +++ b/src/app/features/genes/services/gene.service.ts @@ -27,13 +27,15 @@ export class GeneService { // ------------------------------------------------------------------------ // - getGene(id: string): Observable { + getGene(id: string): Observable { if (this.genes[id]) { return of(this.genes[id]); } - + return this.apiService.getGene(id).pipe( - map((gene: Gene) => { + map((gene: Gene | null) => { + if (!gene) + return null; gene.similar_genes_network = this.getSimilarGenesNetwork(gene); return (this.genes[id] = gene); }) diff --git a/src/app/testing/api-service-stub.ts b/src/app/testing/api-service-stub.ts index cc94237d..a7491cb5 100644 --- a/src/app/testing/api-service-stub.ts +++ b/src/app/testing/api-service-stub.ts @@ -20,7 +20,7 @@ import { @Injectable() export class ApiServiceStub { - getGene(id: string): Observable { + getGene(id: string): Observable { return of(geneMock1); } diff --git a/src/app/testing/gene-service-stub.ts b/src/app/testing/gene-service-stub.ts index c70dbf18..295b37bd 100644 --- a/src/app/testing/gene-service-stub.ts +++ b/src/app/testing/gene-service-stub.ts @@ -30,7 +30,7 @@ export class GeneServiceStub { this.geneService = new GeneService(new ApiServiceStub() as ApiService); } - getGene(id: string): Observable { + getGene(id: string): Observable { return this.geneService.getGene(id); } From 19d8b66cce5b483f8a9d539570183edd467ef46d Mon Sep 17 00:00:00 2001 From: sagely1 <114952739+sagely1@users.noreply.github.com> Date: Tue, 16 Apr 2024 14:17:58 -0700 Subject: [PATCH 08/30] AG-1399 adding e2e test for 404 redirect --- tests/gene-comparison-tool.spec.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/gene-comparison-tool.spec.ts b/tests/gene-comparison-tool.spec.ts index f203600a..551e701d 100644 --- a/tests/gene-comparison-tool.spec.ts +++ b/tests/gene-comparison-tool.spec.ts @@ -27,4 +27,15 @@ test.describe('specific viewport block', () => { const dropdown = page.locator('#subCategory'); await expect(dropdown).toHaveText('Targeted Selected Reaction Monitoring (SRM)'); }); + + test('invalid gene results in a 404 redirect', async ({ page }) => { + // go to invalid ENSG page + await page.goto('/genes/ENSG00000000000'); + + // expect a title "to contain" a substring. + await expect(page).toHaveTitle('Agora'); + + // expect div for page not found content to be visible + expect(page.locator('.page-not-found')).toBeVisible(); + }); }); From d6e6949cb5f768d24c7b4f717dc3bddbd36cb0a6 Mon Sep 17 00:00:00 2001 From: sagely1 <114952739+sagely1@users.noreply.github.com> Date: Wed, 17 Apr 2024 10:47:11 -0700 Subject: [PATCH 09/30] AG-1399 fixing invalid ENSG for /similar route & moving e2e tests --- .../gene-similar/gene-similar.component.ts | 5 ++++- tests/gene-comparison-tool.spec.ts | 11 ----------- tests/gene-details.spec.ts | 17 +++++++++++++++++ tests/gene-similar.spec.ts | 17 +++++++++++++++++ 4 files changed, 38 insertions(+), 12 deletions(-) create mode 100644 tests/gene-details.spec.ts create mode 100644 tests/gene-similar.spec.ts diff --git a/src/app/features/genes/components/gene-similar/gene-similar.component.ts b/src/app/features/genes/components/gene-similar/gene-similar.component.ts index 0e0c01d8..a8be4ea3 100644 --- a/src/app/features/genes/components/gene-similar/gene-similar.component.ts +++ b/src/app/features/genes/components/gene-similar/gene-similar.component.ts @@ -70,7 +70,10 @@ export class GeneSimilarComponent implements OnInit { this.geneService .getGene(params.get('id') as string) .subscribe((gene: Gene | null) => { - if (gene) { + if (!gene) { + this.helperService.setLoading(false); + this.router.navigateByUrl('/404-not-found', { skipLocationChange: true }); + } else { this.gene = gene; this.init(); } diff --git a/tests/gene-comparison-tool.spec.ts b/tests/gene-comparison-tool.spec.ts index 551e701d..f203600a 100644 --- a/tests/gene-comparison-tool.spec.ts +++ b/tests/gene-comparison-tool.spec.ts @@ -27,15 +27,4 @@ test.describe('specific viewport block', () => { const dropdown = page.locator('#subCategory'); await expect(dropdown).toHaveText('Targeted Selected Reaction Monitoring (SRM)'); }); - - test('invalid gene results in a 404 redirect', async ({ page }) => { - // go to invalid ENSG page - await page.goto('/genes/ENSG00000000000'); - - // expect a title "to contain" a substring. - await expect(page).toHaveTitle('Agora'); - - // expect div for page not found content to be visible - expect(page.locator('.page-not-found')).toBeVisible(); - }); }); diff --git a/tests/gene-details.spec.ts b/tests/gene-details.spec.ts new file mode 100644 index 00000000..31883aac --- /dev/null +++ b/tests/gene-details.spec.ts @@ -0,0 +1,17 @@ +import { test, expect } from '@playwright/test'; + +test.describe('specific viewport block', () => { + test.slow(); + test.use({ viewport: { width: 1600, height: 1200 } }); + + test('invalid gene results in a 404 redirect', async ({ page }) => { + // go to invalid ENSG page + await page.goto('/genes/ENSG00000000000'); + + // expect a title "to contain" a substring. + await expect(page).toHaveTitle('Agora'); + + // expect div for page not found content to be visible + expect(page.locator('.page-not-found')).toBeVisible(); + }); +}); \ No newline at end of file diff --git a/tests/gene-similar.spec.ts b/tests/gene-similar.spec.ts new file mode 100644 index 00000000..29341bb4 --- /dev/null +++ b/tests/gene-similar.spec.ts @@ -0,0 +1,17 @@ +import { test, expect } from '@playwright/test'; + +test.describe('specific viewport block', () => { + test.slow(); + test.use({ viewport: { width: 1600, height: 1200 } }); + + test('invalid gene results in a 404 redirect', async ({ page }) => { + // go to invalid ENSG page + await page.goto('/genes/ENSG00000000000/similar'); + + // expect a title "to contain" a substring. + await expect(page).toHaveTitle('Agora'); + + // expect div for page not found content to be visible + expect(page.locator('.page-not-found')).toBeVisible(); + }); +}); \ No newline at end of file From a4ee784e800f2b50fa732b8b57944dbbfc826555 Mon Sep 17 00:00:00 2001 From: Hallie Swan <26949006+hallieswan@users.noreply.github.com> Date: Wed, 24 Apr 2024 10:43:22 -0700 Subject: [PATCH 10/30] AG-1410: change position so tooltip is displayed in firefox --- .../gene-comparison-tool/gene-comparison-tool.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.html b/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.html index bdb7fc11..c5c3d230 100644 --- a/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.html +++ b/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.html @@ -402,7 +402,7 @@

Gene Comparison Tool

'). You must unpin some genes before you can pin more.' : 'Add all matching genes to Pinned list' " - tooltipPosition="top" + tooltipPosition="left" tooltipStyleClass="tooltip" > From 1b595d51d6107cb4244a37a5f94464b48a161f09 Mon Sep 17 00:00:00 2001 From: Hallie Swan <26949006+hallieswan@users.noreply.github.com> Date: Wed, 24 Apr 2024 14:54:37 -0700 Subject: [PATCH 11/30] AG-1407: default to sorting similar genes table by hgnc_symbol ascending --- .../gene-similar/gene-similar.component.html | 1 + .../gene-similar.component.spec.ts | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/app/features/genes/components/gene-similar/gene-similar.component.html b/src/app/features/genes/components/gene-similar/gene-similar.component.html index 6fd7c42c..3fa20da2 100644 --- a/src/app/features/genes/components/gene-similar/gene-similar.component.html +++ b/src/app/features/genes/components/gene-similar/gene-similar.component.html @@ -32,6 +32,7 @@

Similar Genes

[genes]="genes" [columns]="tableColumns" sortField="hgnc_symbol" + [sortOrder]="1" heading="Similar Genes" [gctLink]="true" gctLinkTooltip="Use Agora's Gene Comparison Tool to compare similar genes in this list" diff --git a/src/app/features/genes/components/gene-similar/gene-similar.component.spec.ts b/src/app/features/genes/components/gene-similar/gene-similar.component.spec.ts index a5fc1649..66a39378 100644 --- a/src/app/features/genes/components/gene-similar/gene-similar.component.spec.ts +++ b/src/app/features/genes/components/gene-similar/gene-similar.component.spec.ts @@ -11,6 +11,7 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { GeneSimilarComponent } from './'; import { GeneService } from '../../services'; import { ApiService, HelperService } from '../../../../core/services'; +import { geneMock1, geneMock2, geneMock3 } from '../../../../testing'; // -------------------------------------------------------------------------- // // Tests @@ -18,6 +19,7 @@ import { ApiService, HelperService } from '../../../../core/services'; describe('Component: Gene Similar', () => { let fixture: ComponentFixture; let component: GeneSimilarComponent; + let element: HTMLElement; beforeEach(async () => { await TestBed.configureTestingModule({ @@ -31,9 +33,33 @@ describe('Component: Gene Similar', () => { fixture = TestBed.createComponent(GeneSimilarComponent); component = fixture.componentInstance; fixture.detectChanges(); + element = fixture.nativeElement; }); it('should create', () => { expect(component).toBeTruthy(); }); + + it('should order by hgnc-symbol ascending', () => { + const expectedHgnc = [ + geneMock1.hgnc_symbol, + geneMock2.hgnc_symbol, + geneMock3.hgnc_symbol, + ]; + expectedHgnc.sort(); + + component.genes = [geneMock1, geneMock2, geneMock3]; + fixture.detectChanges(); + + const table = element.querySelector('.similar-gene-table'); + expect(table).not.toBeNull(); + + const rows = Array.from( + table?.querySelectorAll('tbody tr') || [] + ) as HTMLTableRowElement[]; + expect(rows.length).toBe(3); + + const actualHgnc = rows.map(row => row.cells[0].textContent?.trim()); + expect(actualHgnc).toEqual(expectedHgnc); + }); }); From c613d04cfc043c3be45e0abcde40e4a7e1f6207d Mon Sep 17 00:00:00 2001 From: Hallie Swan <26949006+hallieswan@users.noreply.github.com> Date: Thu, 25 Apr 2024 10:26:40 -0700 Subject: [PATCH 12/30] AG-1328: drop null study values from display string and include all values from comma separated strings --- .../gene-nominated-targets.component.spec.ts | 110 +++++++++++++++++- .../gene-nominated-targets.component.ts | 83 +++++-------- src/app/models/genes.ts | 2 +- src/app/testing/gene-mocks.ts | 20 +++- 4 files changed, 158 insertions(+), 57 deletions(-) diff --git a/src/app/features/genes/components/gene-nominated-targets/gene-nominated-targets.component.spec.ts b/src/app/features/genes/components/gene-nominated-targets/gene-nominated-targets.component.spec.ts index 5cdd541e..927e67c1 100644 --- a/src/app/features/genes/components/gene-nominated-targets/gene-nominated-targets.component.spec.ts +++ b/src/app/features/genes/components/gene-nominated-targets/gene-nominated-targets.component.spec.ts @@ -10,6 +10,9 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; // -------------------------------------------------------------------------- // import { GeneNominatedTargetsComponent } from './'; import { ApiService, HelperService } from '../../../../core/services'; +import { Gene, GenesResponse } from '../../../../models'; +import { geneMock1, targetNominationMock1 } from '../../../../testing/gene-mocks'; +import { of } from 'rxjs'; // -------------------------------------------------------------------------- // // Tests @@ -17,6 +20,12 @@ import { ApiService, HelperService } from '../../../../core/services'; describe('Component: Gene Nominated Targets', () => { let fixture: ComponentFixture; let component: GeneNominatedTargetsComponent; + let element: HTMLElement; + let mockApiService: ApiService; + + const COLUMN_INDICES = { + 'cohort_study': 4 + }; beforeEach(async () => { await TestBed.configureTestingModule({ @@ -29,10 +38,109 @@ describe('Component: Gene Nominated Targets', () => { beforeEach(async () => { fixture = TestBed.createComponent(GeneNominatedTargetsComponent); component = fixture.componentInstance; - fixture.detectChanges(); }); + const setUp = (genes: Gene[]) => { + const genesResponse: GenesResponse = { + items: genes + }; + mockApiService = TestBed.inject(ApiService); + spyOn(mockApiService, 'getNominatedGenes').and.returnValue( + of(genesResponse) + ); + fixture.detectChanges(); + element = fixture.nativeElement; + + expect(mockApiService.getNominatedGenes).toHaveBeenCalled(); + + const table = element.querySelector('table'); + expect(table).not.toBeNull(); + + const rows = Array.from( + table?.querySelectorAll('tbody tr') || [] + ) as HTMLTableRowElement[]; + + return { rows }; + }; + it('should create', () => { + setUp([geneMock1]); expect(component).toBeTruthy(); }); + + it('should not show null study values', () => { + const gene: Gene = { + ...geneMock1, + target_nominations: [ + { ...targetNominationMock1, study: null }, + { ...targetNominationMock1, study: 'XYZ Study, ABC Study' }, + { ...targetNominationMock1, study: '' }, + { ...targetNominationMock1, study: 'Study 123, Study 456' }, + ], + }; + const { rows } = setUp([gene]); + expect(rows.length).toBe(1); + + const cols = rows[0].cells; + expect(cols.length).toBeGreaterThan(COLUMN_INDICES.cohort_study); + + expect(cols[COLUMN_INDICES.cohort_study].textContent?.trim()).toEqual( + 'ABC Study, Study 123, Study 456, XYZ Study' + ); + }); + + it('should display sorted, unique study values', () => { + const expectedStudyString = 'ACT, Banner, BLSA, Kronos, MSBB, ROSMAP'; + const { rows } = setUp([geneMock1]); + + expect(rows.length).toBe(1); + + const cols = rows[0].cells; + expect(cols.length).toBeGreaterThan(COLUMN_INDICES.cohort_study); + + expect(cols[COLUMN_INDICES.cohort_study].textContent?.trim()).toEqual( + expectedStudyString + ); + }); + + it('should correctly flatten comma separated arrays', () => { + setUp([]); + + expect(component.commaFlattenArray([])).toEqual([]); + + expect( + component.commaFlattenArray(['ACT, BLSA, Banner', 'ACT, BLSA, Banner']) + ).toEqual(['ACT', 'BLSA', 'Banner', 'ACT', 'BLSA', 'Banner']); + + expect(component.commaFlattenArray(['A, B, C', 'D', 'E, F, G, H'])).toEqual( + ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'] + ); + + expect(component.commaFlattenArray(['A', 'B', 'C'])).toEqual([ + 'A', + 'B', + 'C', + ]); + }); + + it('should correctly format display values', () => { + setUp([]); + + expect(component.formatDisplayValue([])).toEqual(''); + + expect( + component.formatDisplayValue([ + 'ACT', + 'BLSA', + 'Banner', + 'ACT', + 'BLSA', + 'Banner', + ]) + ).toEqual('ACT, Banner, BLSA'); + + expect( + component.formatDisplayValue(['Z', 'Y', 'X', 'A', 'B', 'C', 'B', 'C']) + ).toEqual('A, B, C, X, Y, Z'); + }); }); diff --git a/src/app/features/genes/components/gene-nominated-targets/gene-nominated-targets.component.ts b/src/app/features/genes/components/gene-nominated-targets/gene-nominated-targets.component.ts index befd6a8b..2106de7c 100644 --- a/src/app/features/genes/components/gene-nominated-targets/gene-nominated-targets.component.ts +++ b/src/app/features/genes/components/gene-nominated-targets/gene-nominated-targets.component.ts @@ -97,9 +97,9 @@ export class GeneNominatedTargetsComponent implements OnInit { // First map all entries nested in the data to a new array if (de.target_nominations?.length) { teamsArray = de.target_nominations.map((nt: TargetNomination) => nt.team); - studyArray = de.target_nominations.map( - (nt: TargetNomination) => nt.study - ); + studyArray = de.target_nominations + .map((nt: TargetNomination) => nt.study) + .filter((item) => Boolean(item)) as string[]; programsArray = de.target_nominations.map( (nt: TargetNomination) => nt.source ); @@ -120,37 +120,10 @@ export class GeneNominatedTargetsComponent implements OnInit { inputDataArray = this.commaFlattenArray(inputDataArray); // Populate targetNomination display fields - de.teams_display_value = ''; - if (teamsArray.length) { - de.teams_display_value = teamsArray - .filter(this.getUnique) - .sort((a: string, b: string) => a.localeCompare(b)) - .join(', '); - } - - de.study_display_value = ''; - if (teamsArray.length) { - de.study_display_value = studyArray - .filter(this.getUnique) - .sort((a: string, b: string) => a.localeCompare(b)) - .join(', '); - } - - de.programs_display_value = ''; - if (programsArray.length) { - de.programs_display_value = programsArray - .filter(this.getUnique) - .sort((a: string, b: string) => a.localeCompare(b)) - .join(', '); - } - - de.input_data_display_value = ''; - if (inputDataArray.length) { - de.input_data_display_value = inputDataArray - .filter(this.getUnique) - .sort((a: string, b: string) => a.localeCompare(b)) - .join(', '); - } + de.teams_display_value = this.formatDisplayValue(teamsArray); + de.study_display_value = this.formatDisplayValue(studyArray); + de.programs_display_value = this.formatDisplayValue(programsArray); + de.input_data_display_value = this.formatDisplayValue(inputDataArray); de.initial_nomination_display_value = initialNominationArray.length ? Math.min(...initialNominationArray) @@ -189,27 +162,29 @@ export class GeneNominatedTargetsComponent implements OnInit { return self.indexOf(value) === index; } - commaFlattenArray(array: any[]): any[] { - const finalArray: any[] = []; - if (array.length) { - array.forEach((t) => { - if (t) { - const i = t.indexOf(', '); - if (i > -1) { - const tmpArray = t.split(', '); - finalArray.push(tmpArray[0]); - finalArray.push(tmpArray[1]); - } else { - finalArray.push(t); - } - } else { - finalArray.push(''); - } - }); - array = finalArray; - } + commaFlattenArray(array: string[]): string[] { + const finalArray: string[] = []; + array.forEach((t) => { + const i = t.indexOf(', '); + if (i > -1) { + const tmpArray = t.split(', '); + tmpArray.forEach((val) => finalArray.push(val)); + } else { + finalArray.push(t); + } + }); + return finalArray; + } - return array; + formatDisplayValue(inputArray: string[]) { + let display_value = ''; + if (inputArray.length) { + display_value = inputArray + .filter(this.getUnique) + .sort((a: string, b: string) => a.localeCompare(b)) + .join(', '); + } + return display_value; } onSearch(event: any) { diff --git a/src/app/models/genes.ts b/src/app/models/genes.ts index 55f0f6ef..6f8e5939 100644 --- a/src/app/models/genes.ts +++ b/src/app/models/genes.ts @@ -19,7 +19,7 @@ export interface TargetNomination { predicted_therapeutic_direction: string; data_used_to_support_target_selection: string; data_synapseid: string; - study: string; + study: string | null; input_data: string; validation_study_details: string; initial_nomination: number; diff --git a/src/app/testing/gene-mocks.ts b/src/app/testing/gene-mocks.ts index e8062f86..c26bdea0 100644 --- a/src/app/testing/gene-mocks.ts +++ b/src/app/testing/gene-mocks.ts @@ -1,6 +1,24 @@ /* eslint-disable */ -import { Gene, GCTGene } from '../models'; +import { Gene, GCTGene, TargetNomination } from '../models'; + +export const targetNominationMock1: TargetNomination = { + source: 'Treat-AD', + team: 'Emory-Sage-SGC', + rank: '7', + hgnc_symbol: 'MSN', + target_choice_justification: + 'MSN was identified as a potential driver protein based on protein coexpression analysis. The group of proteins coexpressed with MSN is conserved across the 3 datasets considered, is enriched for inflammatory processes, and for protein products of genes near loci previously associated with AD risk. MSN has increased abundance in AD across all 3 cohorts examined, and progressively increases in asymptomatic (prodromal) AD to symptomatic AD, and also correlates with both hallmark AD pathology scores (CERAD for amyloid burden; and Braak for Tau extent of spread). MSN is highly expressed as a marker of disease-associated microglia and/or endothelial cell types.', + predicted_therapeutic_direction: + 'Antagonism predicted to reduce disease progression. Phosphorylation downstream of Rho/Rock influences actin, focal adhesion binding; may have redundancy with EZR and RDX, complicating targeting. MSN-directed therapeutics that improve microglial motility and/or phagocytosis competence would reduce abeta/amyloid plaque burden.', + data_used_to_support_target_selection: + 'Discovery quantitative proteomics of FrCx \r\n WPCNA of multiple and consensus cohorts\r\n ANOVA', + data_synapseid: 'syn17008058', + study: 'ACT, BLSA, Banner', + input_data: 'Protein', + validation_study_details: 'validation studies ongoing', + initial_nomination: 2018, +}; export const geneMock1: Gene = { _id: '628ea1be0e8d04279fdbaa26', From 201616c4e6706c8c92fb1abdb50ae196d2ce7f1b Mon Sep 17 00:00:00 2001 From: Hallie Swan <26949006+hallieswan@users.noreply.github.com> Date: Fri, 26 Apr 2024 11:45:27 -0700 Subject: [PATCH 13/30] AG-1408: wait for differential expressions boxplot to finish rendering async data before scrolling to anchor link --- .../gene-evidence-rna.component.ts | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/app/features/genes/components/gene-evidence-rna/gene-evidence-rna.component.ts b/src/app/features/genes/components/gene-evidence-rna/gene-evidence-rna.component.ts index 7acce494..c71aaea6 100644 --- a/src/app/features/genes/components/gene-evidence-rna/gene-evidence-rna.component.ts +++ b/src/app/features/genes/components/gene-evidence-rna/gene-evidence-rna.component.ts @@ -1,10 +1,11 @@ -import { Component, Input, AfterViewInit } from '@angular/core'; +import { AfterViewChecked, Component, Input, ViewChild } from '@angular/core'; import { Gene, MedianExpression, RnaDifferentialExpression, } from '../../../../models'; +import { BoxPlotComponent } from '../../../charts/components'; import { GeneService } from '../../services'; import { HelperService } from '../../../../core/services'; @@ -13,7 +14,7 @@ import { HelperService } from '../../../../core/services'; templateUrl: './gene-evidence-rna.component.html', styleUrls: ['./gene-evidence-rna.component.scss'], }) -export class GeneEvidenceRnaComponent implements AfterViewInit { +export class GeneEvidenceRnaComponent implements AfterViewChecked { _gene: Gene | undefined; get gene(): Gene | undefined { return this._gene; @@ -35,6 +36,9 @@ export class GeneEvidenceRnaComponent implements AfterViewInit { consistencyOfChangeChartData: any | undefined; + @ViewChild(BoxPlotComponent) boxPlotComponent: BoxPlotComponent | null = null; + hasScrolled = false; + constructor( private helperService: HelperService, private geneService: GeneService @@ -52,6 +56,8 @@ export class GeneEvidenceRnaComponent implements AfterViewInit { this.differentialExpressionYAxisMax = undefined; this.consistencyOfChangeChartData = undefined; + + this.hasScrolled = false; } init() { @@ -71,13 +77,20 @@ export class GeneEvidenceRnaComponent implements AfterViewInit { this.initConsistencyOfChange(); } - ngAfterViewInit() { - const hash = window.location.hash.substr(1); - if (hash) { - const target = document.getElementById(hash); - if (target) { - // TODO determine if there are async calls altering the offset height - window.scrollTo(0, this.helperService.getOffset(target).top - 150); + ngAfterViewChecked() { + this.scrollToAnchorLink(); + } + + scrollToAnchorLink() { + // AG-1408 - wait for differential expression box plot to finish loading before scrolling + if (this.boxPlotComponent?.isInitialized && !this.hasScrolled) { + const hash = window.location.hash.slice(1); + if (hash) { + const target = document.getElementById(hash); + if (target) { + window.scrollTo(0, this.helperService.getOffset(target).top - 150); + this.hasScrolled = true; + } } } } From 23f53ab150fe61496d0eaa3c4d5ac26ece4254b5 Mon Sep 17 00:00:00 2001 From: Hallie Swan <26949006+hallieswan@users.noreply.github.com> Date: Fri, 26 Apr 2024 12:21:44 -0700 Subject: [PATCH 14/30] AG-1408: add e2e test --- tests/gene-comparison-tool.spec.ts | 7 +++---- tests/gene-details.spec.ts | 12 ++++++++++++ tests/gene-resources.spec.ts | 7 +++---- tests/helpers/utils.ts | 7 +++++++ 4 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 tests/helpers/utils.ts diff --git a/tests/gene-comparison-tool.spec.ts b/tests/gene-comparison-tool.spec.ts index f203600a..9a3f12c5 100644 --- a/tests/gene-comparison-tool.spec.ts +++ b/tests/gene-comparison-tool.spec.ts @@ -1,4 +1,5 @@ import { test, expect } from '@playwright/test'; +import { waitForSpinnerNotVisible } from './helpers/utils'; test.describe('specific viewport block', () => { test.slow(); @@ -8,8 +9,7 @@ test.describe('specific viewport block', () => { await page.goto('/genes/comparison?category=Protein+-+Differential+Expression'); // wait for page to load (i.e. spinner to disappear) - await expect(page.locator('div:nth-child(4) > div > .spinner')) - .not.toBeVisible({ timeout: 250000}); + await waitForSpinnerNotVisible(page); // Expect a title "to contain" a substring. await expect(page).toHaveTitle('Gene Comparison | Visual comparison tool for AD genes'); @@ -20,8 +20,7 @@ test.describe('specific viewport block', () => { await page.goto('/genes/comparison?category=Protein+-+Differential+Expression'); // wait for page to load (i.e. spinner to disappear) - await expect(page.locator('div:nth-child(4) > div > .spinner')) - .not.toBeVisible({ timeout: 150000}); + await waitForSpinnerNotVisible(page, 150000); // expect sub-category dropdown to be SRM const dropdown = page.locator('#subCategory'); diff --git a/tests/gene-details.spec.ts b/tests/gene-details.spec.ts index 31883aac..6e7258a4 100644 --- a/tests/gene-details.spec.ts +++ b/tests/gene-details.spec.ts @@ -1,4 +1,5 @@ import { test, expect } from '@playwright/test'; +import { waitForSpinnerNotVisible } from './helpers/utils'; test.describe('specific viewport block', () => { test.slow(); @@ -14,4 +15,15 @@ test.describe('specific viewport block', () => { // expect div for page not found content to be visible expect(page.locator('.page-not-found')).toBeVisible(); }); + + test('consistency of change section heading is visible when using anchor link', async ({ page}) => { + await page.goto( + '/genes/ENSG00000178209/evidence/rna?model=AD Diagnosis males and females#consistency-of-change' + ); + + await waitForSpinnerNotVisible(page); + + const header = page.getByRole('heading', { name: 'Consistency of Change in Expression'}); + expect(header).toBeInViewport(); + }); }); \ No newline at end of file diff --git a/tests/gene-resources.spec.ts b/tests/gene-resources.spec.ts index f109b1d8..ff9b63bb 100644 --- a/tests/gene-resources.spec.ts +++ b/tests/gene-resources.spec.ts @@ -1,4 +1,5 @@ import { test, expect } from '@playwright/test'; +import { waitForSpinnerNotVisible } from './helpers/utils'; test.describe('specific viewport block', () => { test.slow(); @@ -8,8 +9,7 @@ test.describe('specific viewport block', () => { await page.goto('/genes/ENSG00000178209/resources'); // wait for page to load (i.e. spinner to disappear) - await expect(page.locator('div:nth-child(4) > div > .spinner')) - .not.toBeVisible({ timeout: 250000}); + await waitForSpinnerNotVisible(page); // Expect a title "to contain" a substring. await expect(page).toHaveTitle('Agora'); @@ -20,8 +20,7 @@ test.describe('specific viewport block', () => { await page.goto('/genes/ENSG00000178209/resources'); // wait for page to load (i.e. spinner to disappear) - await expect(page.locator('div:nth-child(4) > div > .spinner')) - .not.toBeVisible({ timeout: 150000}); + await waitForSpinnerNotVisible(page, 150000); // expect link named 'Visit AMP-PD' const link = page.getByRole('link', { name: 'Visit AMP-PD' }); diff --git a/tests/helpers/utils.ts b/tests/helpers/utils.ts new file mode 100644 index 00000000..e6d122f3 --- /dev/null +++ b/tests/helpers/utils.ts @@ -0,0 +1,7 @@ +import { Page, expect } from '@playwright/test'; + +export const waitForSpinnerNotVisible = async (page: Page, timeout = 250000) => { + await expect( + page.locator('div:nth-child(4) > div > .spinner') + ).not.toBeVisible({ timeout: timeout }); +}; From 45ffc4cebe670e0503eda341b6c24e0ef362126b Mon Sep 17 00:00:00 2001 From: sagely1 <114952739+sagely1@users.noreply.github.com> Date: Tue, 7 May 2024 13:20:41 -0700 Subject: [PATCH 15/30] AG-1425 refactor to support last pinned cache --- .../gene-comparison-tool.component.html | 38 +- .../gene-comparison-tool.component.scss | 6 + .../gene-comparison-tool.component.ts | 337 ++++++++++++++---- 3 files changed, 289 insertions(+), 92 deletions(-) diff --git a/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.html b/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.html index bdb7fc11..50f85963 100644 --- a/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.html +++ b/src/app/features/genes/components/gene-comparison-tool/gene-comparison-tool.component.html @@ -288,7 +288,19 @@

Gene Comparison Tool

class="table-divider table-divider-1" >
-
Pinned Genes ({{ pinnedGenes.length }}/50)
+
+ + Pinned Genes ({{ pinnedGenes.length }}/50) + +
+
+ Pinned Genes ({{ uniquePinnedGenesCount }}/50)    +
+
+ {{ pinnedGenes.length }} Proteins +
+
+
- +
@@ -330,7 +342,7 @@

Gene Comparison Tool