feat: add more e2e tests for MPR and add test mode and extension (#3180)
* tests: add various e2e tests for MPR and measurements wip add cypress config feat: add mode and extension for testing add hp applied through search params add MPR tests apply review comments add more e2e tests update yarn lock * fix unit tests failing
This commit is contained in:
parent
ae4183852f
commit
926290f69e
@ -39,7 +39,7 @@ module.exports = (env, argv, { SRC_DIR, DIST_DIR }) => {
|
|||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
mode: isProdBuild ? 'production' : 'development',
|
mode: isProdBuild ? 'production' : 'development',
|
||||||
devtool: isProdBuild ? 'source-map' : 'eval-source-map',
|
devtool: isProdBuild ? 'source-map' : 'cheap-module-source-map',
|
||||||
entry: {
|
entry: {
|
||||||
app: `${SRC_DIR}/index.js`,
|
app: `${SRC_DIR}/index.js`,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -36,13 +36,13 @@
|
|||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-i18next": "^10.11.0",
|
"react-i18next": "^10.11.0",
|
||||||
"react-router": "^6.3.0",
|
"react-router": "^6.8.1",
|
||||||
"react-router-dom": "^6.3.0",
|
"react-router-dom": "^6.8.1",
|
||||||
"webpack": "^5.50.0",
|
"webpack": "^5.50.0",
|
||||||
"webpack-merge": "^5.7.3"
|
"webpack-merge": "^5.7.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.7.6",
|
"@babel/runtime": "^7.20.13",
|
||||||
"react-color": "^2.19.3"
|
"react-color": "^2.19.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@ -43,7 +43,7 @@
|
|||||||
"react": "^17.0.2"
|
"react": "^17.0.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.16.3",
|
"@babel/runtime": "^7.20.13",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
"@cornerstonejs/core": "0.30.1",
|
"@cornerstonejs/core": "0.30.1",
|
||||||
"@cornerstonejs/tools": "0.39.0"
|
"@cornerstonejs/tools": "0.39.0"
|
||||||
|
|||||||
@ -42,7 +42,7 @@
|
|||||||
"lodash": "^4.17.21"
|
"lodash": "^4.17.21"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.17.9",
|
"@babel/runtime": "^7.20.13",
|
||||||
"@cornerstonejs/adapters": "0.3.0",
|
"@cornerstonejs/adapters": "0.3.0",
|
||||||
"@cornerstonejs/core": "0.30.1",
|
"@cornerstonejs/core": "0.30.1",
|
||||||
"@cornerstonejs/streaming-image-volume-loader": "^0.11.2",
|
"@cornerstonejs/streaming-image-volume-loader": "^0.11.2",
|
||||||
|
|||||||
@ -77,7 +77,10 @@ export default async function init({
|
|||||||
|
|
||||||
window.services = servicesManager.services;
|
window.services = servicesManager.services;
|
||||||
|
|
||||||
if (!window.crossOriginIsolated) {
|
if (
|
||||||
|
appConfig.showWarningMessageForCrossOrigin &&
|
||||||
|
!window.crossOriginIsolated
|
||||||
|
) {
|
||||||
uiNotificationService.show({
|
uiNotificationService.show({
|
||||||
title: 'Cross Origin Isolation',
|
title: 'Cross Origin Isolation',
|
||||||
message:
|
message:
|
||||||
@ -86,7 +89,10 @@ export default async function init({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cornerstone.getShouldUseCPURendering()) {
|
if (
|
||||||
|
appConfig.showCPUFallbackMessage &&
|
||||||
|
cornerstone.getShouldUseCPURendering()
|
||||||
|
) {
|
||||||
_showCPURenderingModal(uiModalService, hangingProtocolService);
|
_showCPURenderingModal(uiModalService, hangingProtocolService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -59,6 +59,7 @@ function callInputDialog(
|
|||||||
autoFocus
|
autoFocus
|
||||||
className="mt-2 bg-black border-primary-main"
|
className="mt-2 bg-black border-primary-main"
|
||||||
type="text"
|
type="text"
|
||||||
|
id="annotation"
|
||||||
containerClassName="mr-2"
|
containerClassName="mr-2"
|
||||||
value={value.label}
|
value={value.label}
|
||||||
onChange={event => {
|
onChange={event => {
|
||||||
|
|||||||
@ -58,26 +58,20 @@ export default function toggleMPRHangingProtocol({
|
|||||||
const viewportDisplaySetInstanceUIDs =
|
const viewportDisplaySetInstanceUIDs =
|
||||||
viewports[activeViewportIndex].displaySetInstanceUIDs;
|
viewports[activeViewportIndex].displaySetInstanceUIDs;
|
||||||
|
|
||||||
const errorCallback = error => {
|
// What is the current active protocol and stage number to restore later
|
||||||
// Unable to create MPR, so be sure to return to the cached/original protocol.
|
const { protocol, stage } = hangingProtocolService.getActiveProtocol();
|
||||||
hangingProtocolService.setProtocol(
|
|
||||||
cachedState.protocol.id,
|
|
||||||
viewportMatchDetails,
|
|
||||||
restoreErrorCallback
|
|
||||||
);
|
|
||||||
|
|
||||||
|
const restoreErrorCallback = error => {
|
||||||
|
console.error(error);
|
||||||
uiNotificationService.show({
|
uiNotificationService.show({
|
||||||
title: 'Multiplanar reconstruction (MPR) ',
|
title: 'Multiplanar reconstruction (MPR) ',
|
||||||
message:
|
message:
|
||||||
'Cannot create MPR for this DisplaySet since it is not reconstructable.',
|
'Something went wrong while trying to restore the previous layout.',
|
||||||
type: 'info',
|
type: 'info',
|
||||||
duration: 3000,
|
duration: 3000,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// What is the current active protocol and stage number to restore later
|
|
||||||
const { protocol, stage } = hangingProtocolService.getActiveProtocol();
|
|
||||||
|
|
||||||
if (toggledState) {
|
if (toggledState) {
|
||||||
resetCachedState();
|
resetCachedState();
|
||||||
|
|
||||||
@ -109,6 +103,23 @@ export default function toggleMPRHangingProtocol({
|
|||||||
getToolGroup
|
getToolGroup
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const errorCallback = error => {
|
||||||
|
// Unable to create MPR, so be sure to return to the cached/original protocol.
|
||||||
|
hangingProtocolService.setProtocol(
|
||||||
|
cachedState.protocol.id,
|
||||||
|
viewportMatchDetails,
|
||||||
|
restoreErrorCallback
|
||||||
|
);
|
||||||
|
|
||||||
|
uiNotificationService.show({
|
||||||
|
title: 'Multiplanar reconstruction (MPR) ',
|
||||||
|
message:
|
||||||
|
'Cannot create MPR for this DisplaySet since it is not reconstructable.',
|
||||||
|
type: 'info',
|
||||||
|
duration: 3000,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
hangingProtocolService.setProtocol(
|
hangingProtocolService.setProtocol(
|
||||||
MPR_TOOLGROUP_ID,
|
MPR_TOOLGROUP_ID,
|
||||||
matchDetails,
|
matchDetails,
|
||||||
@ -117,16 +128,6 @@ export default function toggleMPRHangingProtocol({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const restoreErrorCallback = error => {
|
|
||||||
uiNotificationService.show({
|
|
||||||
title: 'Multiplanar reconstruction (MPR) ',
|
|
||||||
message:
|
|
||||||
'Something went wrong while trying to restore the previous layout.',
|
|
||||||
type: 'info',
|
|
||||||
duration: 3000,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
_disableCrosshairs([MPR_TOOLGROUP_ID], getToolGroup);
|
_disableCrosshairs([MPR_TOOLGROUP_ID], getToolGroup);
|
||||||
|
|
||||||
const { layoutType, properties } = cachedState.viewportStructure;
|
const { layoutType, properties } = cachedState.viewportStructure;
|
||||||
|
|||||||
@ -42,7 +42,7 @@
|
|||||||
"webpack-merge": "^5.7.3"
|
"webpack-merge": "^5.7.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.16.3",
|
"@babel/runtime": "^7.20.13",
|
||||||
"@cornerstonejs/calculate-suv": "^1.0.3"
|
"@cornerstonejs/calculate-suv": "^1.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -176,6 +176,7 @@ export default function PanelMeasurementTable({
|
|||||||
<div className="p-4 bg-primary-dark">
|
<div className="p-4 bg-primary-dark">
|
||||||
<Input
|
<Input
|
||||||
autoFocus
|
autoFocus
|
||||||
|
id="annotation"
|
||||||
className="mt-2 bg-black border-primary-main"
|
className="mt-2 bg-black border-primary-main"
|
||||||
type="text"
|
type="text"
|
||||||
containerClassName="mr-2"
|
containerClassName="mr-2"
|
||||||
|
|||||||
@ -37,7 +37,7 @@
|
|||||||
"react": "^17.0.2"
|
"react": "^17.0.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.7.6",
|
"@babel/runtime": "^7.20.13",
|
||||||
"classnames": "^2.3.2"
|
"classnames": "^2.3.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,7 +37,7 @@
|
|||||||
"react": "^17.0.2"
|
"react": "^17.0.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.7.6",
|
"@babel/runtime": "^7.20.13",
|
||||||
"classnames": "^2.3.2"
|
"classnames": "^2.3.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,7 +44,7 @@
|
|||||||
"@ohif/ui": "^2.0.0"
|
"@ohif/ui": "^2.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.16.3",
|
"@babel/runtime": "^7.20.13",
|
||||||
"@ohif/ui": "^2.0.0",
|
"@ohif/ui": "^2.0.0",
|
||||||
"@xstate/react": "^0.8.1",
|
"@xstate/react": "^0.8.1",
|
||||||
"xstate": "^4.10.0"
|
"xstate": "^4.10.0"
|
||||||
|
|||||||
@ -198,6 +198,7 @@ function PanelMeasurementTableTracking({ servicesManager, extensionManager }) {
|
|||||||
<div className="p-4 bg-primary-dark">
|
<div className="p-4 bg-primary-dark">
|
||||||
<Input
|
<Input
|
||||||
autoFocus
|
autoFocus
|
||||||
|
id="annotation"
|
||||||
className="mt-2 bg-black border-primary-main"
|
className="mt-2 bg-black border-primary-main"
|
||||||
type="text"
|
type="text"
|
||||||
containerClassName="mr-2"
|
containerClassName="mr-2"
|
||||||
|
|||||||
8
extensions/test-extension/.webpack/webpack.dev.js
Normal file
8
extensions/test-extension/.webpack/webpack.dev.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const webpackCommon = require('./../../../.webpack/webpack.commonjs.js');
|
||||||
|
const SRC_DIR = path.join(__dirname, '../src');
|
||||||
|
const DIST_DIR = path.join(__dirname, '../dist');
|
||||||
|
|
||||||
|
module.exports = (env, argv) => {
|
||||||
|
return webpackCommon(env, argv, { SRC_DIR, DIST_DIR });
|
||||||
|
};
|
||||||
43
extensions/test-extension/.webpack/webpack.prod.js
Normal file
43
extensions/test-extension/.webpack/webpack.prod.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
const webpack = require('webpack');
|
||||||
|
const merge = require('webpack-merge');
|
||||||
|
const path = require('path');
|
||||||
|
const webpackCommon = require('./../../../.webpack/webpack.base.js');
|
||||||
|
const pkg = require('./../package.json');
|
||||||
|
|
||||||
|
const ROOT_DIR = path.join(__dirname, './..');
|
||||||
|
const SRC_DIR = path.join(__dirname, '../src');
|
||||||
|
const DIST_DIR = path.join(__dirname, '../dist');
|
||||||
|
|
||||||
|
module.exports = (env, argv) => {
|
||||||
|
const commonConfig = webpackCommon(env, argv, { SRC_DIR, DIST_DIR });
|
||||||
|
|
||||||
|
return merge(commonConfig, {
|
||||||
|
stats: {
|
||||||
|
colors: true,
|
||||||
|
hash: true,
|
||||||
|
timings: true,
|
||||||
|
assets: true,
|
||||||
|
chunks: false,
|
||||||
|
chunkModules: false,
|
||||||
|
modules: false,
|
||||||
|
children: false,
|
||||||
|
warnings: true,
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
minimize: true,
|
||||||
|
sideEffects: true,
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
path: ROOT_DIR,
|
||||||
|
library: 'OHIFExtDICOMSR',
|
||||||
|
libraryTarget: 'umd',
|
||||||
|
libraryExport: 'default',
|
||||||
|
filename: pkg.main,
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new webpack.optimize.LimitChunkCountPlugin({
|
||||||
|
maxChunks: 1,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
21
extensions/test-extension/LICENSE
Normal file
21
extensions/test-extension/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018 Open Health Imaging Foundation
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
4
extensions/test-extension/README.md
Normal file
4
extensions/test-extension/README.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Test Extension
|
||||||
|
This extension will provide uitlities and module to help with testing. It is not intended to be used in production.
|
||||||
|
For instance, we can use the hanging protocol module to inject a hanging protocol into the core and later
|
||||||
|
use it to test the hanging protocol detection mechanism.
|
||||||
1
extensions/test-extension/babel.config.js
Normal file
1
extensions/test-extension/babel.config.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
module.exports = require('../../babel.config.js');
|
||||||
43
extensions/test-extension/package.json
Normal file
43
extensions/test-extension/package.json
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"name": "@ohif/extension-test",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "OHIF extension used inside e2e testing",
|
||||||
|
"author": "OHIF",
|
||||||
|
"license": "MIT",
|
||||||
|
"repository": "OHIF/Viewers",
|
||||||
|
"main": "dist/index.umd.js",
|
||||||
|
"module": "src/index.tsx",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14",
|
||||||
|
"npm": ">=6",
|
||||||
|
"yarn": ">=1.16.0"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"README.md"
|
||||||
|
],
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --debug --output-pathinfo",
|
||||||
|
"build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js",
|
||||||
|
"build:package": "yarn run build",
|
||||||
|
"start": "yarn run dev",
|
||||||
|
"test:unit": "jest --watchAll",
|
||||||
|
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@ohif/core": "^3.0.0",
|
||||||
|
"@ohif/ui": "^2.0.0",
|
||||||
|
"dcmjs": "0.29.4",
|
||||||
|
"dicom-parser": "^1.8.9",
|
||||||
|
"hammerjs": "^2.0.8",
|
||||||
|
"prop-types": "^15.6.2",
|
||||||
|
"react": "^17.0.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.20.13",
|
||||||
|
"classnames": "^2.3.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
5
extensions/test-extension/src/id.js
Normal file
5
extensions/test-extension/src/id.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import packageJson from '../package.json';
|
||||||
|
|
||||||
|
const id = packageJson.name;
|
||||||
|
|
||||||
|
export { id };
|
||||||
17
extensions/test-extension/src/index.tsx
Normal file
17
extensions/test-extension/src/index.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { id } from './id';
|
||||||
|
import { Types } from '@ohif/core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const testExtension: Types.Extensions.Extension = {
|
||||||
|
/**
|
||||||
|
* Only required property. Should be a unique value across all extensions.
|
||||||
|
*/
|
||||||
|
id,
|
||||||
|
preRegistration() {
|
||||||
|
console.debug('hello from test-extension init.js');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default testExtension;
|
||||||
@ -37,7 +37,7 @@
|
|||||||
"react": "^17.0.2"
|
"react": "^17.0.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.7.6",
|
"@babel/runtime": "^7.20.13",
|
||||||
"classnames": "^2.3.2"
|
"classnames": "^2.3.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,13 +32,12 @@
|
|||||||
"@ohif/core": "^3.0.0",
|
"@ohif/core": "^3.0.0",
|
||||||
"@ohif/extension-default": "^3.0.0",
|
"@ohif/extension-default": "^3.0.0",
|
||||||
"@ohif/extension-cornerstone": "^3.0.0",
|
"@ohif/extension-cornerstone": "^3.0.0",
|
||||||
"@ohif/extension-cornerstone": "^3.0.0",
|
|
||||||
"@ohif/extension-cornerstone-dicom-sr": "^3.0.0",
|
"@ohif/extension-cornerstone-dicom-sr": "^3.0.0",
|
||||||
"@ohif/extension-dicom-pdf": "^3.0.1",
|
"@ohif/extension-dicom-pdf": "^3.0.1",
|
||||||
"@ohif/extension-dicom-video": "^3.0.1"
|
"@ohif/extension-dicom-video": "^3.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.13.10"
|
"@babel/runtime": "^7.20.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"webpack": "^5.50.0",
|
"webpack": "^5.50.0",
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "mode-basic-test-mode",
|
"name": "@ohif/mode-test",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"description": "Basic mode for testing",
|
"description": "Basic mode for testing",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
@ -38,10 +38,11 @@
|
|||||||
"@ohif/extension-cornerstone-dicom-sr": "^3.0.0",
|
"@ohif/extension-cornerstone-dicom-sr": "^3.0.0",
|
||||||
"@ohif/extension-dicom-pdf": "^3.0.1",
|
"@ohif/extension-dicom-pdf": "^3.0.1",
|
||||||
"@ohif/extension-dicom-video": "^3.0.1",
|
"@ohif/extension-dicom-video": "^3.0.1",
|
||||||
"@ohif/extension-measurement-tracking": "^3.0.0"
|
"@ohif/extension-measurement-tracking": "^3.0.0",
|
||||||
|
"@ohif/extension-test": "0.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.16.3"
|
"@babel/runtime": "^7.20.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"webpack": "^5.50.0",
|
"webpack": "^5.50.0",
|
||||||
|
|||||||
@ -54,6 +54,7 @@ const extensionDependencies = {
|
|||||||
'@ohif/extension-cornerstone-dicom-seg': '^3.0.0',
|
'@ohif/extension-cornerstone-dicom-seg': '^3.0.0',
|
||||||
'@ohif/extension-dicom-pdf': '^3.0.1',
|
'@ohif/extension-dicom-pdf': '^3.0.1',
|
||||||
'@ohif/extension-dicom-video': '^3.0.1',
|
'@ohif/extension-dicom-video': '^3.0.1',
|
||||||
|
'@ohif/extension-test': '^0.0.1',
|
||||||
};
|
};
|
||||||
|
|
||||||
function modeFactory() {
|
function modeFactory() {
|
||||||
|
|||||||
@ -41,7 +41,7 @@
|
|||||||
"@ohif/extension-measurement-tracking": "^3.0.0"
|
"@ohif/extension-measurement-tracking": "^3.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.16.3"
|
"@babel/runtime": "^7.20.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"webpack": "^5.50.0",
|
"webpack": "^5.50.0",
|
||||||
|
|||||||
@ -41,7 +41,7 @@
|
|||||||
"@ohif/extension-measurement-tracking": "^3.0.0"
|
"@ohif/extension-measurement-tracking": "^3.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.16.3"
|
"@babel/runtime": "^7.20.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"webpack": "^5.50.0",
|
"webpack": "^5.50.0",
|
||||||
|
|||||||
@ -55,7 +55,7 @@
|
|||||||
"link-list": "npm ls --depth=0 --link=true"
|
"link-list": "npm ls --depth=0 --link=true"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.16.3",
|
"@babel/runtime": "^7.20.13",
|
||||||
"@kitware/vtk.js": "26.4.0",
|
"@kitware/vtk.js": "26.4.0",
|
||||||
"core-js": "^3.2.1"
|
"core-js": "^3.2.1"
|
||||||
},
|
},
|
||||||
@ -124,7 +124,7 @@
|
|||||||
"start-server-and-test": "^1.10.0",
|
"start-server-and-test": "^1.10.0",
|
||||||
"style-loader": "^1.0.0",
|
"style-loader": "^1.0.0",
|
||||||
"terser-webpack-plugin": "^5.1.4",
|
"terser-webpack-plugin": "^5.1.4",
|
||||||
"typescript": "^4.2.3",
|
"typescript": "4.6.4",
|
||||||
"unused-webpack-plugin": "2.4.0",
|
"unused-webpack-plugin": "2.4.0",
|
||||||
"webpack": "^5.50.0",
|
"webpack": "^5.50.0",
|
||||||
"webpack-cli": "^4.7.2",
|
"webpack-cli": "^4.7.2",
|
||||||
@ -146,6 +146,6 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"**/@babel/runtime": "7.16.3"
|
"**/@babel/runtime": "^7.20.13"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,26 +23,29 @@
|
|||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-i18next": "^10.11.0",
|
"react-i18next": "^10.11.0",
|
||||||
"react-router": "^6.3.0",
|
"react-router": "^6.8.1",
|
||||||
"react-router-dom": "^6.3.0",
|
"react-router-dom": "^6.8.1",
|
||||||
"webpack": "^5.50.0",
|
"webpack": "^5.50.0",
|
||||||
"webpack-merge": "^5.7.3"
|
"webpack-merge": "^5.7.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.7.6"
|
"@babel/runtime": "^7.20.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.5.0",
|
"@babel/core": "^7.17.8",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.5.0",
|
"@babel/plugin-proposal-class-properties": "^7.16.7",
|
||||||
"@babel/plugin-proposal-object-rest-spread": "^7.5.5",
|
"@babel/plugin-proposal-object-rest-spread": "^7.17.3",
|
||||||
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
"@babel/plugin-proposal-private-methods": "^7.18.6",
|
||||||
"@babel/plugin-transform-arrow-functions": "^7.2.0",
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||||
"@babel/plugin-transform-regenerator": "^7.4.5",
|
"@babel/plugin-transform-arrow-functions": "^7.16.7",
|
||||||
"@babel/plugin-transform-runtime": "^7.5.0",
|
"@babel/plugin-transform-regenerator": "^7.16.7",
|
||||||
|
"@babel/plugin-transform-runtime": "^7.17.0",
|
||||||
"babel-plugin-inline-react-svg": "^2.0.1",
|
"babel-plugin-inline-react-svg": "^2.0.1",
|
||||||
"@babel/preset-env": "^7.5.0",
|
"@babel/plugin-transform-typescript": "^7.13.0",
|
||||||
"@babel/preset-react": "^7.0.0",
|
"@babel/preset-env": "^7.16.11",
|
||||||
"@babel/preset-typescript": "^7.18.6",
|
"@babel/preset-react": "^7.16.7",
|
||||||
|
"@babel/preset-typescript": "^7.13.0",
|
||||||
|
|
||||||
"babel-eslint": "^8.0.3",
|
"babel-eslint": "^8.0.3",
|
||||||
"babel-loader": "^8.0.0-beta.4",
|
"babel-loader": "^8.0.0-beta.4",
|
||||||
"clean-webpack-plugin": "^4.0.0",
|
"clean-webpack-plugin": "^4.0.0",
|
||||||
|
|||||||
@ -20,19 +20,21 @@
|
|||||||
"@ohif/core": "^3.0.0"
|
"@ohif/core": "^3.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.7.6"
|
"@babel/runtime": "^7.20.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.5.0",
|
"@babel/core": "^7.17.8",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.5.0",
|
"@babel/plugin-proposal-class-properties": "^7.16.7",
|
||||||
"@babel/plugin-proposal-object-rest-spread": "^7.5.5",
|
"@babel/plugin-proposal-object-rest-spread": "^7.17.3",
|
||||||
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
"@babel/plugin-proposal-private-methods": "^7.18.6",
|
||||||
"@babel/plugin-transform-arrow-functions": "^7.2.0",
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||||
"@babel/plugin-transform-regenerator": "^7.4.5",
|
"@babel/plugin-transform-arrow-functions": "^7.16.7",
|
||||||
"@babel/plugin-transform-runtime": "^7.5.0",
|
"@babel/plugin-transform-regenerator": "^7.16.7",
|
||||||
"@babel/preset-env": "^7.5.0",
|
"@babel/plugin-transform-runtime": "^7.17.0",
|
||||||
"@babel/preset-react": "^7.0.0",
|
"@babel/plugin-transform-typescript": "^7.13.0",
|
||||||
"@babel/preset-typescript": "^7.17.12",
|
"@babel/preset-env": "^7.16.11",
|
||||||
|
"@babel/preset-react": "^7.16.7",
|
||||||
|
"@babel/preset-typescript": "^7.13.0",
|
||||||
"babel-eslint": "^8.0.3",
|
"babel-eslint": "^8.0.3",
|
||||||
"babel-loader": "^8.0.0-beta.4",
|
"babel-loader": "^8.0.0-beta.4",
|
||||||
"babel-plugin-inline-react-svg": "^2.0.1",
|
"babel-plugin-inline-react-svg": "^2.0.1",
|
||||||
|
|||||||
@ -36,7 +36,7 @@
|
|||||||
"@ohif/ui": "^2.0.0"
|
"@ohif/ui": "^2.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.16.3",
|
"@babel/runtime": "^7.20.13",
|
||||||
"dcmjs": "0.29.4",
|
"dcmjs": "0.29.4",
|
||||||
"dicomweb-client": "^0.6.0",
|
"dicomweb-client": "^0.6.0",
|
||||||
"isomorphic-base64": "^1.0.2",
|
"isomorphic-base64": "^1.0.2",
|
||||||
|
|||||||
@ -35,6 +35,7 @@ export interface ExtensionParams extends ExtensionConstructor {
|
|||||||
* have more values than this.
|
* have more values than this.
|
||||||
*/
|
*/
|
||||||
export interface Extension {
|
export interface Extension {
|
||||||
|
id: string;
|
||||||
preRegistration?: (p: ExtensionParams) => void;
|
preRegistration?: (p: ExtensionParams) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,11 +161,18 @@ export default class ExtensionManager {
|
|||||||
// but currently since some extensions need to be registered before
|
// but currently since some extensions need to be registered before
|
||||||
// others, we need to run them sequentially. We need a postInit hook
|
// others, we need to run them sequentially. We need a postInit hook
|
||||||
// to avoid this sequential async registration
|
// to avoid this sequential async registration
|
||||||
for (const extension of extensions) {
|
for (let i = 0; i < extensions.length; i++) {
|
||||||
|
const extension = extensions[i];
|
||||||
const hasConfiguration = Array.isArray(extension);
|
const hasConfiguration = Array.isArray(extension);
|
||||||
try {
|
try {
|
||||||
if (hasConfiguration) {
|
if (hasConfiguration) {
|
||||||
const [ohifExtension, configuration] = extension;
|
// Important: for some reason in the line below the type
|
||||||
|
// of extension is not recognized as [ExtensionRegister,
|
||||||
|
// ExtensionConfiguration] by babel DON"T CHANGE IT
|
||||||
|
// Same for the for loop above don't use
|
||||||
|
// for (const extension of extensions)
|
||||||
|
const ohifExtension = extension[0];
|
||||||
|
const configuration = extension[1];
|
||||||
await this.registerExtension(
|
await this.registerExtension(
|
||||||
ohifExtension,
|
ohifExtension,
|
||||||
configuration,
|
configuration,
|
||||||
|
|||||||
@ -185,7 +185,11 @@ class HangingProtocolService {
|
|||||||
* @param protocolId - the id of the protocol
|
* @param protocolId - the id of the protocol
|
||||||
* @returns protocol - the protocol with the given id
|
* @returns protocol - the protocol with the given id
|
||||||
*/
|
*/
|
||||||
public getProtocolById(id: string): HangingProtocol.Protocol {
|
public getProtocolById(id: string): HangingProtocol.Protocol | undefined {
|
||||||
|
if (!id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const protocol = this.protocols.get(id);
|
const protocol = this.protocols.get(id);
|
||||||
|
|
||||||
if (protocol instanceof Function) {
|
if (protocol instanceof Function) {
|
||||||
@ -233,7 +237,7 @@ class HangingProtocolService {
|
|||||||
* If active protocols ids is null right now, then the specified
|
* If active protocols ids is null right now, then the specified
|
||||||
* protocol will become the only active protocol.
|
* protocol will become the only active protocol.
|
||||||
*/
|
*/
|
||||||
public addActiveProtocol(id: string): void {
|
public addActiveProtocolId(id: string): void {
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -247,17 +251,17 @@ class HangingProtocolService {
|
|||||||
* Sets the active hanging protocols to use, by name. If the value is empty,
|
* Sets the active hanging protocols to use, by name. If the value is empty,
|
||||||
* then resets the active protocols to all the named items.
|
* then resets the active protocols to all the named items.
|
||||||
*/
|
*/
|
||||||
public setActiveProtocols(hangingProtocol?: string[] | string): void {
|
public setActiveProtocolIds(protocolId?: string[] | string): void {
|
||||||
if (!hangingProtocol || !hangingProtocol.length) {
|
if (!protocolId || !protocolId.length) {
|
||||||
this.activeProtocolIds = null;
|
this.activeProtocolIds = null;
|
||||||
console.log('No active protocols, setting all to active');
|
console.log('No active protocols, setting all to active');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (typeof hangingProtocol === 'string') {
|
if (typeof protocolId === 'string') {
|
||||||
this.setActiveProtocols([hangingProtocol]);
|
this.setActiveProtocolIds([protocolId]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.activeProtocolIds = [...hangingProtocol];
|
this.activeProtocolIds = [...protocolId];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -272,7 +276,6 @@ class HangingProtocolService {
|
|||||||
* @param params.displaySets is the list of display sets associated with
|
* @param params.displaySets is the list of display sets associated with
|
||||||
* the studies to display in viewports.
|
* the studies to display in viewports.
|
||||||
* @param protocol is a specific protocol to apply.
|
* @param protocol is a specific protocol to apply.
|
||||||
* @returns
|
|
||||||
*/
|
*/
|
||||||
public run({ studies, displaySets, activeStudy }, protocolId) {
|
public run({ studies, displaySets, activeStudy }, protocolId) {
|
||||||
this.studies = [...studies];
|
this.studies = [...studies];
|
||||||
|
|||||||
@ -36,7 +36,7 @@
|
|||||||
"react-i18next": "^10.11.0"
|
"react-i18next": "^10.11.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.16.3",
|
"@babel/runtime": "^7.20.13",
|
||||||
"i18next-locize-backend": "^2.0.0",
|
"i18next-locize-backend": "^2.0.0",
|
||||||
"locize-editor": "^2.0.0",
|
"locize-editor": "^2.0.0",
|
||||||
"locize-lastused": "^1.1.0"
|
"locize-lastused": "^1.1.0"
|
||||||
|
|||||||
@ -57,6 +57,7 @@ const Input = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
Input.propTypes = {
|
Input.propTypes = {
|
||||||
|
id: PropTypes.string,
|
||||||
label: PropTypes.string,
|
label: PropTypes.string,
|
||||||
containerClassName: PropTypes.string,
|
containerClassName: PropTypes.string,
|
||||||
labelClassName: PropTypes.string,
|
labelClassName: PropTypes.string,
|
||||||
@ -70,7 +71,7 @@ Input.propTypes = {
|
|||||||
readOnly: PropTypes.bool,
|
readOnly: PropTypes.bool,
|
||||||
onKeyPress: PropTypes.func,
|
onKeyPress: PropTypes.func,
|
||||||
onKeyDown: PropTypes.func,
|
onKeyDown: PropTypes.func,
|
||||||
disabled: PropTypes.bool
|
disabled: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Input;
|
export default Input;
|
||||||
|
|||||||
@ -32,6 +32,7 @@ const Modal = ({
|
|||||||
<Typography
|
<Typography
|
||||||
variant="h4"
|
variant="h4"
|
||||||
className="flex grow text-primary-light font-light"
|
className="flex grow text-primary-light font-light"
|
||||||
|
data-cy="modal-header"
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|||||||
@ -79,6 +79,7 @@ const Select = ({
|
|||||||
className,
|
className,
|
||||||
'flex flex-col flex-1 customSelect__wrapper'
|
'flex flex-col flex-1 customSelect__wrapper'
|
||||||
)}
|
)}
|
||||||
|
data-cy={`input-${id}`}
|
||||||
classNamePrefix="customSelect"
|
classNamePrefix="customSelect"
|
||||||
isDisabled={isDisabled}
|
isDisabled={isDisabled}
|
||||||
isClearable={isClearable}
|
isClearable={isClearable}
|
||||||
|
|||||||
@ -95,7 +95,11 @@ const ThumbnailTracked = ({
|
|||||||
>
|
>
|
||||||
<Icon name={trackedIcon} className="w-4 mb-2 text-primary-light" />
|
<Icon name={trackedIcon} className="w-4 mb-2 text-primary-light" />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<div className="text-xl leading-tight text-white text-center">
|
|
||||||
|
<div
|
||||||
|
className="text-xl leading-tight text-white text-center"
|
||||||
|
data-cy={'thumbnail-viewport-labels'}
|
||||||
|
>
|
||||||
{renderViewportLabels()}
|
{renderViewportLabels()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -29,18 +29,18 @@ const copyPluginFromExtensions = writePluginImportFile(SRC_DIR, DIST_DIR);
|
|||||||
|
|
||||||
const setHeaders = (res, path) => {
|
const setHeaders = (res, path) => {
|
||||||
if (path.indexOf('.gz') !== -1) {
|
if (path.indexOf('.gz') !== -1) {
|
||||||
res.setHeader('Content-Encoding', 'gzip')
|
res.setHeader('Content-Encoding', 'gzip');
|
||||||
} else if (path.indexOf('.br') !== -1) {
|
} else if (path.indexOf('.br') !== -1) {
|
||||||
res.setHeader('Content-Encoding', 'br')
|
res.setHeader('Content-Encoding', 'br');
|
||||||
}
|
}
|
||||||
if (path.indexOf('.pdf') !== -1) {
|
if (path.indexOf('.pdf') !== -1) {
|
||||||
res.setHeader('Content-Type', 'application/pdf');
|
res.setHeader('Content-Type', 'application/pdf');
|
||||||
} else if (path.indexOf('/frames') !== -1) {
|
} else if (path.indexOf('/frames') !== -1) {
|
||||||
res.setHeader('Content-Type', 'multipart/related')
|
res.setHeader('Content-Type', 'multipart/related');
|
||||||
} else {
|
} else {
|
||||||
res.setHeader('Content-Type', 'application/json')
|
res.setHeader('Content-Type', 'application/json');
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
module.exports = (env, argv) => {
|
module.exports = (env, argv) => {
|
||||||
const baseConfig = webpackBase(env, argv, { SRC_DIR, DIST_DIR });
|
const baseConfig = webpackBase(env, argv, { SRC_DIR, DIST_DIR });
|
||||||
@ -55,7 +55,7 @@ module.exports = (env, argv) => {
|
|||||||
path: DIST_DIR,
|
path: DIST_DIR,
|
||||||
filename: isProdBuild ? '[name].bundle.[chunkhash].js' : '[name].js',
|
filename: isProdBuild ? '[name].bundle.[chunkhash].js' : '[name].js',
|
||||||
publicPath: PUBLIC_URL, // Used by HtmlWebPackPlugin for asset prefix
|
publicPath: PUBLIC_URL, // Used by HtmlWebPackPlugin for asset prefix
|
||||||
devtoolModuleFilenameTemplate: function (info) {
|
devtoolModuleFilenameTemplate: function(info) {
|
||||||
if (isProdBuild) {
|
if (isProdBuild) {
|
||||||
return `webpack:///${info.resourcePath}`;
|
return `webpack:///${info.resourcePath}`;
|
||||||
} else {
|
} else {
|
||||||
@ -146,12 +146,12 @@ module.exports = (env, argv) => {
|
|||||||
proxy: {
|
proxy: {
|
||||||
'/dicomweb': 'http://localhost:5000',
|
'/dicomweb': 'http://localhost:5000',
|
||||||
},
|
},
|
||||||
'static': [
|
static: [
|
||||||
{
|
{
|
||||||
directory: '../../testdata',
|
directory: '../../testdata',
|
||||||
staticOptions: {
|
staticOptions: {
|
||||||
extensions: ['gz', 'br'],
|
extensions: ['gz', 'br'],
|
||||||
index: "index.json.gz",
|
index: ['index.json.gz', 'index.mht.gz'],
|
||||||
redirect: true,
|
redirect: true,
|
||||||
setHeaders,
|
setHeaders,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -17,8 +17,9 @@ function constructLines(input, categoryName) {
|
|||||||
if (!input) return lines;
|
if (!input) return lines;
|
||||||
|
|
||||||
input.forEach(entry => {
|
input.forEach(entry => {
|
||||||
const packageName = entry.packageName;
|
if (entry.default === false) return;
|
||||||
|
|
||||||
|
const packageName = entry.packageName;
|
||||||
const defaultImportName = `${categoryName}${pluginCount}`;
|
const defaultImportName = `${categoryName}${pluginCount}`;
|
||||||
|
|
||||||
lines.importLines.push(
|
lines.importLines.push(
|
||||||
@ -66,7 +67,23 @@ function getRuntimeLoadModesExtensions() {
|
|||||||
' for(const modeFactory of modesFactory) {\n' +
|
' for(const modeFactory of modesFactory) {\n' +
|
||||||
' const newModes = await modeFactory(modes,extensions);\n' +
|
' const newModes = await modeFactory(modes,extensions);\n' +
|
||||||
' newModes.forEach(newMode => modes.push(newMode));\n' +
|
' newModes.forEach(newMode => modes.push(newMode));\n' +
|
||||||
'}\n}\n'
|
'}\n}\n\n\n' +
|
||||||
|
'async function loadRuntimeImports(config) {\n' +
|
||||||
|
' if (config && config.modes && config.modes.length) {\n' +
|
||||||
|
' for (const modeName of config.modes) {\n' +
|
||||||
|
' const existingMode = modes.find(mode => mode.id === modeName);\n' +
|
||||||
|
' if (!existingMode) {\n' +
|
||||||
|
' if (modeName === `@ohif/mode-test`) {\n' +
|
||||||
|
' const mode = await import(`@ohif/mode-test`);\n' +
|
||||||
|
' window.modes.push(mode.default);\n' +
|
||||||
|
' const extension = await import(`@ohif/extension-test`);\n' +
|
||||||
|
' window.extensions.push(extension.default);\n' +
|
||||||
|
' }\n' +
|
||||||
|
' }\n' +
|
||||||
|
' };\n' +
|
||||||
|
' }\n' +
|
||||||
|
'}\n' +
|
||||||
|
'export { loadRuntimeImports };\n\n'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,11 +218,12 @@ function writePluginImportsFile(SRC_DIR, DIST_DIR) {
|
|||||||
'dist'
|
'dist'
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log('copy plugins', [...copyPluginPublicToDistBuild,
|
console.log('copy plugins', [
|
||||||
...copyPluginPublicToDistLink,
|
...copyPluginPublicToDistBuild,
|
||||||
...copyPluginDistToDistBuild,
|
...copyPluginPublicToDistLink,
|
||||||
...copyPluginDistToDistLink,
|
...copyPluginDistToDistBuild,
|
||||||
])
|
...copyPluginDistToDistLink,
|
||||||
|
]);
|
||||||
return [
|
return [
|
||||||
...copyPluginPublicToDistBuild,
|
...copyPluginPublicToDistBuild,
|
||||||
...copyPluginPublicToDistLink,
|
...copyPluginPublicToDistLink,
|
||||||
|
|||||||
40
platform/viewer/cypress.config.ts
Normal file
40
platform/viewer/cypress.config.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { defineConfig } from 'cypress';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
chromeWebSecurity: false,
|
||||||
|
e2e: {
|
||||||
|
experimentalRunAllSpecs: true,
|
||||||
|
supportFile: 'cypress/support/index.js',
|
||||||
|
setupNodeEvents(on, config) {
|
||||||
|
on('before:browser:launch', (browser, launchOptions) => {
|
||||||
|
// `args` is an array of all the arguments that will
|
||||||
|
// be passed to browsers when it launches
|
||||||
|
|
||||||
|
console.log(launchOptions.args); // print all current args
|
||||||
|
|
||||||
|
console.log('***', browser.family, browser.name, '***');
|
||||||
|
if (browser.family === 'chromium' && browser.name !== 'electron') {
|
||||||
|
// auto open devtools
|
||||||
|
launchOptions.args.push('--enable-features=SharedArrayBuffer');
|
||||||
|
}
|
||||||
|
|
||||||
|
// whatever you return here becomes the launchOptions
|
||||||
|
return launchOptions;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
baseUrl: 'http://localhost:3000',
|
||||||
|
waitForAnimations: true,
|
||||||
|
chromeWebSecurity: false,
|
||||||
|
defaultCommandTimeout: 10000,
|
||||||
|
requestTimeout: 10000,
|
||||||
|
responseTimeout: 10000,
|
||||||
|
specPattern: 'cypress/integration/**/*.spec.[jt]s',
|
||||||
|
projectId: '4oe38f',
|
||||||
|
video: false,
|
||||||
|
reporter: 'junit',
|
||||||
|
reporterOptions: {
|
||||||
|
mochaFile: 'cypress/results/test-output.xml',
|
||||||
|
toConsole: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"baseUrl": "http://localhost:3000",
|
|
||||||
"chromeWebSecurity": false,
|
|
||||||
"waitForAnimations": true,
|
|
||||||
"defaultCommandTimeout": 10000,
|
|
||||||
"requestTimeout": 10000,
|
|
||||||
"responseTimeout": 10000,
|
|
||||||
"projectId": "4oe38f",
|
|
||||||
"video": false,
|
|
||||||
"reporter": "junit",
|
|
||||||
"reporterOptions": {
|
|
||||||
"mochaFile": "cypress/results/test-output.xml",
|
|
||||||
"toConsole": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
6
platform/viewer/cypress/.eslintrc.js
Normal file
6
platform/viewer/cypress/.eslintrc.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: ['cypress'],
|
||||||
|
env: {
|
||||||
|
'cypress/globals': true,
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -1,18 +1,13 @@
|
|||||||
describe('OHIF PDF Display', function () {
|
describe('OHIF PDF Display', function() {
|
||||||
before(() => {
|
beforeEach(function() {
|
||||||
cy.openStudyInViewer(
|
cy.openStudyInViewer('2.25.317377619501274872606137091638706705333');
|
||||||
'2.25.317377619501274872606137091638706705333'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
cy.resetViewport().wait(50);
|
cy.resetViewport().wait(50);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('checks if series thumbnails are being displayed', function () {
|
it('checks if series thumbnails are being displayed', function() {
|
||||||
cy.get('[data-cy="study-browser-thumbnail"]')
|
cy.get('[data-cy="study-browser-thumbnail"]')
|
||||||
.its('length')
|
.its('length')
|
||||||
.should('be.gt', 0);
|
.should('be.gt', 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,15 +1,10 @@
|
|||||||
describe('OHIF Video Display', function () {
|
describe('OHIF Video Display', function() {
|
||||||
before(() => {
|
beforeEach(function() {
|
||||||
cy.openStudyInViewer(
|
cy.openStudyInViewer('2.25.96975534054447904995905761963464388233');
|
||||||
'2.25.96975534054447904995905761963464388233'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
cy.resetViewport().wait(50);
|
cy.resetViewport().wait(50);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('checks if series thumbnails are being displayed', function () {
|
it('checks if series thumbnails are being displayed', function() {
|
||||||
cy.get('[data-cy="study-browser-thumbnail"]')
|
cy.get('[data-cy="study-browser-thumbnail"]')
|
||||||
.its('length')
|
.its('length')
|
||||||
.should('be.gt', 1);
|
.should('be.gt', 1);
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
describe('OHIF Cornerstone Hotkeys', () => {
|
describe('OHIF Cornerstone Hotkeys', () => {
|
||||||
before(() => {
|
beforeEach(() => {
|
||||||
cy.checkStudyRouteInViewer(
|
cy.checkStudyRouteInViewer(
|
||||||
'1.2.840.113619.2.5.1762583153.215519.978957063.78'
|
'1.2.840.113619.2.5.1762583153.215519.978957063.78'
|
||||||
);
|
);
|
||||||
@ -15,12 +15,8 @@ describe('OHIF Cornerstone Hotkeys', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
cy.expectMinimumThumbnails(3);
|
cy.expectMinimumThumbnails(3);
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.initCornerstoneToolsAliases();
|
cy.initCornerstoneToolsAliases();
|
||||||
cy.initCommonElementsAliases();
|
cy.initCommonElementsAliases();
|
||||||
cy.resetViewport();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('checks if hotkeys "R" and "L" can rotate the image', () => {
|
it('checks if hotkeys "R" and "L" can rotate the image', () => {
|
||||||
@ -45,11 +41,11 @@ describe('OHIF Cornerstone Hotkeys', () => {
|
|||||||
|
|
||||||
it('checks if hotkeys "V" and "H" can flip the image', () => {
|
it('checks if hotkeys "V" and "H" can flip the image', () => {
|
||||||
// Hotkey H
|
// Hotkey H
|
||||||
cy.get('body').type('H');
|
cy.get('body').type('h');
|
||||||
cy.get('@viewportInfoMidLeft').should('contains.text', 'L');
|
cy.get('@viewportInfoMidLeft').should('contains.text', 'L');
|
||||||
cy.get('@viewportInfoMidTop').should('contains.text', 'A');
|
cy.get('@viewportInfoMidTop').should('contains.text', 'A');
|
||||||
// Hotkey V
|
// Hotkey V
|
||||||
cy.get('body').type('V');
|
cy.get('body').type('v');
|
||||||
cy.get('@viewportInfoMidLeft').should('contains.text', 'L');
|
cy.get('@viewportInfoMidLeft').should('contains.text', 'L');
|
||||||
cy.get('@viewportInfoMidTop').should('contains.text', 'P');
|
cy.get('@viewportInfoMidTop').should('contains.text', 'P');
|
||||||
});
|
});
|
||||||
@ -73,26 +69,17 @@ describe('OHIF Cornerstone Hotkeys', () => {
|
|||||||
// cy.get('@viewportInfoTopLeft').should('contains.text', 'Zoom:1.67x');
|
// cy.get('@viewportInfoTopLeft').should('contains.text', 'Zoom:1.67x');
|
||||||
// });
|
// });
|
||||||
|
|
||||||
// it('checks if hotkey "SPACEBAR" can reset the image', () => {
|
it('checks if hotkey "SPACEBAR" can reset the image', () => {
|
||||||
// //Click on button and verify if icon is active on toolbar
|
// Press multiples hotkeys
|
||||||
// cy.get('@zoomBtn')
|
cy.get('body').type('v+++i');
|
||||||
// .click()
|
cy.get('@viewportInfoMidLeft').should('contains.text', 'R');
|
||||||
// .then($zoomBtn => {
|
cy.get('@viewportInfoMidTop').should('contains.text', 'P');
|
||||||
// cy.wrap($zoomBtn).should('have.class', 'active');
|
|
||||||
// });
|
|
||||||
|
|
||||||
// // Press multiples hotkeys
|
// Hotkey SPACEBAR
|
||||||
// cy.get('body').type('V+++I');
|
cy.get('body').type(' ');
|
||||||
// cy.get('@viewportInfoMidLeft').should('contains.text', 'L');
|
cy.get('@viewportInfoMidLeft').should('contains.text', 'R');
|
||||||
// cy.get('@viewportInfoMidTop').should('contains.text', 'A');
|
cy.get('@viewportInfoMidTop').should('contains.text', 'A');
|
||||||
// cy.get('@viewportInfoTopLeft').should('contains.text', 'Zoom:2.30x');
|
});
|
||||||
|
|
||||||
// // Hotkey SPACEBAR
|
|
||||||
// cy.get('body').type(' ');
|
|
||||||
// cy.get('@viewportInfoMidLeft').should('contains.text', 'R');
|
|
||||||
// cy.get('@viewportInfoMidTop').should('contains.text', 'A');
|
|
||||||
// cy.get('@viewportInfoTopLeft').should('contains.text', 'Zoom:1.67x');
|
|
||||||
// });
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// TODO: Pretty sure this is not implemented yet
|
// TODO: Pretty sure this is not implemented yet
|
||||||
|
|||||||
@ -1,12 +1,9 @@
|
|||||||
describe('OHIF Cornerstone Toolbar', () => {
|
describe('OHIF Cornerstone Toolbar', () => {
|
||||||
before(() => {
|
beforeEach(() => {
|
||||||
cy.checkStudyRouteInViewer(
|
cy.checkStudyRouteInViewer(
|
||||||
'1.2.840.113619.2.5.1762583153.215519.978957063.78'
|
'1.2.840.113619.2.5.1762583153.215519.978957063.78'
|
||||||
);
|
);
|
||||||
cy.expectMinimumThumbnails(3);
|
cy.expectMinimumThumbnails(3);
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.initCornerstoneToolsAliases();
|
cy.initCornerstoneToolsAliases();
|
||||||
cy.initCommonElementsAliases();
|
cy.initCommonElementsAliases();
|
||||||
|
|
||||||
@ -16,8 +13,6 @@ describe('OHIF Cornerstone Toolbar', () => {
|
|||||||
|
|
||||||
//const expectedText = 'Ser: 1';
|
//const expectedText = 'Ser: 1';
|
||||||
//cy.get('@viewportInfoBottomLeft').should('contains.text', expectedText);
|
//cy.get('@viewportInfoBottomLeft').should('contains.text', expectedText);
|
||||||
|
|
||||||
cy.resetViewport();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('checks if all primary buttons are being displayed', () => {
|
it('checks if all primary buttons are being displayed', () => {
|
||||||
|
|||||||
@ -1,32 +1,17 @@
|
|||||||
describe('OHIF Download Snapshot File', () => {
|
describe('OHIF Download Snapshot File', () => {
|
||||||
before(() => {
|
beforeEach(() => {
|
||||||
cy.checkStudyRouteInViewer(
|
cy.checkStudyRouteInViewer(
|
||||||
'1.2.840.113619.2.5.1762583153.215519.978957063.78'
|
'1.2.840.113619.2.5.1762583153.215519.978957063.78'
|
||||||
);
|
);
|
||||||
cy.expectMinimumThumbnails(3);
|
cy.expectMinimumThumbnails(3);
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.openDownloadImageModal();
|
cy.openDownloadImageModal();
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
it('checks displayed information for Desktop experience', function() {
|
||||||
afterEach(() => {
|
|
||||||
// Close modal
|
|
||||||
|
|
||||||
// TODO: Modal.tsx currently uses Icon instead of IconButton, so
|
|
||||||
// it's not as easy to give it the cypress data attribute.
|
|
||||||
// We should fix that first
|
|
||||||
cy.get('[data-cy="close-button"]')
|
|
||||||
.scrollIntoView()
|
|
||||||
.click();
|
|
||||||
});*/
|
|
||||||
|
|
||||||
/*it('checks displayed information for Desktop experience', function() {
|
|
||||||
// Set Desktop resolution
|
// Set Desktop resolution
|
||||||
cy.viewport(1750, 720);
|
// cy.viewport(1750, 720);
|
||||||
// Visual comparison
|
// Visual comparison
|
||||||
cy.screenshot('Download Image Modal - Desktop experience');
|
// cy.screenshot('Download Image Modal - Desktop experience');
|
||||||
//Check if all elements are displayed
|
//Check if all elements are displayed
|
||||||
|
|
||||||
// TODO: need to add this attribute to the modal
|
// TODO: need to add this attribute to the modal
|
||||||
@ -36,19 +21,19 @@ describe('OHIF Download Snapshot File', () => {
|
|||||||
|
|
||||||
// Check input fields
|
// Check input fields
|
||||||
// TODO: select2
|
// TODO: select2
|
||||||
cy.get('[data-cy="file-type"]')
|
// cy.get('[data-cy="file-type"]')
|
||||||
.select('png')
|
// .select('png')
|
||||||
.should('have.value', 'png')
|
// .should('have.value', 'png')
|
||||||
.select('jpg')
|
// .select('jpg')
|
||||||
.should('have.value', 'jpg');
|
// .should('have.value', 'jpg');
|
||||||
|
|
||||||
// Check image preview
|
// Check image preview
|
||||||
cy.get('[data-cy="image-preview"]').should('contain.text', 'Image preview');
|
cy.get('[data-cy="image-preview"]').should('contain.text', 'Image preview');
|
||||||
|
|
||||||
TODO: This is a canvas now, not an img with src
|
//TODO: This is a canvas now, not an img with src
|
||||||
cy.get('[data-cy="viewport-preview-img"]')
|
// cy.get('[data-cy="viewport-preview-img"]')
|
||||||
.should('have.attr', 'src')
|
// .should('have.attr', 'src')
|
||||||
.and('include', 'data:image');
|
// .and('include', 'data:image');
|
||||||
|
|
||||||
// Check buttons
|
// Check buttons
|
||||||
cy.get('[data-cy="cancel-btn"]')
|
cy.get('[data-cy="cancel-btn"]')
|
||||||
@ -58,7 +43,6 @@ describe('OHIF Download Snapshot File', () => {
|
|||||||
.scrollIntoView()
|
.scrollIntoView()
|
||||||
.should('be.visible');
|
.should('be.visible');
|
||||||
});
|
});
|
||||||
*/
|
|
||||||
|
|
||||||
/*it('cancel changes on download modal', function() {
|
/*it('cancel changes on download modal', function() {
|
||||||
//Change Image Width, Filename and File Type
|
//Change Image Width, Filename and File Type
|
||||||
@ -88,8 +72,7 @@ describe('OHIF Download Snapshot File', () => {
|
|||||||
// TO-DO once issue is fixed: https://github.com/OHIF/Viewers/issues/1217
|
// TO-DO once issue is fixed: https://github.com/OHIF/Viewers/issues/1217
|
||||||
// it('checks error messages for empty fields', function() {
|
// it('checks error messages for empty fields', function() {
|
||||||
// //Clear fields Image Width and Filename
|
// //Clear fields Image Width and Filename
|
||||||
// cy.get('[data-cy="image-width"]')
|
// cy.get('[data-cy="image-width"]').clear();
|
||||||
// .clear();
|
|
||||||
// cy.get('[data-cy="file-name"]').clear();
|
// cy.get('[data-cy="file-name"]').clear();
|
||||||
|
|
||||||
// //Click on Download button
|
// //Click on Download button
|
||||||
|
|||||||
@ -0,0 +1,98 @@
|
|||||||
|
describe('OHIF Study Viewer Page', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
cy.checkStudyRouteInViewer(
|
||||||
|
'1.2.840.113619.2.5.1762583153.215519.978957063.78'
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.expectMinimumThumbnails(3);
|
||||||
|
cy.initCommonElementsAliases();
|
||||||
|
cy.initCornerstoneToolsAliases();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scrolls series stack using scrollbar', function() {
|
||||||
|
cy.scrollToIndex(13);
|
||||||
|
|
||||||
|
cy.get('@viewportInfoTopRight').should('contains.text', '14');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('performs right click to zoom', function() {
|
||||||
|
// This is not used to activate the tool, it is used to ensure the
|
||||||
|
// top left viewport info shows the zoom values (it only shows up
|
||||||
|
// when the zoom tool is active)
|
||||||
|
cy.get('@zoomBtn')
|
||||||
|
.click()
|
||||||
|
.then($zoomBtn => {
|
||||||
|
cy.wrap($zoomBtn).should('have.class', 'active');
|
||||||
|
});
|
||||||
|
|
||||||
|
const zoomLevelInitial = cy
|
||||||
|
.get('@viewportInfoTopLeft')
|
||||||
|
.then($viewportInfo => {
|
||||||
|
return $viewportInfo.text().substring(6, 9);
|
||||||
|
});
|
||||||
|
|
||||||
|
//Right click on viewport
|
||||||
|
cy.get('@viewport')
|
||||||
|
.trigger('mousedown', 'top', { buttons: 2 })
|
||||||
|
.trigger('mousemove', 'center', { buttons: 2 })
|
||||||
|
.trigger('mouseup');
|
||||||
|
|
||||||
|
// make sure the new zoom level is less than the initial
|
||||||
|
cy.get('@viewportInfoTopLeft').then($viewportInfo => {
|
||||||
|
const zoomLevelFinal = $viewportInfo.text().substring(6, 9);
|
||||||
|
expect(zoomLevelFinal < zoomLevelInitial).to.eq(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/*it('performs middle click to pan', function() {
|
||||||
|
//Get image position from cornerstone and check if y axis was modified
|
||||||
|
let cornerstone;
|
||||||
|
let currentPan;
|
||||||
|
|
||||||
|
// TO DO: Replace the cornerstone pan check by Percy snapshot comparison
|
||||||
|
cy.window()
|
||||||
|
.its('cornerstone')
|
||||||
|
.then(c => {
|
||||||
|
cornerstone = c;
|
||||||
|
currentPan = () =>
|
||||||
|
cornerstone.getEnabledElements()[0].viewport.translation;
|
||||||
|
});
|
||||||
|
|
||||||
|
//pan image with middle click
|
||||||
|
cy.get('@viewport')
|
||||||
|
.trigger('mousedown', 'center', { buttons: 3 })
|
||||||
|
.trigger('mousemove', 'bottom', { buttons: 3 })
|
||||||
|
.trigger('mouseup', 'bottom')
|
||||||
|
.then(() => {
|
||||||
|
expect(currentPan().y > 0).to.eq(true);
|
||||||
|
});
|
||||||
|
});*/
|
||||||
|
|
||||||
|
/*it('opens About modal and verify the displayed information', function() {
|
||||||
|
cy.get('[data-cy="options-dropdown"]')
|
||||||
|
.first()
|
||||||
|
.click();
|
||||||
|
cy.get('[data-cy="about-modal"]')
|
||||||
|
.as('aboutOverlay')
|
||||||
|
.should('be.visible');
|
||||||
|
|
||||||
|
//check buttons and links
|
||||||
|
cy.get('[data-cy="about-modal"]')
|
||||||
|
.should('contains.text', 'Visit the forum')
|
||||||
|
.and('contains.text', 'Report an issue')
|
||||||
|
.and('contains.text', 'https://github.com/OHIF/Viewers/');
|
||||||
|
|
||||||
|
//check version number
|
||||||
|
cy.get('[data-cy="about-modal"]').then($modal => {
|
||||||
|
cy.get('[data-cy="header-version-info"]').should($headerVersionNumber => {
|
||||||
|
$headerVersionNumber = $headerVersionNumber.text().substring(1);
|
||||||
|
expect($modal).to.contain($headerVersionNumber);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//close modal
|
||||||
|
cy.get('[data-cy="close-button"]').click();
|
||||||
|
cy.get('@aboutOverlay').should('not.exist');
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
});
|
||||||
@ -0,0 +1,213 @@
|
|||||||
|
describe('OHIF Measurement Panel', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
cy.checkStudyRouteInViewer(
|
||||||
|
'1.2.840.113619.2.5.1762583153.215519.978957063.78'
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.expectMinimumThumbnails(3);
|
||||||
|
cy.initCommonElementsAliases();
|
||||||
|
cy.initCornerstoneToolsAliases();
|
||||||
|
cy.resetViewport().wait(50);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('checks if Measurements right panel can be hidden/displayed', function() {
|
||||||
|
cy.get('@measurementsPanel').should('exist');
|
||||||
|
cy.get('@measurementsPanel').should('be.visible');
|
||||||
|
|
||||||
|
cy.get('@RightCollapseBtn').click();
|
||||||
|
cy.get('@measurementsPanel').should('not.exist');
|
||||||
|
|
||||||
|
cy.get('@RightCollapseBtn').click();
|
||||||
|
cy.get('@measurementsPanel').should('exist');
|
||||||
|
cy.get('@measurementsPanel').should('be.visible');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('checks if measurement item can be Relabeled under Measurements panel', function() {
|
||||||
|
// Add length measurement
|
||||||
|
cy.addLengthMeasurement();
|
||||||
|
cy.get('[data-cy="measurement-tracking-prompt-begin-tracking"]').should(
|
||||||
|
'exist'
|
||||||
|
);
|
||||||
|
cy.get('[data-cy="measurement-tracking-prompt-begin-tracking"]').should(
|
||||||
|
'be.visible'
|
||||||
|
);
|
||||||
|
cy.get('[data-cy="prompt-begin-tracking-yes"]').click();
|
||||||
|
cy.get('[data-cy="measurement-item"]').click();
|
||||||
|
|
||||||
|
cy.get('[data-cy="measurement-item"]')
|
||||||
|
.find('svg')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
// enter Bone label
|
||||||
|
cy.get('[data-cy="input-annotation"]').should('exist');
|
||||||
|
cy.get('[data-cy="input-annotation"]').should('be.visible');
|
||||||
|
cy.get('[data-cy="input-annotation"]').type('Bone{enter}');
|
||||||
|
|
||||||
|
// Verify if 'Bone' label was added
|
||||||
|
cy.get('[data-cy="measurement-item"]').should('contain.text', 'Bone');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('checks if image would jump when clicked on a measurement item', function() {
|
||||||
|
// Add length measurement
|
||||||
|
cy.addLengthMeasurement();
|
||||||
|
cy.get('[data-cy="prompt-begin-tracking-yes"]').click();
|
||||||
|
|
||||||
|
cy.scrollToIndex(13);
|
||||||
|
|
||||||
|
cy.addLengthMeasurement([100, 100], [200, 200]); //Adding measurement in the viewport
|
||||||
|
|
||||||
|
cy.get('@viewportInfoTopRight').should('contains.text', '(14/');
|
||||||
|
|
||||||
|
// Click on first measurement item
|
||||||
|
cy.get('[data-cy="measurement-item"]').click();
|
||||||
|
|
||||||
|
cy.get('@viewportInfoTopRight').should('contains.text', '(1/');
|
||||||
|
cy.get('@viewportInfoTopRight').should('not.contains.text', '(14/');
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO: Not sure why this is failing
|
||||||
|
it('checks if Description can be added to measurement item under Measurements panel', () => {
|
||||||
|
cy.addLengthMeasurement(); //Adding measurement in the viewport
|
||||||
|
cy.get('@measurementsBtn').click();
|
||||||
|
cy.get('.measurementItem').click();
|
||||||
|
|
||||||
|
// Click "Description"
|
||||||
|
cy.get('.btnAction')
|
||||||
|
.contains('Description')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
// Enter description text
|
||||||
|
const descriptionText = 'Adding text for description test';
|
||||||
|
cy.get('#description').type(descriptionText);
|
||||||
|
|
||||||
|
// Confirm
|
||||||
|
cy.get('.btn-confirm').click();
|
||||||
|
|
||||||
|
//Verify if descriptionText was added
|
||||||
|
cy.get('.measurementLocation').should('contain.text', descriptionText);
|
||||||
|
|
||||||
|
// Remove the measurement we just added
|
||||||
|
cy.get('.btnAction')
|
||||||
|
.last()
|
||||||
|
.contains('Delete')
|
||||||
|
.click()
|
||||||
|
|
||||||
|
// Close panel
|
||||||
|
cy.get('@measurementsBtn').click();
|
||||||
|
cy.get('@measurementsPanel').should('not.be.enabled');
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*it('checks if measurement item can be deleted through the context menu on the viewport', function() {
|
||||||
|
cy.addLengthMeasurement([100, 100], [200, 100]); //Adding measurement in the viewport
|
||||||
|
|
||||||
|
//Right click on measurement annotation
|
||||||
|
const [x1, y1] = [150, 100];
|
||||||
|
cy.get('@viewport')
|
||||||
|
.trigger('mousedown', x1, y1, {
|
||||||
|
which: 3,
|
||||||
|
})
|
||||||
|
.trigger('mouseup', x1, y1, {
|
||||||
|
which: 3,
|
||||||
|
})
|
||||||
|
.wait(300)
|
||||||
|
.then(() => {
|
||||||
|
//Contextmenu is visible
|
||||||
|
cy.get('.ToolContextMenu').should('be.visible');
|
||||||
|
});
|
||||||
|
|
||||||
|
//Click "Delete measurement"
|
||||||
|
cy.get('.form-action')
|
||||||
|
.contains('Delete measurement')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
//Open measurements menu
|
||||||
|
cy.get('@measurementsBtn').click();
|
||||||
|
|
||||||
|
//Verify measurements was removed from panel
|
||||||
|
cy.get('.measurementItem')
|
||||||
|
.should('not.exist')
|
||||||
|
.log('Annotation successfully removed');
|
||||||
|
|
||||||
|
//Close panel
|
||||||
|
cy.get('@measurementsBtn').click();
|
||||||
|
cy.get('@measurementsPanel').should('not.exist');
|
||||||
|
});*/
|
||||||
|
|
||||||
|
/*it('adds relabel and description to measurement item through the context menu on the viewport', function() {
|
||||||
|
cy.addLengthMeasurement([100, 100], [200, 100]); //Adding measurement in the viewport
|
||||||
|
|
||||||
|
// Relabel
|
||||||
|
// Right click on measurement annotation
|
||||||
|
const [x1, y1] = [150, 100];
|
||||||
|
cy.get('@viewport')
|
||||||
|
.trigger('mousedown', x1, y1, {
|
||||||
|
which: 3,
|
||||||
|
})
|
||||||
|
.trigger('mouseup', x1, y1, {
|
||||||
|
which: 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Contextmenu is visible
|
||||||
|
cy.get('.ToolContextMenu').should('be.visible');
|
||||||
|
|
||||||
|
// Click "Relabel"
|
||||||
|
cy.get('.form-action')
|
||||||
|
.contains('Relabel')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
// Search for "Brain"
|
||||||
|
cy.get('.searchInput').type('Brain');
|
||||||
|
|
||||||
|
// Select "Brain" Result
|
||||||
|
cy.get('.treeInputs > .wrapperLabel')
|
||||||
|
.contains('Brain')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
// Confirm Selection
|
||||||
|
cy.get('.checkIconWrapper').click();
|
||||||
|
|
||||||
|
// Description
|
||||||
|
// Right click on measurement annotation
|
||||||
|
cy.get('@viewport')
|
||||||
|
.trigger('mousedown', x1, y1, {
|
||||||
|
which: 3,
|
||||||
|
})
|
||||||
|
.trigger('mouseup', x1, y1, {
|
||||||
|
which: 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Contextmenu is visible
|
||||||
|
cy.get('.ToolContextMenu').should('be.visible');
|
||||||
|
|
||||||
|
// Click "Description"
|
||||||
|
cy.get('.form-action')
|
||||||
|
.contains('Add Description')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
// Enter description text
|
||||||
|
const descriptionText = 'Adding text for description test';
|
||||||
|
cy.get('#description').type(descriptionText);
|
||||||
|
|
||||||
|
// Confirm
|
||||||
|
cy.get('.btn-confirm').click();
|
||||||
|
|
||||||
|
//Open measurements menu
|
||||||
|
cy.get('@measurementsBtn').click();
|
||||||
|
|
||||||
|
// Verify if label was added
|
||||||
|
cy.get('.measurementLocation')
|
||||||
|
.should('contain.text', 'Brain')
|
||||||
|
.log('Relabel added with success');
|
||||||
|
|
||||||
|
//Verify if descriptionText was added
|
||||||
|
cy.get('.measurementLocation')
|
||||||
|
.should('contain.text', descriptionText)
|
||||||
|
.log('Description added with success');
|
||||||
|
|
||||||
|
// Close panel
|
||||||
|
cy.get('@measurementsBtn').click();
|
||||||
|
cy.get('@measurementsPanel').should('not.exist');
|
||||||
|
});*/
|
||||||
|
});
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
describe('OHIF Study Viewer Page', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
cy.checkStudyRouteInViewer(
|
||||||
|
'1.2.840.113619.2.5.1762583153.215519.978957063.78'
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.expectMinimumThumbnails(3);
|
||||||
|
cy.initCommonElementsAliases();
|
||||||
|
cy.initCornerstoneToolsAliases();
|
||||||
|
cy.resetViewport().wait(50);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('checks if series thumbnails are being displayed', function() {
|
||||||
|
cy.get('[data-cy="study-browser-thumbnail"]')
|
||||||
|
.its('length')
|
||||||
|
.should('be.gt', 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('drags and drop a series thumbnail into viewport', function() {
|
||||||
|
cy.get('[data-cy="study-browser-thumbnail"]:nth-child(2)') //element to be dragged
|
||||||
|
.drag('.cornerstone-canvas'); //dropzone element
|
||||||
|
|
||||||
|
//const expectedText =
|
||||||
|
// 'Ser: 2Img: 1 1/13512 x 512Loc: -17.60 mm Thick: 3.00 mm';
|
||||||
|
//cy.get('@viewportInfoBottomLeft').should('contain.text', expectedText);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('checks if Series left panel can be hidden/displayed', function() {
|
||||||
|
cy.get('@seriesPanel').should('exist');
|
||||||
|
cy.get('@seriesPanel').should('be.visible');
|
||||||
|
|
||||||
|
cy.get('@seriesBtn').click();
|
||||||
|
cy.get('@seriesPanel').should('not.exist');
|
||||||
|
|
||||||
|
cy.get('@seriesBtn').click();
|
||||||
|
cy.get('@seriesPanel').should('exist');
|
||||||
|
cy.get('@seriesPanel').should('be.visible');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('performs double-click to load thumbnail in active viewport', () => {
|
||||||
|
cy.get('[data-cy="study-browser-thumbnail"]:nth-child(2)').dblclick();
|
||||||
|
|
||||||
|
//cy.get('@viewportInfoBottomLeft').should('contains.text', expectedText);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,339 +0,0 @@
|
|||||||
describe('OHIF Study Viewer Page', function() {
|
|
||||||
before(() => {
|
|
||||||
cy.checkStudyRouteInViewer(
|
|
||||||
'1.2.840.113619.2.5.1762583153.215519.978957063.78'
|
|
||||||
);
|
|
||||||
|
|
||||||
cy.expectMinimumThumbnails(3);
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
cy.initCommonElementsAliases();
|
|
||||||
cy.initCornerstoneToolsAliases();
|
|
||||||
cy.resetViewport().wait(50);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('checks if series thumbnails are being displayed', function() {
|
|
||||||
cy.get('[data-cy="study-browser-thumbnail"]')
|
|
||||||
.its('length')
|
|
||||||
.should('be.gt', 1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('drags and drop a series thumbnail into viewport', function() {
|
|
||||||
cy.get('[data-cy="study-browser-thumbnail"]:nth-child(2)') //element to be dragged
|
|
||||||
.drag('.cornerstone-canvas'); //dropzone element
|
|
||||||
|
|
||||||
//const expectedText =
|
|
||||||
// 'Ser: 2Img: 1 1/13512 x 512Loc: -17.60 mm Thick: 3.00 mm';
|
|
||||||
//cy.get('@viewportInfoBottomLeft').should('contain.text', expectedText);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('checks if Series left panel can be hidden/displayed', function() {
|
|
||||||
cy.get('@seriesPanel').should('exist');
|
|
||||||
cy.get('@seriesPanel').should('be.visible');
|
|
||||||
|
|
||||||
cy.get('@seriesBtn').click();
|
|
||||||
cy.get('@seriesPanel').should('not.exist');
|
|
||||||
|
|
||||||
cy.get('@seriesBtn').click();
|
|
||||||
cy.get('@seriesPanel').should('exist');
|
|
||||||
cy.get('@seriesPanel').should('be.visible');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('checks if Measurements right panel can be hidden/displayed', function() {
|
|
||||||
cy.get('@measurementsPanel').should('exist');
|
|
||||||
cy.get('@measurementsPanel').should('be.visible');
|
|
||||||
|
|
||||||
cy.get('@RightCollapseBtn').click();
|
|
||||||
cy.get('@measurementsPanel').should('not.exist');
|
|
||||||
|
|
||||||
cy.get('@RightCollapseBtn').click();
|
|
||||||
cy.get('@measurementsPanel').should('exist');
|
|
||||||
cy.get('@measurementsPanel').should('be.visible');
|
|
||||||
});
|
|
||||||
|
|
||||||
/*it('checks if measurement item can be Relabeled under Measurements panel', function() {
|
|
||||||
// Add length measurement
|
|
||||||
cy.addLengthMeasurement();
|
|
||||||
cy.get('[data-cy="measurement-tracking-prompt-begin-tracking"]').should('exist')
|
|
||||||
cy.get('[data-cy="measurement-tracking-prompt-begin-tracking"]').should('be.visible')
|
|
||||||
cy.get('[data-cy="prompt-begin-tracking-yes"]').click();
|
|
||||||
cy.get('.measurementItem').click();
|
|
||||||
|
|
||||||
// Click "Relabel"
|
|
||||||
cy.get('.btnAction', { timeout: 10000 })
|
|
||||||
.first()
|
|
||||||
.contains('Relabel')
|
|
||||||
.click().should('be.visible');
|
|
||||||
|
|
||||||
// Search for "Bone"
|
|
||||||
cy.get('.searchInput').type('Bone');
|
|
||||||
|
|
||||||
// Select "Bone" Result
|
|
||||||
cy.get('.treeInputs > .wrapperLabel')
|
|
||||||
.contains('Bone')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
// Confirm Selection
|
|
||||||
cy.get('.checkIconWrapper').click();
|
|
||||||
|
|
||||||
// Verify if 'Bone' label was added
|
|
||||||
cy.get('.measurementLocation').should('contain.text', 'Bone');
|
|
||||||
|
|
||||||
// Remove the measurement we just added
|
|
||||||
cy.get('.btnAction')
|
|
||||||
.last()
|
|
||||||
.contains('Delete')
|
|
||||||
.click()
|
|
||||||
|
|
||||||
// Close panel
|
|
||||||
cy.get('@measurementsBtn').click();
|
|
||||||
cy.get('@measurementsPanel').should('not.exist');
|
|
||||||
});*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
TODO: Not sure why this is failing
|
|
||||||
it('checks if Description can be added to measurement item under Measurements panel', () => {
|
|
||||||
cy.addLengthMeasurement(); //Adding measurement in the viewport
|
|
||||||
cy.get('@measurementsBtn').click();
|
|
||||||
cy.get('.measurementItem').click();
|
|
||||||
|
|
||||||
// Click "Description"
|
|
||||||
cy.get('.btnAction')
|
|
||||||
.contains('Description')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
// Enter description text
|
|
||||||
const descriptionText = 'Adding text for description test';
|
|
||||||
cy.get('#description').type(descriptionText);
|
|
||||||
|
|
||||||
// Confirm
|
|
||||||
cy.get('.btn-confirm').click();
|
|
||||||
|
|
||||||
//Verify if descriptionText was added
|
|
||||||
cy.get('.measurementLocation').should('contain.text', descriptionText);
|
|
||||||
|
|
||||||
// Remove the measurement we just added
|
|
||||||
cy.get('.btnAction')
|
|
||||||
.last()
|
|
||||||
.contains('Delete')
|
|
||||||
.click()
|
|
||||||
|
|
||||||
// Close panel
|
|
||||||
cy.get('@measurementsBtn').click();
|
|
||||||
cy.get('@measurementsPanel').should('not.be.enabled');
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*it('checks if measurement item can be deleted through the context menu on the viewport', function() {
|
|
||||||
cy.addLengthMeasurement([100, 100], [200, 100]); //Adding measurement in the viewport
|
|
||||||
|
|
||||||
//Right click on measurement annotation
|
|
||||||
const [x1, y1] = [150, 100];
|
|
||||||
cy.get('@viewport')
|
|
||||||
.trigger('mousedown', x1, y1, {
|
|
||||||
which: 3,
|
|
||||||
})
|
|
||||||
.trigger('mouseup', x1, y1, {
|
|
||||||
which: 3,
|
|
||||||
})
|
|
||||||
.wait(300)
|
|
||||||
.then(() => {
|
|
||||||
//Contextmenu is visible
|
|
||||||
cy.get('.ToolContextMenu').should('be.visible');
|
|
||||||
});
|
|
||||||
|
|
||||||
//Click "Delete measurement"
|
|
||||||
cy.get('.form-action')
|
|
||||||
.contains('Delete measurement')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
//Open measurements menu
|
|
||||||
cy.get('@measurementsBtn').click();
|
|
||||||
|
|
||||||
//Verify measurements was removed from panel
|
|
||||||
cy.get('.measurementItem')
|
|
||||||
.should('not.exist')
|
|
||||||
.log('Annotation successfully removed');
|
|
||||||
|
|
||||||
//Close panel
|
|
||||||
cy.get('@measurementsBtn').click();
|
|
||||||
cy.get('@measurementsPanel').should('not.exist');
|
|
||||||
});*/
|
|
||||||
|
|
||||||
/*it('adds relabel and description to measurement item through the context menu on the viewport', function() {
|
|
||||||
cy.addLengthMeasurement([100, 100], [200, 100]); //Adding measurement in the viewport
|
|
||||||
|
|
||||||
// Relabel
|
|
||||||
// Right click on measurement annotation
|
|
||||||
const [x1, y1] = [150, 100];
|
|
||||||
cy.get('@viewport')
|
|
||||||
.trigger('mousedown', x1, y1, {
|
|
||||||
which: 3,
|
|
||||||
})
|
|
||||||
.trigger('mouseup', x1, y1, {
|
|
||||||
which: 3,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Contextmenu is visible
|
|
||||||
cy.get('.ToolContextMenu').should('be.visible');
|
|
||||||
|
|
||||||
// Click "Relabel"
|
|
||||||
cy.get('.form-action')
|
|
||||||
.contains('Relabel')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
// Search for "Brain"
|
|
||||||
cy.get('.searchInput').type('Brain');
|
|
||||||
|
|
||||||
// Select "Brain" Result
|
|
||||||
cy.get('.treeInputs > .wrapperLabel')
|
|
||||||
.contains('Brain')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
// Confirm Selection
|
|
||||||
cy.get('.checkIconWrapper').click();
|
|
||||||
|
|
||||||
// Description
|
|
||||||
// Right click on measurement annotation
|
|
||||||
cy.get('@viewport')
|
|
||||||
.trigger('mousedown', x1, y1, {
|
|
||||||
which: 3,
|
|
||||||
})
|
|
||||||
.trigger('mouseup', x1, y1, {
|
|
||||||
which: 3,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Contextmenu is visible
|
|
||||||
cy.get('.ToolContextMenu').should('be.visible');
|
|
||||||
|
|
||||||
// Click "Description"
|
|
||||||
cy.get('.form-action')
|
|
||||||
.contains('Add Description')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
// Enter description text
|
|
||||||
const descriptionText = 'Adding text for description test';
|
|
||||||
cy.get('#description').type(descriptionText);
|
|
||||||
|
|
||||||
// Confirm
|
|
||||||
cy.get('.btn-confirm').click();
|
|
||||||
|
|
||||||
//Open measurements menu
|
|
||||||
cy.get('@measurementsBtn').click();
|
|
||||||
|
|
||||||
// Verify if label was added
|
|
||||||
cy.get('.measurementLocation')
|
|
||||||
.should('contain.text', 'Brain')
|
|
||||||
.log('Relabel added with success');
|
|
||||||
|
|
||||||
//Verify if descriptionText was added
|
|
||||||
cy.get('.measurementLocation')
|
|
||||||
.should('contain.text', descriptionText)
|
|
||||||
.log('Description added with success');
|
|
||||||
|
|
||||||
// Close panel
|
|
||||||
cy.get('@measurementsBtn').click();
|
|
||||||
cy.get('@measurementsPanel').should('not.exist');
|
|
||||||
});*/
|
|
||||||
|
|
||||||
it('scrolls series stack using scrollbar', function() {
|
|
||||||
// Workaround implemented based on Cypress issue:
|
|
||||||
// https://github.com/cypress-io/cypress/issues/1570#issuecomment-450966053
|
|
||||||
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
|
|
||||||
window.HTMLInputElement.prototype,
|
|
||||||
'value'
|
|
||||||
).set;
|
|
||||||
|
|
||||||
cy.get('input.imageSlider[type=range]').then($range => {
|
|
||||||
// get the DOM node
|
|
||||||
const range = $range[0];
|
|
||||||
// set the value manually
|
|
||||||
nativeInputValueSetter.call(range, 13);
|
|
||||||
// now dispatch the event
|
|
||||||
range.dispatchEvent(new Event('change', { value: 13, bubbles: true }));
|
|
||||||
});
|
|
||||||
|
|
||||||
/*const expectedText =
|
|
||||||
'Ser: 2Img: 13 13/13512 x 512Loc: 18.40 mm Thick: 3.00 mm'; //'Img: 13 13/13';
|
|
||||||
cy.get('@viewportInfoBottomLeft').should('contains.text', expectedText);*/
|
|
||||||
});
|
|
||||||
|
|
||||||
it('performs double-click to load thumbnail in active viewport', () => {
|
|
||||||
cy.get('[data-cy="study-browser-thumbnail"]:nth-child(2)').dblclick();
|
|
||||||
|
|
||||||
//const expectedText = 'Ser: 3';
|
|
||||||
//cy.get('@viewportInfoBottomLeft').should('contains.text', expectedText);
|
|
||||||
});
|
|
||||||
|
|
||||||
// it('performs right click to zoom', function() {
|
|
||||||
// // This is not used to activate the tool, it is used to ensure the
|
|
||||||
// // top left viewport info shows the zoom values (it only shows up
|
|
||||||
// // when the zoom tool is active)
|
|
||||||
// cy.get('@zoomBtn')
|
|
||||||
// .click()
|
|
||||||
// .then($zoomBtn => {
|
|
||||||
// cy.wrap($zoomBtn).should('have.class', 'active');
|
|
||||||
// });
|
|
||||||
|
|
||||||
// //Right click on viewport
|
|
||||||
// cy.get('@viewport')
|
|
||||||
// .trigger('mousedown', 'top', { buttons: 2 })
|
|
||||||
// .trigger('mousemove', 'center', { buttons: 2 })
|
|
||||||
// .trigger('mouseup');
|
|
||||||
|
|
||||||
// const expectedText = 'Zoom:6.65x';
|
|
||||||
// cy.get('@viewportInfoTopLeft').should('contains.text', expectedText);
|
|
||||||
// });
|
|
||||||
|
|
||||||
/*it('performs middle click to pan', function() {
|
|
||||||
//Get image position from cornerstone and check if y axis was modified
|
|
||||||
let cornerstone;
|
|
||||||
let currentPan;
|
|
||||||
|
|
||||||
// TO DO: Replace the cornerstone pan check by Percy snapshot comparison
|
|
||||||
cy.window()
|
|
||||||
.its('cornerstone')
|
|
||||||
.then(c => {
|
|
||||||
cornerstone = c;
|
|
||||||
currentPan = () =>
|
|
||||||
cornerstone.getEnabledElements()[0].viewport.translation;
|
|
||||||
});
|
|
||||||
|
|
||||||
//pan image with middle click
|
|
||||||
cy.get('@viewport')
|
|
||||||
.trigger('mousedown', 'center', { buttons: 3 })
|
|
||||||
.trigger('mousemove', 'bottom', { buttons: 3 })
|
|
||||||
.trigger('mouseup', 'bottom')
|
|
||||||
.then(() => {
|
|
||||||
expect(currentPan().y > 0).to.eq(true);
|
|
||||||
});
|
|
||||||
});*/
|
|
||||||
|
|
||||||
/*it('opens About modal and verify the displayed information', function() {
|
|
||||||
cy.get('[data-cy="options-dropdown"]')
|
|
||||||
.first()
|
|
||||||
.click();
|
|
||||||
cy.get('[data-cy="about-modal"]')
|
|
||||||
.as('aboutOverlay')
|
|
||||||
.should('be.visible');
|
|
||||||
|
|
||||||
//check buttons and links
|
|
||||||
cy.get('[data-cy="about-modal"]')
|
|
||||||
.should('contains.text', 'Visit the forum')
|
|
||||||
.and('contains.text', 'Report an issue')
|
|
||||||
.and('contains.text', 'https://github.com/OHIF/Viewers/');
|
|
||||||
|
|
||||||
//check version number
|
|
||||||
cy.get('[data-cy="about-modal"]').then($modal => {
|
|
||||||
cy.get('[data-cy="header-version-info"]').should($headerVersionNumber => {
|
|
||||||
$headerVersionNumber = $headerVersionNumber.text().substring(1);
|
|
||||||
expect($modal).to.contain($headerVersionNumber);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//close modal
|
|
||||||
cy.get('[data-cy="close-button"]').click();
|
|
||||||
cy.get('@aboutOverlay').should('not.exist');
|
|
||||||
});*/
|
|
||||||
});
|
|
||||||
@ -1,12 +1,10 @@
|
|||||||
//We are keeping the hardcoded results values for the study list tests
|
//We are keeping the hardcoded results values for the study list tests
|
||||||
//this is intended to be running in a controled docker environment with test data.
|
//this is intended to be running in a controled docker environment with test data.
|
||||||
describe('OHIF Study List', function () {
|
describe('OHIF Study List', function() {
|
||||||
context('Desktop resolution', function () {
|
context('Desktop resolution', function() {
|
||||||
before(function () {
|
beforeEach(function() {
|
||||||
cy.openStudyList();
|
cy.openStudyList();
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
cy.viewport(1750, 720);
|
cy.viewport(1750, 720);
|
||||||
cy.initStudyListAliasesOnDesktop();
|
cy.initStudyListAliasesOnDesktop();
|
||||||
//Clear all text fields
|
//Clear all text fields
|
||||||
@ -14,14 +12,9 @@ describe('OHIF Study List', function () {
|
|||||||
cy.get('@MRN').clear();
|
cy.get('@MRN').clear();
|
||||||
cy.get('@AccessionNumber').clear();
|
cy.get('@AccessionNumber').clear();
|
||||||
cy.get('@StudyDescription').clear();
|
cy.get('@StudyDescription').clear();
|
||||||
|
|
||||||
// TODO: Find out how to clear this input
|
|
||||||
// select-2 / react-select has some specific ways
|
|
||||||
// to trigger things
|
|
||||||
//cy.get('@modalities')
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Displays several studies initially', function () {
|
it('Displays several studies initially', function() {
|
||||||
cy.waitStudyList();
|
cy.waitStudyList();
|
||||||
cy.get('@searchResult2').should($list => {
|
cy.get('@searchResult2').should($list => {
|
||||||
expect($list.length).to.be.greaterThan(1);
|
expect($list.length).to.be.greaterThan(1);
|
||||||
@ -30,7 +23,7 @@ describe('OHIF Study List', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('searches Patient Name with exact string', function () {
|
it('searches Patient Name with exact string', function() {
|
||||||
cy.get('@PatientName').type('Juno');
|
cy.get('@PatientName').type('Juno');
|
||||||
//Wait result list to be displayed
|
//Wait result list to be displayed
|
||||||
cy.waitStudyList();
|
cy.waitStudyList();
|
||||||
@ -40,8 +33,7 @@ describe('OHIF Study List', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
/* TODO - re-enable this once the test server is fixed to allow searching by mrn
|
it('searches MRN with exact string', function() {
|
||||||
it('searches MRN with exact string', function () {
|
|
||||||
cy.get('@MRN').type('0000003');
|
cy.get('@MRN').type('0000003');
|
||||||
//Wait result list to be displayed
|
//Wait result list to be displayed
|
||||||
cy.waitStudyList();
|
cy.waitStudyList();
|
||||||
@ -50,31 +42,17 @@ describe('OHIF Study List', function () {
|
|||||||
expect($list).to.contain('0000003');
|
expect($list).to.contain('0000003');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
*/
|
|
||||||
|
|
||||||
it('searches Accession with exact string', function () {
|
it('searches Accession with exact string', function() {
|
||||||
cy.get('@AccessionNumber').type('0000155811');
|
cy.get('@AccessionNumber').type('321');
|
||||||
//Wait result list to be displayed
|
//Wait result list to be displayed
|
||||||
cy.waitStudyList();
|
cy.waitStudyList();
|
||||||
cy.get('@searchResult2').should($list => {
|
cy.get('@searchResult2').should($list => {
|
||||||
expect($list.length).to.be.eq(1);
|
expect($list.length).to.be.eq(1);
|
||||||
expect($list).to.contain('0000155811');
|
expect($list).to.contain('321');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: need to be able to programmatically change react-select
|
|
||||||
/*it('searches Modality with camel case', function() {
|
|
||||||
cy.get('@modalities').type('Ct');
|
|
||||||
//Wait result list to be displayed
|
|
||||||
cy.waitStudyList();
|
|
||||||
cy.get('@searchResult2').should($list => {
|
|
||||||
expect($list.length).to.be.greaterThan(1);
|
|
||||||
expect($list).to.contain('CT');
|
|
||||||
});
|
|
||||||
});*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
TODO: Currently broken in dicomweb-server
|
|
||||||
it('searches Description with exact string', function() {
|
it('searches Description with exact string', function() {
|
||||||
cy.get('@StudyDescription').type('PETCT');
|
cy.get('@StudyDescription').type('PETCT');
|
||||||
//Wait result list to be displayed
|
//Wait result list to be displayed
|
||||||
@ -84,10 +62,19 @@ describe('OHIF Study List', function () {
|
|||||||
expect($list).to.contain('PETCT');
|
expect($list).to.contain('PETCT');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
*/
|
|
||||||
|
|
||||||
// TODO: need to be able to programmatically change react-select
|
/* Todo: fix react select
|
||||||
/*it('changes Rows per page and checks the study count', function() {
|
it('searches Modality with camel case', function() {
|
||||||
|
cy.get('@modalities').type('Ct');
|
||||||
|
// Wait result list to be displayed
|
||||||
|
cy.waitStudyList();
|
||||||
|
cy.get('@searchResult2').should($list => {
|
||||||
|
expect($list.length).to.be.greaterThan(1);
|
||||||
|
expect($list).to.contain('CT');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('changes Rows per page and checks the study count', function() {
|
||||||
//Show Rows per page options
|
//Show Rows per page options
|
||||||
const pageRows = [25, 50, 100];
|
const pageRows = [25, 50, 100];
|
||||||
|
|
||||||
|
|||||||
139
platform/viewer/cypress/integration/volume/MPR.spec.js
Normal file
139
platform/viewer/cypress/integration/volume/MPR.spec.js
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
describe('OHIF MPR', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.checkStudyRouteInViewer(
|
||||||
|
'1.3.6.1.4.1.25403.345050719074.3824.20170125113417.1'
|
||||||
|
);
|
||||||
|
cy.expectMinimumThumbnails(3);
|
||||||
|
cy.initCornerstoneToolsAliases();
|
||||||
|
cy.initCommonElementsAliases();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not go MPR for non reconstructible displaySets', () => {
|
||||||
|
cy.get('[data-cy="MPR"]').click();
|
||||||
|
cy.get('.cornerstone-canvas').should('have.length', 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should go MPR for reconstructible displaySets and come back', () => {
|
||||||
|
cy.wait(1000);
|
||||||
|
cy.get(':nth-child(3) > [data-cy="study-browser-thumbnail"]').dblclick();
|
||||||
|
|
||||||
|
cy.get('[data-cy="MPR"]').click();
|
||||||
|
|
||||||
|
cy.get('.cornerstone-canvas').should('have.length', 3);
|
||||||
|
|
||||||
|
cy.get('[data-cy="MPR"]').click();
|
||||||
|
|
||||||
|
cy.get('.cornerstone-canvas').should('have.length', 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render correctly the MPR', () => {
|
||||||
|
cy.wait(1000);
|
||||||
|
|
||||||
|
cy.get(':nth-child(3) > [data-cy="study-browser-thumbnail"]').dblclick();
|
||||||
|
cy.get('[data-cy="MPR"]').click();
|
||||||
|
|
||||||
|
cy.get('[data-cy="thumbnail-viewport-labels"]').should('have.length', 3);
|
||||||
|
|
||||||
|
cy.get('.cornerstone-canvas').should('have.length', 3);
|
||||||
|
|
||||||
|
cy.get('[data-cy="thumbnail-viewport-labels"]')
|
||||||
|
.eq(2)
|
||||||
|
.find('div')
|
||||||
|
.should('have.length', 3)
|
||||||
|
.each(($div, index) => {
|
||||||
|
const text = $div.text();
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
expect(text).to.equal('A');
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
expect(text).to.equal('B');
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
expect(text).to.equal('C');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`Unexpected div found with text: ${text}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// check cornerstone to see if each has images
|
||||||
|
// we can later do visual testing to match the images with a baseline
|
||||||
|
cy.window()
|
||||||
|
.its('cornerstone')
|
||||||
|
.then(cornerstone => {
|
||||||
|
const viewports = cornerstone.getRenderingEngines()[0].getViewports();
|
||||||
|
|
||||||
|
const imageData1 = viewports[0].getImageData();
|
||||||
|
const imageData2 = viewports[1].getImageData();
|
||||||
|
const imageData3 = viewports[2].getImageData();
|
||||||
|
|
||||||
|
// for some reason map doesn't work here
|
||||||
|
cy.wrap(imageData1).should('not.be', undefined);
|
||||||
|
cy.wrap(imageData2).should('not.be', undefined);
|
||||||
|
cy.wrap(imageData3).should('not.be', undefined);
|
||||||
|
|
||||||
|
cy.wrap(imageData1.dimensions).should(
|
||||||
|
'deep.equal',
|
||||||
|
imageData2.dimensions
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.wrap(imageData1.origin).should('deep.equal', imageData2.origin);
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.get('[data-cy="MPR"]').click();
|
||||||
|
|
||||||
|
cy.get('.cornerstone-canvas').should('have.length', 1);
|
||||||
|
|
||||||
|
// should not have any div under it
|
||||||
|
cy.get('[data-cy="thumbnail-viewport-labels"]')
|
||||||
|
.eq(2)
|
||||||
|
.find('div')
|
||||||
|
.should('have.length', 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly render Crosshairs for MPR', () => {
|
||||||
|
cy.wait(1000);
|
||||||
|
|
||||||
|
cy.get('[data-cy="Crosshairs"]').click();
|
||||||
|
cy.window()
|
||||||
|
.its('cornerstoneTools')
|
||||||
|
.then(cornerstoneTools => {
|
||||||
|
const state = cornerstoneTools.annotation.state.getDefaultAnnotationManager();
|
||||||
|
|
||||||
|
const fORMap = state.annotations;
|
||||||
|
// it should not have crosshairs yet
|
||||||
|
expect(Object.keys(fORMap)).to.have.length(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.get(':nth-child(3) > [data-cy="study-browser-thumbnail"]').dblclick();
|
||||||
|
cy.get('[data-cy="MPR"]').click();
|
||||||
|
cy.get('[data-cy="Crosshairs"]').click();
|
||||||
|
|
||||||
|
cy.wait(1000);
|
||||||
|
|
||||||
|
// check cornerstone to see if each has crosshairs
|
||||||
|
// we can later do visual testing to match the images with a baseline
|
||||||
|
cy.window()
|
||||||
|
.its('cornerstoneTools')
|
||||||
|
.then(cornerstoneTools => {
|
||||||
|
const state = cornerstoneTools.annotation.state.getDefaultAnnotationManager();
|
||||||
|
|
||||||
|
const fORMap = state.annotations;
|
||||||
|
const fOR = Object.keys(fORMap)[0];
|
||||||
|
const fORAnnotation = fORMap[fOR];
|
||||||
|
|
||||||
|
// it should have crosshairs as the only key
|
||||||
|
expect(Object.keys(fORAnnotation)).to.have.length(1);
|
||||||
|
|
||||||
|
const crosshairs = fORAnnotation.Crosshairs;
|
||||||
|
|
||||||
|
// it should have three
|
||||||
|
expect(crosshairs).to.have.length(3);
|
||||||
|
|
||||||
|
expect(crosshairs[0].data.handles.toolCenter).to.deep.equal(
|
||||||
|
crosshairs[1].data.handles.toolCenter
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -18,6 +18,7 @@ export function initCornerstoneToolsAliases() {
|
|||||||
cy.get('[data-cy="MoreTools-split-button-secondary"]').as('moreBtnSecondary');
|
cy.get('[data-cy="MoreTools-split-button-secondary"]').as('moreBtnSecondary');
|
||||||
cy.get('[data-cy="Layout"]').as('layoutBtn');
|
cy.get('[data-cy="Layout"]').as('layoutBtn');
|
||||||
cy.get('.cornerstone-viewport-element').as('viewport');
|
cy.get('.cornerstone-viewport-element').as('viewport');
|
||||||
|
cy.get('[data-cy="MPR"]').as('mprBtn');
|
||||||
}
|
}
|
||||||
|
|
||||||
//Creating aliases for Common page elements
|
//Creating aliases for Common page elements
|
||||||
@ -49,9 +50,16 @@ export function initCommonElementsAliases() {
|
|||||||
|
|
||||||
//Creating aliases for Routes
|
//Creating aliases for Routes
|
||||||
export function initRouteAliases() {
|
export function initRouteAliases() {
|
||||||
cy.server();
|
cy.intercept('GET', '**/series**', { statusCode: 200, body: [] }).as(
|
||||||
cy.route('GET', '**/series**').as('getStudySeries');
|
'getStudySeries'
|
||||||
cy.route('GET', '**/studies**').as('getStudies');
|
);
|
||||||
|
|
||||||
|
// Todo: for some reason cypress does not redirect to the correct url
|
||||||
|
// so we intercept the request and redirect it to the correct url
|
||||||
|
cy.intercept('/studies?limit*', req => {
|
||||||
|
const url = req.url.replace(/\/studies\?/, '/studies/?limit');
|
||||||
|
req.url = url;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//Creating aliases for Study List page elements on Desktop experience
|
//Creating aliases for Study List page elements on Desktop experience
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import {
|
|||||||
initCornerstoneToolsAliases,
|
initCornerstoneToolsAliases,
|
||||||
initCommonElementsAliases,
|
initCommonElementsAliases,
|
||||||
initRouteAliases,
|
initRouteAliases,
|
||||||
initVTKToolsAliases,
|
|
||||||
initStudyListAliasesOnDesktop,
|
initStudyListAliasesOnDesktop,
|
||||||
initStudyListAliasesOnTablet,
|
initStudyListAliasesOnTablet,
|
||||||
initPreferencesModalAliases,
|
initPreferencesModalAliases,
|
||||||
@ -46,7 +45,9 @@ import {
|
|||||||
Cypress.Commands.add('openStudy', PatientName => {
|
Cypress.Commands.add('openStudy', PatientName => {
|
||||||
cy.openStudyList();
|
cy.openStudyList();
|
||||||
cy.get('#filter-patientNameOrId').type(PatientName);
|
cy.get('#filter-patientNameOrId').type(PatientName);
|
||||||
cy.wait('@getStudies');
|
// cy.get('@getStudies').then(() => {
|
||||||
|
cy.wait(1000);
|
||||||
|
|
||||||
cy.get('[data-cy="study-list-results"]', { timeout: 5000 })
|
cy.get('[data-cy="study-list-results"]', { timeout: 5000 })
|
||||||
.contains(PatientName)
|
.contains(PatientName)
|
||||||
.first()
|
.first()
|
||||||
@ -56,15 +57,16 @@ Cypress.Commands.add('openStudy', PatientName => {
|
|||||||
Cypress.Commands.add('checkStudyRouteInViewer', StudyInstanceUID => {
|
Cypress.Commands.add('checkStudyRouteInViewer', StudyInstanceUID => {
|
||||||
cy.location('pathname').then($url => {
|
cy.location('pathname').then($url => {
|
||||||
cy.log($url);
|
cy.log($url);
|
||||||
if ($url == 'blank' || !$url.includes(`/viewer/${StudyInstanceUID}`)) {
|
if ($url == 'blank' || !$url.includes(`/basic-test/${StudyInstanceUID}`)) {
|
||||||
cy.openStudyInViewer(StudyInstanceUID);
|
cy.openStudyInViewer(StudyInstanceUID);
|
||||||
cy.waitDicomImage();
|
cy.waitDicomImage();
|
||||||
|
cy.wait(2000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Cypress.Commands.add('openStudyInViewer', StudyInstanceUID => {
|
Cypress.Commands.add('openStudyInViewer', StudyInstanceUID => {
|
||||||
cy.visit(`/viewer?StudyInstanceUIDs=${StudyInstanceUID}`);
|
cy.visit(`/basic-test?StudyInstanceUIDs=${StudyInstanceUID}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,16 +91,20 @@ Cypress.Commands.add('openStudyModality', Modality => {
|
|||||||
/**
|
/**
|
||||||
* Command to wait and check if a new page was loaded
|
* Command to wait and check if a new page was loaded
|
||||||
*
|
*
|
||||||
* @param {string} url - part of the expected url. Default value is /viewer
|
* @param {string} url - part of the expected url. Default value is /basic-test
|
||||||
*/
|
*/
|
||||||
Cypress.Commands.add('isPageLoaded', (url = '/viewer') => {
|
Cypress.Commands.add('isPageLoaded', (url = '/basic-test') => {
|
||||||
return cy.location('pathname', { timeout: 60000 }).should('include', url);
|
return cy.location('pathname', { timeout: 60000 }).should('include', url);
|
||||||
});
|
});
|
||||||
|
|
||||||
Cypress.Commands.add('openStudyList', () => {
|
Cypress.Commands.add('openStudyList', () => {
|
||||||
cy.initRouteAliases();
|
cy.initRouteAliases();
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
cy.wait('@getStudies');
|
|
||||||
|
// For some reason cypress 12.x does not like to stub the network request
|
||||||
|
// so we just wait herer for 1 second
|
||||||
|
// cy.wait('@getStudies');
|
||||||
|
cy.wait(1000);
|
||||||
});
|
});
|
||||||
|
|
||||||
Cypress.Commands.add('waitStudyList', () => {
|
Cypress.Commands.add('waitStudyList', () => {
|
||||||
@ -450,6 +456,29 @@ Cypress.Commands.add('openPreferences', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Cypress.Commands.add('scrollToIndex', index => {
|
||||||
|
// Workaround implemented based on Cypress issue:
|
||||||
|
// https://github.com/cypress-io/cypress/issues/1570#issuecomment-450966053
|
||||||
|
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
|
||||||
|
window.HTMLInputElement.prototype,
|
||||||
|
'value'
|
||||||
|
).set;
|
||||||
|
|
||||||
|
cy.get('input.imageSlider[type=range]').then($range => {
|
||||||
|
// get the DOM node
|
||||||
|
const range = $range[0];
|
||||||
|
// set the value manually
|
||||||
|
nativeInputValueSetter.call(range, index);
|
||||||
|
// now dispatch the event
|
||||||
|
range.dispatchEvent(
|
||||||
|
new Event('change', {
|
||||||
|
value: index,
|
||||||
|
bubbles: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
Cypress.Commands.add('closePreferences', () => {
|
Cypress.Commands.add('closePreferences', () => {
|
||||||
cy.log('Close User Preferences Modal');
|
cy.log('Close User Preferences Modal');
|
||||||
|
|
||||||
@ -596,3 +625,14 @@ Cypress.Commands.add('setLanguage', (language, save = true) => {
|
|||||||
.click();
|
.click();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// hide noisy logs
|
||||||
|
// https://github.com/cypress-io/cypress/issues/7362
|
||||||
|
// uncomment this if you really need the network logs
|
||||||
|
const origLog = Cypress.log;
|
||||||
|
Cypress.log = function(opts, ...other) {
|
||||||
|
if (opts.displayName === 'script' || opts.name === 'request') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return origLog(opts, ...other);
|
||||||
|
};
|
||||||
|
|||||||
@ -46,7 +46,7 @@
|
|||||||
"README.md"
|
"README.md"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.16.3",
|
"@babel/runtime": "^7.20.13",
|
||||||
"@ohif/core": "^3.0.0",
|
"@ohif/core": "^3.0.0",
|
||||||
"@ohif/extension-cornerstone": "^3.0.0",
|
"@ohif/extension-cornerstone": "^3.0.0",
|
||||||
"@ohif/extension-cornerstone-dicom-seg": "^3.0.0",
|
"@ohif/extension-cornerstone-dicom-seg": "^3.0.0",
|
||||||
@ -54,8 +54,10 @@
|
|||||||
"@ohif/extension-default": "^3.0.0",
|
"@ohif/extension-default": "^3.0.0",
|
||||||
"@ohif/extension-dicom-pdf": "^3.0.1",
|
"@ohif/extension-dicom-pdf": "^3.0.1",
|
||||||
"@ohif/extension-dicom-video": "^3.0.1",
|
"@ohif/extension-dicom-video": "^3.0.1",
|
||||||
|
"@ohif/extension-test": "0.0.1",
|
||||||
"@ohif/i18n": "^1.0.0",
|
"@ohif/i18n": "^1.0.0",
|
||||||
"@ohif/mode-basic-dev-mode": "^3.0.0",
|
"@ohif/mode-basic-dev-mode": "^3.0.0",
|
||||||
|
"@ohif/mode-test": "^0.0.1",
|
||||||
"@ohif/mode-longitudinal": "^3.0.0",
|
"@ohif/mode-longitudinal": "^3.0.0",
|
||||||
"@ohif/ui": "^2.0.0",
|
"@ohif/ui": "^2.0.0",
|
||||||
"@types/react": "^17.0.38",
|
"@types/react": "^17.0.38",
|
||||||
@ -82,13 +84,13 @@
|
|||||||
"react-dropzone": "^10.1.7",
|
"react-dropzone": "^10.1.7",
|
||||||
"react-i18next": "^10.11.0",
|
"react-i18next": "^10.11.0",
|
||||||
"react-resize-detector": "^6.7.6",
|
"react-resize-detector": "^6.7.6",
|
||||||
"react-router": "^6.3.0",
|
"react-router": "^6.8.1",
|
||||||
"react-router-dom": "^6.3.0"
|
"react-router-dom": "^6.8.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/plugin-proposal-private-methods": "^7.18.6",
|
"@babel/plugin-proposal-private-methods": "^7.18.6",
|
||||||
"@percy/cypress": "^3.1.1",
|
"@percy/cypress": "^3.1.1",
|
||||||
"cypress": "^9.5.4",
|
"cypress": "^12.6.0",
|
||||||
"cypress-file-upload": "^3.5.3",
|
"cypress-file-upload": "^3.5.3",
|
||||||
"glob": "^8.0.3",
|
"glob": "^8.0.3",
|
||||||
"identity-obj-proxy": "3.0.x",
|
"identity-obj-proxy": "3.0.x",
|
||||||
|
|||||||
@ -6,6 +6,8 @@ window.config = {
|
|||||||
showStudyList: true,
|
showStudyList: true,
|
||||||
// below flag is for performance reasons, but it might not work for all servers
|
// below flag is for performance reasons, but it might not work for all servers
|
||||||
omitQuotationForMultipartRequest: true,
|
omitQuotationForMultipartRequest: true,
|
||||||
|
showWarningMessageForCrossOrigin: true,
|
||||||
|
showCPUFallbackMessage: true,
|
||||||
showLoadingIndicator: true,
|
showLoadingIndicator: true,
|
||||||
// filterQueryParam: false,
|
// filterQueryParam: false,
|
||||||
dataSources: [
|
dataSources: [
|
||||||
|
|||||||
@ -12,6 +12,8 @@ window.config = {
|
|||||||
maxNumberOfWebWorkers: 3,
|
maxNumberOfWebWorkers: 3,
|
||||||
// below flag is for performance reasons, but it might not work for all servers
|
// below flag is for performance reasons, but it might not work for all servers
|
||||||
omitQuotationForMultipartRequest: true,
|
omitQuotationForMultipartRequest: true,
|
||||||
|
showWarningMessageForCrossOrigin: true,
|
||||||
|
showCPUFallbackMessage: true,
|
||||||
showLoadingIndicator: true,
|
showLoadingIndicator: true,
|
||||||
maxNumRequests: {
|
maxNumRequests: {
|
||||||
interaction: 100,
|
interaction: 100,
|
||||||
|
|||||||
@ -4,6 +4,8 @@ window.config = {
|
|||||||
showStudyList: true,
|
showStudyList: true,
|
||||||
// below flag is for performance reasons, but it might not work for all servers
|
// below flag is for performance reasons, but it might not work for all servers
|
||||||
omitQuotationForMultipartRequest: true,
|
omitQuotationForMultipartRequest: true,
|
||||||
|
showWarningMessageForCrossOrigin: true,
|
||||||
|
showCPUFallbackMessage: true,
|
||||||
servers: {
|
servers: {
|
||||||
dicomWeb: [
|
dicomWeb: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -6,6 +6,8 @@ window.config = {
|
|||||||
showStudyList: true,
|
showStudyList: true,
|
||||||
// below flag is for performance reasons, but it might not work for all servers
|
// below flag is for performance reasons, but it might not work for all servers
|
||||||
omitQuotationForMultipartRequest: true,
|
omitQuotationForMultipartRequest: true,
|
||||||
|
showWarningMessageForCrossOrigin: true,
|
||||||
|
showCPUFallbackMessage: true,
|
||||||
showLoadingIndicator: true,
|
showLoadingIndicator: true,
|
||||||
// filterQueryParam: false,
|
// filterQueryParam: false,
|
||||||
dataSources: [
|
dataSources: [
|
||||||
|
|||||||
@ -7,6 +7,8 @@ window.config = {
|
|||||||
maxNumberOfWebWorkers: 3,
|
maxNumberOfWebWorkers: 3,
|
||||||
// below flag is for performance reasons, but it might not work for all servers
|
// below flag is for performance reasons, but it might not work for all servers
|
||||||
omitQuotationForMultipartRequest: true,
|
omitQuotationForMultipartRequest: true,
|
||||||
|
showWarningMessageForCrossOrigin: true,
|
||||||
|
showCPUFallbackMessage: true,
|
||||||
showLoadingIndicator: true,
|
showLoadingIndicator: true,
|
||||||
// filterQueryParam: false,
|
// filterQueryParam: false,
|
||||||
dataSources: [
|
dataSources: [
|
||||||
|
|||||||
@ -5,6 +5,8 @@ window.config = {
|
|||||||
modes: [],
|
modes: [],
|
||||||
// below flag is for performance reasons, but it might not work for all servers
|
// below flag is for performance reasons, but it might not work for all servers
|
||||||
omitQuotationForMultipartRequest: true,
|
omitQuotationForMultipartRequest: true,
|
||||||
|
showWarningMessageForCrossOrigin: true,
|
||||||
|
showCPUFallbackMessage: true,
|
||||||
showLoadingIndicator: true,
|
showLoadingIndicator: true,
|
||||||
dataSources: [
|
dataSources: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -3,6 +3,8 @@ window.config = {
|
|||||||
showStudyList: true,
|
showStudyList: true,
|
||||||
// below flag is for performance reasons, but it might not work for all servers
|
// below flag is for performance reasons, but it might not work for all servers
|
||||||
omitQuotationForMultipartRequest: true,
|
omitQuotationForMultipartRequest: true,
|
||||||
|
showWarningMessageForCrossOrigin: true,
|
||||||
|
showCPUFallbackMessage: true,
|
||||||
showLoadingIndicator: true,
|
showLoadingIndicator: true,
|
||||||
servers: {
|
servers: {
|
||||||
// This is an array, but we'll only use the first entry for now
|
// This is an array, but we'll only use the first entry for now
|
||||||
|
|||||||
@ -5,6 +5,8 @@ window.config = {
|
|||||||
modes: [],
|
modes: [],
|
||||||
// below flag is for performance reasons, but it might not work for all servers
|
// below flag is for performance reasons, but it might not work for all servers
|
||||||
omitQuotationForMultipartRequest: true,
|
omitQuotationForMultipartRequest: true,
|
||||||
|
showWarningMessageForCrossOrigin: true,
|
||||||
|
showCPUFallbackMessage: true,
|
||||||
showLoadingIndicator: true,
|
showLoadingIndicator: true,
|
||||||
dataSources: [
|
dataSources: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
window.config = {
|
window.config = {
|
||||||
routerBasename: '/',
|
routerBasename: '/',
|
||||||
// whiteLabelling: {},
|
|
||||||
extensions: [],
|
extensions: [],
|
||||||
modes: [],
|
modes: ['@ohif/mode-test'],
|
||||||
showStudyList: true,
|
showStudyList: true,
|
||||||
// below flag is for performance reasons, but it might not work for all servers
|
// below flag is for performance reasons, but it might not work for all servers
|
||||||
omitQuotationForMultipartRequest: true,
|
omitQuotationForMultipartRequest: true,
|
||||||
|
showWarningMessageForCrossOrigin: false,
|
||||||
|
showCPUFallbackMessage: false,
|
||||||
// filterQueryParam: false,
|
// filterQueryParam: false,
|
||||||
dataSources: [
|
dataSources: [
|
||||||
{
|
{
|
||||||
@ -13,7 +14,9 @@ window.config = {
|
|||||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||||
sourceName: 'dicomweb',
|
sourceName: 'dicomweb',
|
||||||
configuration: {
|
configuration: {
|
||||||
name: 'DCM4CHEE',
|
// The most important field to set for static WADO
|
||||||
|
staticWado: true,
|
||||||
|
name: 'StaticWADO',
|
||||||
wadoUriRoot: '/viewer-testdata',
|
wadoUriRoot: '/viewer-testdata',
|
||||||
qidoRoot: '/viewer-testdata',
|
qidoRoot: '/viewer-testdata',
|
||||||
wadoRoot: '/viewer-testdata',
|
wadoRoot: '/viewer-testdata',
|
||||||
@ -24,7 +27,6 @@ window.config = {
|
|||||||
enableStudyLazyLoad: true,
|
enableStudyLazyLoad: true,
|
||||||
supportsFuzzyMatching: false,
|
supportsFuzzyMatching: false,
|
||||||
supportsWildcard: true,
|
supportsWildcard: true,
|
||||||
staticWado: true,
|
|
||||||
singlepart: 'video,thumbnail,pdf',
|
singlepart: 'video,thumbnail,pdf',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -3,6 +3,8 @@ window.config = {
|
|||||||
enableGoogleCloudAdapter: false,
|
enableGoogleCloudAdapter: false,
|
||||||
// below flag is for performance reasons, but it might not work for all servers
|
// below flag is for performance reasons, but it might not work for all servers
|
||||||
omitQuotationForMultipartRequest: true,
|
omitQuotationForMultipartRequest: true,
|
||||||
|
showWarningMessageForCrossOrigin: true,
|
||||||
|
showCPUFallbackMessage: true,
|
||||||
showLoadingIndicator: true,
|
showLoadingIndicator: true,
|
||||||
// This is an array, but we'll only use the first entry for now
|
// This is an array, but we'll only use the first entry for now
|
||||||
oidc: [
|
oidc: [
|
||||||
|
|||||||
@ -3,6 +3,8 @@ window.config = {
|
|||||||
enableGoogleCloudAdapter: true,
|
enableGoogleCloudAdapter: true,
|
||||||
// below flag is for performance reasons, but it might not work for all servers
|
// below flag is for performance reasons, but it might not work for all servers
|
||||||
omitQuotationForMultipartRequest: true,
|
omitQuotationForMultipartRequest: true,
|
||||||
|
showWarningMessageForCrossOrigin: true,
|
||||||
|
showCPUFallbackMessage: true,
|
||||||
showLoadingIndicator: true,
|
showLoadingIndicator: true,
|
||||||
servers: {
|
servers: {
|
||||||
// This is an array, but we'll only use the first entry for now
|
// This is an array, but we'll only use the first entry for now
|
||||||
|
|||||||
@ -5,6 +5,8 @@ window.config = {
|
|||||||
modes: [],
|
modes: [],
|
||||||
// below flag is for performance reasons, but it might not work for all servers
|
// below flag is for performance reasons, but it might not work for all servers
|
||||||
omitQuotationForMultipartRequest: true,
|
omitQuotationForMultipartRequest: true,
|
||||||
|
showWarningMessageForCrossOrigin: true,
|
||||||
|
showCPUFallbackMessage: true,
|
||||||
showLoadingIndicator: true,
|
showLoadingIndicator: true,
|
||||||
dataSources: [
|
dataSources: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -6,6 +6,8 @@ window.config = {
|
|||||||
showStudyList: true,
|
showStudyList: true,
|
||||||
maxNumberOfWebWorkers: 3,
|
maxNumberOfWebWorkers: 3,
|
||||||
showLoadingIndicator: true,
|
showLoadingIndicator: true,
|
||||||
|
showWarningMessageForCrossOrigin: true,
|
||||||
|
showCPUFallbackMessage: true,
|
||||||
// filterQueryParam: false,
|
// filterQueryParam: false,
|
||||||
dataSources: [
|
dataSources: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -9,6 +9,8 @@ window.config = {
|
|||||||
maxNumberOfWebWorkers: 4,
|
maxNumberOfWebWorkers: 4,
|
||||||
// below flag is for performance reasons, but it might not work for all servers
|
// below flag is for performance reasons, but it might not work for all servers
|
||||||
omitQuotationForMultipartRequest: true,
|
omitQuotationForMultipartRequest: true,
|
||||||
|
showWarningMessageForCrossOrigin: true,
|
||||||
|
showCPUFallbackMessage: true,
|
||||||
showLoadingIndicator: true,
|
showLoadingIndicator: true,
|
||||||
// filterQueryParam: false,
|
// filterQueryParam: false,
|
||||||
dataSources: [
|
dataSources: [
|
||||||
|
|||||||
@ -14,6 +14,8 @@ window.config = {
|
|||||||
maxNumberOfWebWorkers: 4,
|
maxNumberOfWebWorkers: 4,
|
||||||
// below flag is for performance reasons, but it might not work for all servers
|
// below flag is for performance reasons, but it might not work for all servers
|
||||||
omitQuotationForMultipartRequest: true,
|
omitQuotationForMultipartRequest: true,
|
||||||
|
showWarningMessageForCrossOrigin: true,
|
||||||
|
showCPUFallbackMessage: true,
|
||||||
showLoadingIndicator: true,
|
showLoadingIndicator: true,
|
||||||
// filterQueryParam: false,
|
// filterQueryParam: false,
|
||||||
dataSources: [
|
dataSources: [
|
||||||
|
|||||||
@ -6,6 +6,8 @@ window.config = {
|
|||||||
showStudyList: true,
|
showStudyList: true,
|
||||||
// below flag is for performance reasons, but it might not work for all servers
|
// below flag is for performance reasons, but it might not work for all servers
|
||||||
omitQuotationForMultipartRequest: true,
|
omitQuotationForMultipartRequest: true,
|
||||||
|
showWarningMessageForCrossOrigin: true,
|
||||||
|
showCPUFallbackMessage: true,
|
||||||
showLoadingIndicator: true,
|
showLoadingIndicator: true,
|
||||||
// filterQueryParam: false,
|
// filterQueryParam: false,
|
||||||
dataSources: [
|
dataSources: [
|
||||||
|
|||||||
@ -3,6 +3,8 @@ window.config = {
|
|||||||
showStudyList: true,
|
showStudyList: true,
|
||||||
// below flag is for performance reasons, but it might not work for all servers
|
// below flag is for performance reasons, but it might not work for all servers
|
||||||
omitQuotationForMultipartRequest: true,
|
omitQuotationForMultipartRequest: true,
|
||||||
|
showWarningMessageForCrossOrigin: true,
|
||||||
|
showCPUFallbackMessage: true,
|
||||||
showLoadingIndicator: true,
|
showLoadingIndicator: true,
|
||||||
servers: {
|
servers: {
|
||||||
dicomWeb: [
|
dicomWeb: [
|
||||||
|
|||||||
@ -96,6 +96,11 @@ async function appInit(appConfigOrFunc, defaultExtensions, defaultModes) {
|
|||||||
appConfig.modes.push(mode);
|
appConfig.modes.push(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove modes that are not objects, or have no id
|
||||||
|
appConfig.modes = appConfig.modes.filter(
|
||||||
|
mode => typeof mode === 'object' && mode.id
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
appConfig,
|
appConfig,
|
||||||
commandsManager,
|
commandsManager,
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import useDebounce from './useDebounce.js';
|
import useDebounce from './useDebounce.js';
|
||||||
import useQuery from './useQuery.js';
|
import useQuery from './useQuery.js';
|
||||||
|
import useSearchParams from './useSearchParams.js';
|
||||||
|
|
||||||
export { useDebounce, useQuery };
|
export { useDebounce, useQuery, useSearchParams };
|
||||||
|
|||||||
17
platform/viewer/src/hooks/useSearchParams.js
Normal file
17
platform/viewer/src/hooks/useSearchParams.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import useQuery from './useQuery';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* It returns a Map of the query parameters in the URL, where the keys are
|
||||||
|
* lowercase
|
||||||
|
* @returns A function that returns a Map of the query parameters.
|
||||||
|
*/
|
||||||
|
export default function useSearchParams() {
|
||||||
|
const query = useQuery();
|
||||||
|
// make query params case-insensitive
|
||||||
|
const searchParams = new Map();
|
||||||
|
for (const [key, value] of query) {
|
||||||
|
searchParams.set(key.toLowerCase(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return searchParams;
|
||||||
|
}
|
||||||
@ -5,7 +5,6 @@ import 'regenerator-runtime/runtime';
|
|||||||
import App from './App';
|
import App from './App';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EXTENSIONS AND MODES
|
* EXTENSIONS AND MODES
|
||||||
* =================
|
* =================
|
||||||
@ -15,21 +14,23 @@ import ReactDOM from 'react-dom';
|
|||||||
* pluginImports.js imports all of the modes and extensions and adds them
|
* pluginImports.js imports all of the modes and extensions and adds them
|
||||||
* to the window for processing.
|
* to the window for processing.
|
||||||
*/
|
*/
|
||||||
import loadDynamicImports from './pluginImports.js';
|
import loadDynamicImports, { loadRuntimeImports } from './pluginImports.js';
|
||||||
|
|
||||||
loadDynamicImports().then(() => {
|
loadDynamicImports().then(() => {
|
||||||
/**
|
loadRuntimeImports(window.config).then(() => {
|
||||||
* Combine our appConfiguration with installed extensions and modes.
|
/**
|
||||||
* In the future appConfiguration may contain modes added at runtime.
|
* Combine our appConfiguration with installed extensions and modes.
|
||||||
* */
|
* In the future appConfiguration may contain modes added at runtime.
|
||||||
const appProps = {
|
* */
|
||||||
config: window ? window.config : {},
|
const appProps = {
|
||||||
defaultExtensions: window.extensions,
|
config: window ? window.config : {},
|
||||||
defaultModes: window.modes,
|
defaultExtensions: window.extensions,
|
||||||
};
|
defaultModes: window.modes,
|
||||||
|
};
|
||||||
|
|
||||||
/** Create App */
|
/** Create App */
|
||||||
const app = React.createElement(App, appProps, null);
|
const app = React.createElement(App, appProps, null);
|
||||||
/** Render */
|
/** Render */
|
||||||
ReactDOM.render(app, document.getElementById('root'));
|
ReactDOM.render(app, document.getElementById('root'));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,9 +3,9 @@ import { useParams, useLocation } from 'react-router';
|
|||||||
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
// TODO: DicomMetadataStore should be injected?
|
// TODO: DicomMetadataStore should be injected?
|
||||||
import { DicomMetadataStore } from '@ohif/core';
|
import { DicomMetadataStore, ServicesManager } from '@ohif/core';
|
||||||
import { DragAndDropProvider, ImageViewerProvider } from '@ohif/ui';
|
import { DragAndDropProvider, ImageViewerProvider } from '@ohif/ui';
|
||||||
import { useQuery } from '@hooks';
|
import { useQuery, useSearchParams } from '@hooks';
|
||||||
import ViewportGrid from '@components/ViewportGrid';
|
import ViewportGrid from '@components/ViewportGrid';
|
||||||
import Compose from './Compose';
|
import Compose from './Compose';
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ import Compose from './Compose';
|
|||||||
*/
|
*/
|
||||||
function defaultRouteInit(
|
function defaultRouteInit(
|
||||||
{ servicesManager, studyInstanceUIDs, dataSource, filters },
|
{ servicesManager, studyInstanceUIDs, dataSource, filters },
|
||||||
hangingProtocol
|
hangingProtocolId
|
||||||
) {
|
) {
|
||||||
const {
|
const {
|
||||||
displaySetService,
|
displaySetService,
|
||||||
@ -86,7 +86,7 @@ function defaultRouteInit(
|
|||||||
// hanging protocol in the mode configuration
|
// hanging protocol in the mode configuration
|
||||||
hangingProtocolService.run(
|
hangingProtocolService.run(
|
||||||
{ studies, activeStudy, displaySets },
|
{ studies, activeStudy, displaySets },
|
||||||
hangingProtocol
|
hangingProtocolId
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -105,7 +105,9 @@ export default function ModeRoute({
|
|||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const query = useQuery();
|
const query = useQuery();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
|
const runTimeHangingProtocolId = searchParams.get('hangingprotocolid');
|
||||||
const [studyInstanceUIDs, setStudyInstanceUIDs] = useState();
|
const [studyInstanceUIDs, setStudyInstanceUIDs] = useState();
|
||||||
|
|
||||||
const [refresh, setRefresh] = useState(false);
|
const [refresh, setRefresh] = useState(false);
|
||||||
@ -121,7 +123,7 @@ export default function ModeRoute({
|
|||||||
const {
|
const {
|
||||||
displaySetService,
|
displaySetService,
|
||||||
hangingProtocolService,
|
hangingProtocolService,
|
||||||
} = servicesManager.services;
|
} = (servicesManager as ServicesManager).services;
|
||||||
|
|
||||||
const { extensions, sopClassHandlers, hotkeys, hangingProtocol } = mode;
|
const { extensions, sopClassHandlers, hotkeys, hangingProtocol } = mode;
|
||||||
|
|
||||||
@ -249,22 +251,36 @@ export default function ModeRoute({
|
|||||||
extensionManager,
|
extensionManager,
|
||||||
commandsManager,
|
commandsManager,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// use the URL hangingProtocolId if it exists, otherwise use the one
|
||||||
|
// defined in the mode configuration
|
||||||
|
const hangingProtocolIdToUse = hangingProtocolService.getProtocolById(
|
||||||
|
runTimeHangingProtocolId
|
||||||
|
)
|
||||||
|
? runTimeHangingProtocolId
|
||||||
|
: hangingProtocol;
|
||||||
|
|
||||||
// Sets the active hanging protocols - if hangingProtocol is undefined,
|
// Sets the active hanging protocols - if hangingProtocol is undefined,
|
||||||
// resets to default. Done before the onModeEnter to allow the onModeEnter
|
// resets to default. Done before the onModeEnter to allow the onModeEnter
|
||||||
// to perform custom hanging protocol actions
|
// to perform custom hanging protocol actions
|
||||||
hangingProtocolService.setActiveProtocols(hangingProtocol);
|
hangingProtocolService.setActiveProtocolIds(hangingProtocolIdToUse);
|
||||||
mode?.onModeEnter({ servicesManager, extensionManager, commandsManager });
|
|
||||||
|
mode?.onModeEnter({
|
||||||
|
servicesManager,
|
||||||
|
extensionManager,
|
||||||
|
commandsManager,
|
||||||
|
});
|
||||||
|
|
||||||
const setupRouteInit = async () => {
|
const setupRouteInit = async () => {
|
||||||
/**
|
/**
|
||||||
* The next line should get all the query parameters provided by the URL
|
* The next line should get all the query parameters provided by the URL
|
||||||
* - except the StudyInstaceUIDs - and create an object called filters
|
* - except the StudyInstanceUIDs - and create an object called filters
|
||||||
* used to filtering the study as the user wants otherwise it will return
|
* used to filtering the study as the user wants otherwise it will return
|
||||||
* a empty object.
|
* a empty object.
|
||||||
*
|
*
|
||||||
* Example:
|
* Example:
|
||||||
* const filters = {
|
* const filters = {
|
||||||
* seriesInstaceUID: 1.2.276.0.7230010.3.1.3.1791068887.5412.1620253993.114611
|
* seriesInstanceUID: 1.2.276.0.7230010.3.1.3.1791068887.5412.1620253993.114611
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
const filters =
|
const filters =
|
||||||
@ -272,7 +288,10 @@ export default function ModeRoute({
|
|||||||
(acc: Record<string, string>, val: string) => {
|
(acc: Record<string, string>, val: string) => {
|
||||||
if (val !== 'StudyInstanceUIDs') {
|
if (val !== 'StudyInstanceUIDs') {
|
||||||
if (['seriesInstanceUID', 'SeriesInstanceUID'].includes(val)) {
|
if (['seriesInstanceUID', 'SeriesInstanceUID'].includes(val)) {
|
||||||
return { ...acc, seriesInstanceUID: query.get(val) };
|
return {
|
||||||
|
...acc,
|
||||||
|
seriesInstanceUID: query.get(val),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return { ...acc, [val]: query.get(val) };
|
return { ...acc, [val]: query.get(val) };
|
||||||
@ -291,7 +310,7 @@ export default function ModeRoute({
|
|||||||
dataSource,
|
dataSource,
|
||||||
filters,
|
filters,
|
||||||
},
|
},
|
||||||
hangingProtocol
|
hangingProtocolIdToUse
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,7 +321,7 @@ export default function ModeRoute({
|
|||||||
dataSource,
|
dataSource,
|
||||||
filters,
|
filters,
|
||||||
},
|
},
|
||||||
hangingProtocol
|
hangingProtocolIdToUse
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -316,7 +335,10 @@ export default function ModeRoute({
|
|||||||
// information, and must be in a try/catch to ensure subscriptions
|
// information, and must be in a try/catch to ensure subscriptions
|
||||||
// are unsubscribed.
|
// are unsubscribed.
|
||||||
try {
|
try {
|
||||||
mode?.onModeExit?.({ servicesManager, extensionManager });
|
mode?.onModeExit?.({
|
||||||
|
servicesManager,
|
||||||
|
extensionManager,
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('mode exit failure', e);
|
console.warn('mode exit failure', e);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
//
|
//
|
||||||
import filtersMeta from './filtersMeta.js';
|
import filtersMeta from './filtersMeta.js';
|
||||||
import { useAppConfig } from '@state';
|
import { useAppConfig } from '@state';
|
||||||
import { useDebounce, useQuery } from '@hooks';
|
import { useDebounce, useSearchParams } from '@hooks';
|
||||||
import { utils, hotkeys } from '@ohif/core';
|
import { utils, hotkeys } from '@ohif/core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -54,10 +54,10 @@ function WorkList({
|
|||||||
// ~ Modes
|
// ~ Modes
|
||||||
const [appConfig] = useAppConfig();
|
const [appConfig] = useAppConfig();
|
||||||
// ~ Filters
|
// ~ Filters
|
||||||
const query = useQuery();
|
const searchParams = useSearchParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const STUDIES_LIMIT = 101;
|
const STUDIES_LIMIT = 101;
|
||||||
const queryFilterValues = _getQueryFilterValues(query);
|
const queryFilterValues = _getQueryFilterValues(searchParams);
|
||||||
const [filterValues, _setFilterValues] = useState({
|
const [filterValues, _setFilterValues] = useState({
|
||||||
...defaultFilterValues,
|
...defaultFilterValues,
|
||||||
...queryFilterValues,
|
...queryFilterValues,
|
||||||
@ -503,7 +503,7 @@ const defaultFilterValues = {
|
|||||||
|
|
||||||
function _tryParseInt(str, defaultValue) {
|
function _tryParseInt(str, defaultValue) {
|
||||||
let retValue = defaultValue;
|
let retValue = defaultValue;
|
||||||
if (str !== null) {
|
if (str != null) {
|
||||||
if (str.length > 0) {
|
if (str.length > 0) {
|
||||||
if (!isNaN(str)) {
|
if (!isNaN(str)) {
|
||||||
retValue = parseInt(str);
|
retValue = parseInt(str);
|
||||||
@ -513,24 +513,24 @@ function _tryParseInt(str, defaultValue) {
|
|||||||
return retValue;
|
return retValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _getQueryFilterValues(query) {
|
function _getQueryFilterValues(params) {
|
||||||
const queryFilterValues = {
|
const queryFilterValues = {
|
||||||
patientName: query.get('patientName'),
|
patientName: params.get('patientname'),
|
||||||
mrn: query.get('mrn'),
|
mrn: params.get('mrn'),
|
||||||
studyDate: {
|
studyDate: {
|
||||||
startDate: query.get('startDate'),
|
startDate: params.get('startdate') || null,
|
||||||
endDate: query.get('endDate'),
|
endDate: params.get('enddate') || null,
|
||||||
},
|
},
|
||||||
description: query.get('description'),
|
description: params.get('description'),
|
||||||
modalities: query.get('modalities')
|
modalities: params.get('modalities')
|
||||||
? query.get('modalities').split(',')
|
? params.get('modalities').split(',')
|
||||||
: [],
|
: [],
|
||||||
accession: query.get('accession'),
|
accession: params.get('accession'),
|
||||||
sortBy: query.get('sortBy'),
|
sortBy: params.get('sortby'),
|
||||||
sortDirection: query.get('sortDirection'),
|
sortDirection: params.get('sortdirection'),
|
||||||
pageNumber: _tryParseInt(query.get('pageNumber'), undefined),
|
pageNumber: _tryParseInt(params.get('pagenumber'), undefined),
|
||||||
resultsPerPage: _tryParseInt(query.get('resultsPerPage'), undefined),
|
resultsPerPage: _tryParseInt(params.get('resultsperpage'), undefined),
|
||||||
datasources: query.get('datasources'),
|
datasources: params.get('datasources'),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Delete null/undefined keys
|
// Delete null/undefined keys
|
||||||
|
|||||||
2
testdata
2
testdata
@ -1 +1 @@
|
|||||||
Subproject commit 1bca96aa2aeae7d90b3c8db73845c85dd27b34d5
|
Subproject commit 4d59660c2883ed749a680e5fb6d4624ab54c9422
|
||||||
@ -3,7 +3,8 @@
|
|||||||
"baseUrl": "./",
|
"baseUrl": "./",
|
||||||
"checkJs": true,
|
"checkJs": true,
|
||||||
// Ensure that Babel can safely transpile files in the TypeScript project
|
// Ensure that Babel can safely transpile files in the TypeScript project
|
||||||
"isolatedModules": false,
|
"isolatedModules": true,
|
||||||
|
"emitDeclarationOnly": true,
|
||||||
"jsx": "react",
|
"jsx": "react",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
@ -12,14 +13,16 @@
|
|||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@ohif/core": [
|
"@ohif/core": ["platform/core/src"],
|
||||||
"platform/core/src"
|
|
||||||
],
|
|
||||||
"@ohif/ui": ["platform/ui/src"],
|
"@ohif/ui": ["platform/ui/src"],
|
||||||
"@ohif/i18n": ["platform/i18n/src"],
|
"@ohif/i18n": ["platform/i18n/src"],
|
||||||
"@ohif/viewer": ["platform/viewer/src"]
|
"@ohif/viewer": ["platform/viewer/src"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["platform/**/src/**/*", "extensions/**/src/**/*", "modes/**/src/**/*"],
|
"include": [
|
||||||
|
"platform/**/src/**/*",
|
||||||
|
"extensions/**/src/**/*",
|
||||||
|
"modes/**/src/**/*"
|
||||||
|
],
|
||||||
"exclude": ["node_modules", "dist"]
|
"exclude": ["node_modules", "dist"]
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user