parent
c3ce909434
commit
33307d3cd2
@ -25,11 +25,9 @@ executors:
|
|||||||
docker:
|
docker:
|
||||||
# Primary container image where all steps run.
|
# Primary container image where all steps run.
|
||||||
- image: 'cypress/browsers:node14.15.0-chrome86-ff82'
|
- image: 'cypress/browsers:node14.15.0-chrome86-ff82'
|
||||||
- image: 'ohif/viewer-testdata:0.1-test'
|
|
||||||
|
|
||||||
defaults: &defaults
|
defaults: &defaults
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:14.15.0
|
- image: cimg/node:14.18
|
||||||
environment:
|
environment:
|
||||||
TERM: xterm # Enable colors in term
|
TERM: xterm # Enable colors in term
|
||||||
QUICK_BUILD: true
|
QUICK_BUILD: true
|
||||||
@ -349,7 +347,8 @@ workflows:
|
|||||||
store_artifacts: true
|
store_artifacts: true
|
||||||
working_directory: platform/viewer
|
working_directory: platform/viewer
|
||||||
build:
|
build:
|
||||||
npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js
|
git submodule update --init &&
|
||||||
|
npx cross-env QUICK_BUILD=true APP_CONFIG=config/e2e.js
|
||||||
yarn run build
|
yarn run build
|
||||||
start: yarn run test:e2e:serve
|
start: yarn run test:e2e:serve
|
||||||
spec: 'cypress/integration/common/**/*,cypress/integration/pwa/**/*'
|
spec: 'cypress/integration/common/**/*,cypress/integration/pwa/**/*'
|
||||||
@ -377,7 +376,8 @@ workflows:
|
|||||||
store_artifacts: true
|
store_artifacts: true
|
||||||
working_directory: platform/viewer
|
working_directory: platform/viewer
|
||||||
build:
|
build:
|
||||||
npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js
|
git submodule update --init &&
|
||||||
|
npx cross-env QUICK_BUILD=true APP_CONFIG=config/e2e.js
|
||||||
yarn run build:package
|
yarn run build:package
|
||||||
start: yarn run test:e2e:serve
|
start: yarn run test:e2e:serve
|
||||||
spec: 'cypress/integration/common/**/*,cypress/integration/script-tag/**/*'
|
spec: 'cypress/integration/common/**/*,cypress/integration/script-tag/**/*'
|
||||||
@ -410,7 +410,8 @@ workflows:
|
|||||||
store_artifacts: false
|
store_artifacts: false
|
||||||
working_directory: platform/viewer
|
working_directory: platform/viewer
|
||||||
build:
|
build:
|
||||||
npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js
|
git submodule update --init &&
|
||||||
|
npx cross-env QUICK_BUILD=true APP_CONFIG=config/e2e.js
|
||||||
yarn run build
|
yarn run build
|
||||||
# start server --> verify running --> percy + chrome + cypress
|
# start server --> verify running --> percy + chrome + cypress
|
||||||
command: yarn run test:e2e:dist
|
command: yarn run test:e2e:dist
|
||||||
@ -489,7 +490,8 @@ workflows:
|
|||||||
store_artifacts: false
|
store_artifacts: false
|
||||||
working_directory: platform/viewer
|
working_directory: platform/viewer
|
||||||
build:
|
build:
|
||||||
npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js
|
git submodule update --init &&
|
||||||
|
npx cross-env QUICK_BUILD=true APP_CONFIG=config/e2e.js
|
||||||
yarn run build
|
yarn run build
|
||||||
# start server --> verify running --> percy + chrome + cypress
|
# start server --> verify running --> percy + chrome + cypress
|
||||||
command: yarn run test:e2e:dist
|
command: yarn run test:e2e:dist
|
||||||
|
|||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "testdata"]
|
||||||
|
path = testdata
|
||||||
|
url = https://github.com/OHIF/viewer-testdata-dicomweb.git
|
||||||
1
.node-version
Normal file
1
.node-version
Normal file
@ -0,0 +1 @@
|
|||||||
|
14.18.0
|
||||||
@ -28,7 +28,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' : 'cheap-module-eval-source-map',
|
devtool: isProdBuild ? 'source-map' : 'eval-cheap-module-source-map',
|
||||||
entry: {
|
entry: {
|
||||||
app: `${SRC_DIR}/index.js`,
|
app: `${SRC_DIR}/index.js`,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -33,6 +33,7 @@ class DicomPDFViewport extends Component {
|
|||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
byteArray: TypedArrayProp.uint8,
|
byteArray: TypedArrayProp.uint8,
|
||||||
|
rawPdf: PropTypes.bool,
|
||||||
useNative: PropTypes.bool,
|
useNative: PropTypes.bool,
|
||||||
viewportData: PropTypes.object,
|
viewportData: PropTypes.object,
|
||||||
activeViewportIndex: PropTypes.number,
|
activeViewportIndex: PropTypes.number,
|
||||||
@ -45,7 +46,8 @@ class DicomPDFViewport extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
const dataSet = this.parseByteArray(this.props.byteArray);
|
const { rawPdf } = this.props
|
||||||
|
const dataSet = !rawPdf && this.parseByteArray(this.props.byteArray);
|
||||||
const fileURL = this.getPDFFileUrl(dataSet, this.props.byteArray);
|
const fileURL = this.getPDFFileUrl(dataSet, this.props.byteArray);
|
||||||
|
|
||||||
this.setState(state => ({ ...state, fileURL }));
|
this.setState(state => ({ ...state, fileURL }));
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
|||||||
import OHIF from '@ohif/core';
|
import OHIF from '@ohif/core';
|
||||||
import OHIFComponentPlugin from './OHIFComponentPlugin.js';
|
import OHIFComponentPlugin from './OHIFComponentPlugin.js';
|
||||||
import DicomPDFViewport from './DicomPDFViewport';
|
import DicomPDFViewport from './DicomPDFViewport';
|
||||||
|
import { str2ab } from '@ohif/core';
|
||||||
|
|
||||||
const { DicomLoaderService } = OHIF.utils;
|
const { DicomLoaderService } = OHIF.utils;
|
||||||
|
|
||||||
@ -18,6 +19,7 @@ class OHIFDicomPDFViewport extends Component {
|
|||||||
|
|
||||||
state = {
|
state = {
|
||||||
byteArray: null,
|
byteArray: null,
|
||||||
|
rawPdf: false,
|
||||||
error: null,
|
error: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -34,6 +36,15 @@ class OHIFDicomPDFViewport extends Component {
|
|||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { displaySet, studies } = this.props.viewportData;
|
const { displaySet, studies } = this.props.viewportData;
|
||||||
|
|
||||||
|
if (displaySet.metadata && displaySet.metadata.EncapsulatedDocument) {
|
||||||
|
const { InlineBinary, BulkDataURI } = displaySet.metadata.EncapsulatedDocument;
|
||||||
|
if (InlineBinary) {
|
||||||
|
const inlineBinaryData = atob(InlineBinary);
|
||||||
|
const byteArray = str2ab(inlineBinaryData);
|
||||||
|
this.setState({ byteArray, rawPdf: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
DicomLoaderService.findDicomDataPromise(displaySet, studies).then(
|
DicomLoaderService.findDicomDataPromise(displaySet, studies).then(
|
||||||
data => this.setState({ byteArray: new Uint8Array(data) }),
|
data => this.setState({ byteArray: new Uint8Array(data) }),
|
||||||
error => {
|
error => {
|
||||||
@ -49,15 +60,16 @@ class OHIFDicomPDFViewport extends Component {
|
|||||||
viewportIndex,
|
viewportIndex,
|
||||||
activeViewportIndex,
|
activeViewportIndex,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { byteArray, error } = this.state;
|
const { byteArray, error, rawPdf } = this.state;
|
||||||
const { id, init, destroy } = OHIFDicomPDFViewport;
|
const { id, init, destroy } = OHIFDicomPDFViewport;
|
||||||
const pluginProps = { id, init, destroy };
|
const pluginProps = { id, init, destroy };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<OHIFComponentPlugin {...pluginProps}>
|
<OHIFComponentPlugin {...pluginProps}>
|
||||||
{byteArray && (
|
{(byteArray) && (
|
||||||
<DicomPDFViewport
|
<DicomPDFViewport
|
||||||
byteArray={byteArray}
|
byteArray={byteArray}
|
||||||
|
rawPdf={rawPdf}
|
||||||
setViewportActive={setViewportActive}
|
setViewportActive={setViewportActive}
|
||||||
viewportIndex={viewportIndex}
|
viewportIndex={viewportIndex}
|
||||||
activeViewportIndex={activeViewportIndex}
|
activeViewportIndex={activeViewportIndex}
|
||||||
|
|||||||
@ -56,6 +56,7 @@
|
|||||||
"@babel/preset-env": "^7.5.0",
|
"@babel/preset-env": "^7.5.0",
|
||||||
"@babel/preset-react": "^7.0.0",
|
"@babel/preset-react": "^7.0.0",
|
||||||
"@percy/cypress": "^2.2.0",
|
"@percy/cypress": "^2.2.0",
|
||||||
|
"@webpack-cli/serve": "^1.6.1",
|
||||||
"babel-eslint": "9.x",
|
"babel-eslint": "9.x",
|
||||||
"babel-loader": "^8.0.6",
|
"babel-loader": "^8.0.6",
|
||||||
"babel-plugin-inline-react-svg": "1.1.0",
|
"babel-plugin-inline-react-svg": "1.1.0",
|
||||||
@ -104,8 +105,8 @@
|
|||||||
"stylus-loader": "^3.0.2",
|
"stylus-loader": "^3.0.2",
|
||||||
"terser-webpack-plugin": "^2.1.0",
|
"terser-webpack-plugin": "^2.1.0",
|
||||||
"webpack": "^4.35.2",
|
"webpack": "^4.35.2",
|
||||||
"webpack-cli": "^3.3.5",
|
"webpack-cli": "^4.9.2",
|
||||||
"webpack-dev-server": "^3.7.2",
|
"webpack-dev-server": "^4.7.3",
|
||||||
"webpack-hot-middleware": "^2.25.0",
|
"webpack-hot-middleware": "^2.25.0",
|
||||||
"webpack-merge": "^4.2.1",
|
"webpack-merge": "^4.2.1",
|
||||||
"workbox-webpack-plugin": "^5.0.0-beta.1",
|
"workbox-webpack-plugin": "^5.0.0-beta.1",
|
||||||
@ -123,6 +124,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
|
"**/node-gyp": "8.4.1",
|
||||||
"**/@babel/runtime": "7.5.5"
|
"**/@babel/runtime": "7.5.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import ui from './ui';
|
|||||||
import user from './user.js';
|
import user from './user.js';
|
||||||
import errorHandler from './errorHandler.js';
|
import errorHandler from './errorHandler.js';
|
||||||
import utils, { hotkeys } from './utils/';
|
import utils, { hotkeys } from './utils/';
|
||||||
|
import str2ab from './utils/str2ab';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
UINotificationService,
|
UINotificationService,
|
||||||
@ -96,6 +97,8 @@ export {
|
|||||||
UIDialogService,
|
UIDialogService,
|
||||||
MeasurementService,
|
MeasurementService,
|
||||||
LoggerService,
|
LoggerService,
|
||||||
|
|
||||||
|
str2ab,
|
||||||
};
|
};
|
||||||
|
|
||||||
export { OHIF };
|
export { OHIF };
|
||||||
|
|||||||
@ -26,6 +26,7 @@ describe('Top level exports', () => {
|
|||||||
'cornerstone',
|
'cornerstone',
|
||||||
'default', //
|
'default', //
|
||||||
'errorHandler',
|
'errorHandler',
|
||||||
|
'str2ab',
|
||||||
'string',
|
'string',
|
||||||
'ui',
|
'ui',
|
||||||
'user',
|
'user',
|
||||||
|
|||||||
@ -9,38 +9,27 @@ import { api } from 'dicomweb-client';
|
|||||||
*/
|
*/
|
||||||
export default class StaticWadoClient extends api.DICOMwebClient {
|
export default class StaticWadoClient extends api.DICOMwebClient {
|
||||||
static filterKeys = {
|
static filterKeys = {
|
||||||
StudyInstanceUID: '0020000D',
|
"StudyInstanceUID": "0020000D",
|
||||||
PatientName: '00100010',
|
"PatientName": "00100010",
|
||||||
PatientID: '00100020',
|
"00100020": "mrn",
|
||||||
StudyDescription: '00081030',
|
"PatientID": "00100020",
|
||||||
ModalitiesInStudy: '00080061',
|
"StudyDescription": "00081030",
|
||||||
|
"StudyDate": "00080020",
|
||||||
|
"ModalitiesInStudy": "00080061",
|
||||||
|
AccessionNumber: "00080050",
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(qidoConfig) {
|
constructor(qidoConfig) {
|
||||||
super(qidoConfig);
|
super(qidoConfig);
|
||||||
this.staticWado = qidoConfig.staticWado;
|
this.staticWado = qidoConfig.staticWado;
|
||||||
this.extendMetadataWithInstances = qidoConfig.extendMetadataWithInstances;
|
|
||||||
}
|
|
||||||
|
|
||||||
async retrieveSeriesMetadata(options) {
|
|
||||||
if (!this.extendMetadataWithInstances)
|
|
||||||
return super.retrieveSeriesMetadata(options);
|
|
||||||
const results = await Promise.all([
|
|
||||||
super.retrieveSeriesMetadata(options),
|
|
||||||
this.searchForInstances(options),
|
|
||||||
]);
|
|
||||||
const metadata = results[0];
|
|
||||||
const instances = results[1];
|
|
||||||
return metadata.map(item => {
|
|
||||||
const sopUID = item['00080018'].Value[0];
|
|
||||||
const instance = instances.find(
|
|
||||||
instance => instance['00080018'].Value[0] == sopUID
|
|
||||||
);
|
|
||||||
Object.assign(item, instance);
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace the search for studies remote query with a local version which
|
||||||
|
* retrieves a complete query list and then sub-selects from it locally.
|
||||||
|
* @param {*} options
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
async searchForStudies(options) {
|
async searchForStudies(options) {
|
||||||
if (!this.staticWado) return super.searchForStudies(options);
|
if (!this.staticWado) return super.searchForStudies(options);
|
||||||
|
|
||||||
@ -56,6 +45,62 @@ export default class StaticWadoClient extends api.DICOMwebClient {
|
|||||||
return filtered;
|
return filtered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares values, matching any instance of desired to any instance of
|
||||||
|
* actual by recursively go through the paired set of values. That is,
|
||||||
|
* this is O(m*n) where m is how many items in desired and n is the length of actual
|
||||||
|
* Then, at the individual item node, compares the Alphabetic name if present,
|
||||||
|
* and does a sub-string matching on string values, and otherwise does an
|
||||||
|
* exact match comparison.
|
||||||
|
*
|
||||||
|
* @param {*} desired
|
||||||
|
* @param {*} actual
|
||||||
|
* @returns true if the values match
|
||||||
|
*/
|
||||||
|
compareValues(desired, actual) {
|
||||||
|
if (Array.isArray(desired)) {
|
||||||
|
return desired.find(item => this.compareValues(item, actual));
|
||||||
|
}
|
||||||
|
if (Array.isArray(actual)) {
|
||||||
|
return actual.find(actualItem => this.compareValues(desired, actualItem));
|
||||||
|
}
|
||||||
|
if (actual && actual.Alphabetic) {
|
||||||
|
actual = actual.Alphabetic;
|
||||||
|
}
|
||||||
|
if (typeof (actual) == 'string') {
|
||||||
|
if (actual.length === 0) return true;
|
||||||
|
if (desired.length === 0 || desired === '*') return true;
|
||||||
|
if (desired[0] === '*' && desired[desired.length - 1] === '*') {
|
||||||
|
console.log(`Comparing ${actual} to ${desired.substring(1, desired.length - 1)}`)
|
||||||
|
return actual.indexOf(desired.substring(1, desired.length - 1)) != -1;
|
||||||
|
} else if (desired[desired.length - 1] === '*') {
|
||||||
|
return actual.indexOf(desired.substring(0, desired.length - 1)) != -1;
|
||||||
|
} else if (desired[0] === '*') {
|
||||||
|
return actual.indexOf(desired.substring(1)) === actual.length - desired.length + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return desired === actual;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Compares a pair of dates to see if the value is within the range */
|
||||||
|
compareDateRange(range, value) {
|
||||||
|
if (!value) return true;
|
||||||
|
const dash = range.indexOf('-');
|
||||||
|
if (dash === -1) return this.compareValues(range, value);
|
||||||
|
const start = range.substring(0, dash);
|
||||||
|
const end = range.substring(dash + 1);
|
||||||
|
return (!start || value >= start) &&
|
||||||
|
(!end || value <= end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters the return list by the query parameters.
|
||||||
|
*
|
||||||
|
* @param {*} key
|
||||||
|
* @param {*} queryParams
|
||||||
|
* @param {*} study
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
filterItem(key, queryParams, study) {
|
filterItem(key, queryParams, study) {
|
||||||
const altKey = StaticWadoClient.filterKeys[key] || key;
|
const altKey = StaticWadoClient.filterKeys[key] || key;
|
||||||
if (!queryParams) return true;
|
if (!queryParams) return true;
|
||||||
@ -63,23 +108,8 @@ export default class StaticWadoClient extends api.DICOMwebClient {
|
|||||||
if (!testValue) return true;
|
if (!testValue) return true;
|
||||||
const valueElem = study[key] || study[altKey];
|
const valueElem = study[key] || study[altKey];
|
||||||
if (!valueElem) return false;
|
if (!valueElem) return false;
|
||||||
|
if (valueElem.vr == 'DA') return this.compareDateRange(testValue, valueElem.Value[0]);
|
||||||
const value = valueElem.Value;
|
const value = valueElem.Value;
|
||||||
return this.eqItem(testValue, value);
|
return this.compareValues(testValue, value) && true;
|
||||||
}
|
|
||||||
|
|
||||||
eqItem(testValue, value) {
|
|
||||||
if (testValue.filter) {
|
|
||||||
return (
|
|
||||||
testValue.filter(testItem => this.eqItem(testItem, value)).length > 0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (value === testValue) return true;
|
|
||||||
if (typeof value == 'string') {
|
|
||||||
return value.indexOf(testValue) != -1;
|
|
||||||
}
|
|
||||||
if (value.Alphabetic) return this.eqItem(testValue, value.Alphabetic);
|
|
||||||
if (value.filter)
|
|
||||||
return value.filter(item => this.eqItem(testValue, item)).length > 0;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,6 +26,16 @@ const PROXY_TARGET = process.env.PROXY_TARGET;
|
|||||||
const PROXY_DOMAIN = process.env.PROXY_DOMAIN;
|
const PROXY_DOMAIN = process.env.PROXY_DOMAIN;
|
||||||
const ENTRY_TARGET = process.env.ENTRY_TARGET || `${SRC_DIR}/index.js`;
|
const ENTRY_TARGET = process.env.ENTRY_TARGET || `${SRC_DIR}/index.js`;
|
||||||
|
|
||||||
|
|
||||||
|
const setHeaders = (res, path) => {
|
||||||
|
res.setHeader('Content-Type', 'text/plain')
|
||||||
|
if (path.indexOf('.gz') !== -1) {
|
||||||
|
res.setHeader('Content-Encoding', 'gzip')
|
||||||
|
} else if (path.indexOf('.br') !== -1) {
|
||||||
|
res.setHeader('Content-Encoding', 'br')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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 });
|
||||||
const isProdBuild = process.env.NODE_ENV === 'production';
|
const isProdBuild = process.env.NODE_ENV === 'production';
|
||||||
@ -134,11 +144,40 @@ module.exports = (env, argv) => {
|
|||||||
hot: true,
|
hot: true,
|
||||||
open: true,
|
open: true,
|
||||||
port: 3000,
|
port: 3000,
|
||||||
host: '0.0.0.0',
|
client: {
|
||||||
public: 'http://localhost:' + 3000,
|
overlay: { errors: true, warnings: false },
|
||||||
|
},
|
||||||
|
'static': [
|
||||||
|
{
|
||||||
|
directory: path.join(require('os').homedir(), 'dicomweb'),
|
||||||
|
staticOptions: {
|
||||||
|
extensions: ['gz', 'br'],
|
||||||
|
index: "index.json.gz",
|
||||||
|
redirect: true,
|
||||||
|
setHeaders,
|
||||||
|
},
|
||||||
|
publicPath: '/dicomweb',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
directory: '../../testdata',
|
||||||
|
staticOptions: {
|
||||||
|
extensions: ['gz', 'br'],
|
||||||
|
index: "index.json.gz",
|
||||||
|
redirect: true,
|
||||||
|
setHeaders,
|
||||||
|
},
|
||||||
|
publicPath: '/testdata',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
//public: 'http://localhost:' + 3000,
|
||||||
|
//writeToDisk: true,
|
||||||
historyApiFallback: {
|
historyApiFallback: {
|
||||||
disableDotRule: true,
|
disableDotRule: true,
|
||||||
},
|
},
|
||||||
|
headers: {
|
||||||
|
'Cross-Origin-Embedder-Policy': 'require-corp',
|
||||||
|
'Cross-Origin-Opener-Policy': 'same-origin',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import '@percy/cypress';
|
import '@percy/cypress';
|
||||||
|
import { enable } from 'cornerstone-core';
|
||||||
import 'cypress-file-upload';
|
import 'cypress-file-upload';
|
||||||
import { DragSimulator } from '../helpers/DragSimulator.js';
|
import { DragSimulator } from '../helpers/DragSimulator.js';
|
||||||
import {
|
import {
|
||||||
@ -199,6 +200,11 @@ Cypress.Commands.add('waitDicomImage', (timeout = 50000) => {
|
|||||||
.its('cornerstone')
|
.its('cornerstone')
|
||||||
.then({ timeout }, $cornerstone => {
|
.then({ timeout }, $cornerstone => {
|
||||||
return new Cypress.Promise(resolve => {
|
return new Cypress.Promise(resolve => {
|
||||||
|
const onEnabled = enabledEvt => {
|
||||||
|
const element = enabledEvt.detail.element;
|
||||||
|
|
||||||
|
element.addEventListener('cornerstoneimagerendered', onEvent);
|
||||||
|
};
|
||||||
const onEvent = renderedEvt => {
|
const onEvent = renderedEvt => {
|
||||||
const element = renderedEvt.detail.element;
|
const element = renderedEvt.detail.element;
|
||||||
|
|
||||||
@ -209,15 +215,17 @@ Cypress.Commands.add('waitDicomImage', (timeout = 50000) => {
|
|||||||
);
|
);
|
||||||
resolve();
|
resolve();
|
||||||
};
|
};
|
||||||
const onEnabled = enabledEvt => {
|
const enabledElements = $cornerstone.getEnabledElements();
|
||||||
const element = enabledEvt.detail.element;
|
if (enabledElements && enabledElements.length && !enabledElements[0].invalid) {
|
||||||
|
// Sometimes the page finishes rendering before this gets run,
|
||||||
element.addEventListener('cornerstoneimagerendered', onEvent);
|
// if so, just resolve immediately.
|
||||||
};
|
resolve();
|
||||||
|
} else {
|
||||||
$cornerstone.events.addEventListener(
|
$cornerstone.events.addEventListener(
|
||||||
'cornerstoneelementenabled',
|
'cornerstoneelementenabled',
|
||||||
onEnabled
|
onEnabled
|
||||||
);
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,8 +22,8 @@
|
|||||||
"build:viewer:ci": "cross-env NODE_ENV=production PUBLIC_URL=/pwa/ APP_CONFIG=config/netlify.js QUICK_BUILD=true node --max_old_space_size=4096 ./../../node_modules/webpack/bin/webpack.js --config .webpack/webpack.pwa.js",
|
"build:viewer:ci": "cross-env NODE_ENV=production PUBLIC_URL=/pwa/ APP_CONFIG=config/netlify.js QUICK_BUILD=true node --max_old_space_size=4096 ./../../node_modules/webpack/bin/webpack.js --config .webpack/webpack.pwa.js",
|
||||||
"build:viewer:demo": "cross-env NODE_ENV=production APP_CONFIG=config/demo.js HTML_TEMPLATE=rollbar.html QUICK_BUILD=true node --max_old_space_size=4096 ./../../node_modules/webpack/bin/webpack.js --progress --config .webpack/webpack.pwa.js",
|
"build:viewer:demo": "cross-env NODE_ENV=production APP_CONFIG=config/demo.js HTML_TEMPLATE=rollbar.html QUICK_BUILD=true node --max_old_space_size=4096 ./../../node_modules/webpack/bin/webpack.js --progress --config .webpack/webpack.pwa.js",
|
||||||
"build:viewer:package": "yarn run build:package",
|
"build:viewer:package": "yarn run build:package",
|
||||||
"dev": "cross-env NODE_ENV=development webpack-dev-server --config .webpack/webpack.pwa.js --watch",
|
"dev": "cross-env NODE_ENV=development webpack-dev-server --config .webpack/webpack.pwa.js",
|
||||||
"dev:orthanc": "cross-env NODE_ENV=development PROXY_TARGET=/dicom-web PROXY_DOMAIN=http://localhost:8042 APP_CONFIG=config/docker_nginx-orthanc.js webpack-dev-server --config .webpack/webpack.pwa.js --watch",
|
"dev:orthanc": "cross-env NODE_ENV=development PROXY_TARGET=/dicom-web PROXY_DOMAIN=http://localhost:8042 APP_CONFIG=config/docker_nginx-orthanc.js webpack-dev-server --config .webpack/webpack.pwa.js",
|
||||||
"dev:viewer": "yarn run dev",
|
"dev:viewer": "yarn run dev",
|
||||||
"start": "yarn run dev",
|
"start": "yarn run dev",
|
||||||
"test:e2e": "cypress open",
|
"test:e2e": "cypress open",
|
||||||
@ -31,7 +31,7 @@
|
|||||||
"test:e2e:script-tag": "cypress run --config video=false --browser chrome --spec 'cypress/integration/common/**/*,cypress/integration/script-tag/**/*'",
|
"test:e2e:script-tag": "cypress run --config video=false --browser chrome --spec 'cypress/integration/common/**/*,cypress/integration/script-tag/**/*'",
|
||||||
"test:e2e:local": "cypress run --config video=false --browser chrome --spec 'cypress/integration/common/**/*,cypress/integration/pwa/**/*'",
|
"test:e2e:local": "cypress run --config video=false --browser chrome --spec 'cypress/integration/common/**/*,cypress/integration/pwa/**/*'",
|
||||||
"test:e2e:dist": "start-server-and-test test:e2e:serve http://localhost:3000 test:e2e:ci",
|
"test:e2e:dist": "start-server-and-test test:e2e:serve http://localhost:3000 test:e2e:ci",
|
||||||
"test:e2e:serve": "serve -n -l 3000 -s dist",
|
"test:e2e:serve": "cross-env APP_CONFIG=config/e2e.js yarn start",
|
||||||
"test:unit": "jest --watchAll",
|
"test:unit": "jest --watchAll",
|
||||||
"test:unit:ci": "jest --ci --runInBand --collectCoverage",
|
"test:unit:ci": "jest --ci --runInBand --collectCoverage",
|
||||||
"prepublishOnly": "yarn run build:package",
|
"prepublishOnly": "yarn run build:package",
|
||||||
|
|||||||
116
platform/viewer/public/config/e2e.js
Normal file
116
platform/viewer/public/config/e2e.js
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
window.config = {
|
||||||
|
routerBasename: '/',
|
||||||
|
showStudyList: true,
|
||||||
|
servers: {
|
||||||
|
dicomWeb: [
|
||||||
|
{
|
||||||
|
name: 'LocalStatic',
|
||||||
|
wadoUriRoot: '/testdata',
|
||||||
|
qidoRoot: '/testdata',
|
||||||
|
wadoRoot: '/testdata',
|
||||||
|
qidoSupportsIncludeField: false,
|
||||||
|
imageRendering: 'wadors',
|
||||||
|
enableStudyLazyLoad: true,
|
||||||
|
supportsFuzzyMatching: false,
|
||||||
|
staticWado: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
// Extensions should be able to suggest default values for these?
|
||||||
|
// Or we can require that these be explicitly set
|
||||||
|
hotkeys: [
|
||||||
|
// ~ Global
|
||||||
|
{
|
||||||
|
commandName: 'incrementActiveViewport',
|
||||||
|
label: 'Next Viewport',
|
||||||
|
keys: ['right'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
commandName: 'decrementActiveViewport',
|
||||||
|
label: 'Previous Viewport',
|
||||||
|
keys: ['left'],
|
||||||
|
},
|
||||||
|
// Supported Keys: https://craig.is/killing/mice
|
||||||
|
// ~ Cornerstone Extension
|
||||||
|
{ commandName: 'rotateViewportCW', label: 'Rotate Right', keys: ['r'] },
|
||||||
|
{ commandName: 'rotateViewportCCW', label: 'Rotate Left', keys: ['l'] },
|
||||||
|
{ commandName: 'invertViewport', label: 'Invert', keys: ['i'] },
|
||||||
|
{
|
||||||
|
commandName: 'flipViewportVertical',
|
||||||
|
label: 'Flip Horizontally',
|
||||||
|
keys: ['h'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
commandName: 'flipViewportHorizontal',
|
||||||
|
label: 'Flip Vertically',
|
||||||
|
keys: ['v'],
|
||||||
|
},
|
||||||
|
{ commandName: 'scaleUpViewport', label: 'Zoom In', keys: ['+'] },
|
||||||
|
{ commandName: 'scaleDownViewport', label: 'Zoom Out', keys: ['-'] },
|
||||||
|
{ commandName: 'fitViewportToWindow', label: 'Zoom to Fit', keys: ['='] },
|
||||||
|
{ commandName: 'resetViewport', label: 'Reset', keys: ['space'] },
|
||||||
|
// clearAnnotations
|
||||||
|
{ commandName: 'nextImage', label: 'Next Image', keys: ['down'] },
|
||||||
|
{ commandName: 'previousImage', label: 'Previous Image', keys: ['up'] },
|
||||||
|
// firstImage
|
||||||
|
// lastImage
|
||||||
|
{
|
||||||
|
commandName: 'previousViewportDisplaySet',
|
||||||
|
label: 'Previous Series',
|
||||||
|
keys: ['pagedown'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
commandName: 'nextViewportDisplaySet',
|
||||||
|
label: 'Next Series',
|
||||||
|
keys: ['pageup'],
|
||||||
|
},
|
||||||
|
// ~ Cornerstone Tools
|
||||||
|
{ commandName: 'setZoomTool', label: 'Zoom', keys: ['z'] },
|
||||||
|
// ~ Window level presets
|
||||||
|
{
|
||||||
|
commandName: 'windowLevelPreset1',
|
||||||
|
label: 'W/L Preset 1',
|
||||||
|
keys: ['1'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
commandName: 'windowLevelPreset2',
|
||||||
|
label: 'W/L Preset 2',
|
||||||
|
keys: ['2'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
commandName: 'windowLevelPreset3',
|
||||||
|
label: 'W/L Preset 3',
|
||||||
|
keys: ['3'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
commandName: 'windowLevelPreset4',
|
||||||
|
label: 'W/L Preset 4',
|
||||||
|
keys: ['4'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
commandName: 'windowLevelPreset5',
|
||||||
|
label: 'W/L Preset 5',
|
||||||
|
keys: ['5'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
commandName: 'windowLevelPreset6',
|
||||||
|
label: 'W/L Preset 6',
|
||||||
|
keys: ['6'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
commandName: 'windowLevelPreset7',
|
||||||
|
label: 'W/L Preset 7',
|
||||||
|
keys: ['7'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
commandName: 'windowLevelPreset8',
|
||||||
|
label: 'W/L Preset 8',
|
||||||
|
keys: ['8'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
commandName: 'windowLevelPreset9',
|
||||||
|
label: 'W/L Preset 9',
|
||||||
|
keys: ['9'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
@ -5,9 +5,9 @@ window.config = {
|
|||||||
dicomWeb: [
|
dicomWeb: [
|
||||||
{
|
{
|
||||||
name: 'LocalStatic',
|
name: 'LocalStatic',
|
||||||
wadoUriRoot: 'http://localhost:5000/',
|
wadoUriRoot: '/dicomweb',
|
||||||
qidoRoot: 'http://localhost:5000',
|
qidoRoot: '/dicomweb',
|
||||||
wadoRoot: 'http://localhost:5000/',
|
wadoRoot: '/dicomweb',
|
||||||
qidoSupportsIncludeField: false,
|
qidoSupportsIncludeField: false,
|
||||||
imageRendering: 'wadors',
|
imageRendering: 'wadors',
|
||||||
thumbnailRendering: 'thumbnail',
|
thumbnailRendering: 'thumbnail',
|
||||||
|
|||||||
1
runtime.txt
Normal file
1
runtime.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
3.7
|
||||||
1
testdata
Submodule
1
testdata
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 47d51e15a0d41c02f806570d8cf78ab39e782387
|
||||||
Loading…
Reference in New Issue
Block a user