From e8ed5f8054754b2a08f6bde621dd89141097d185 Mon Sep 17 00:00:00 2001 From: Igor Octaviano Date: Tue, 15 Oct 2024 17:59:21 -0300 Subject: [PATCH 1/7] Address lint issues --- package.json | 2 +- src/BufferStream.js | 2 +- src/DicomMessage.js | 26 +- src/adapters/Cornerstone/Bidirectional.js | 1 - src/adapters/Cornerstone/EllipticalRoi.js | 3 - src/adapters/Cornerstone/Length.js | 3 - src/adapters/Cornerstone/Segmentation_3X.js | 2 - src/adapters/Cornerstone/Segmentation_4X.js | 174 +---- src/adapters/Cornerstone3D/Bidirectional.js | 2 - src/adapters/Cornerstone3D/EllipticalROI.js | 4 +- src/adapters/Cornerstone3D/Length.js | 2 - .../Cornerstone3D/MeasurementReport.js | 2 +- .../Cornerstone3D/PlanarFreehandROI.js | 3 - src/adapters/VTKjs/Segmentation.js | 1 + src/colors.js | 2 +- src/derivations/DerivedPixels.js | 1 - src/derivations/ParametricMap.js | 1 - src/dictionary.js | 2 +- src/sr/coding.js | 5 + src/sr/contentItems.js | 12 +- src/sr/documents.js | 4 +- src/sr/templates.js | 58 +- src/sr/valueTypes.js | 8 +- src/utilities/Message.js | 3 - src/utilities/TID300/Bidirectional.js | 1 - src/utilities/TID300/CobbAngle.js | 1 - src/utilities/TID300/Point.js | 1 - src/utilities/TID300/Polygon.js | 1 - src/utilities/deepEqual.js | 9 +- src/utilities/orientation/rotateMatrix902D.js | 16 - test/adapters.test.js | 30 +- test/data-encoding.test.js | 5 +- test/data-options.test.js | 1 - test/data.test.js | 341 ++++++--- test/lossless-read-write.test.js | 680 +++++++++++------- test/testUtils.js | 3 +- test/utilities/deepEqual.test.js | 49 +- 37 files changed, 775 insertions(+), 686 deletions(-) diff --git a/package.json b/package.json index b1c464a0..7102abac 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "build:examples": "npm run build && npx cpx 'build/**/*.{js,map}' examples/js", "start": "rollup -c -w", "format": "prettier --write 'src/**/*.js' 'test/**/*.js'", - "lint": "eslint --fix ." + "lint": "eslint --fix 'src/**/*.js' 'test/**/*.js'" }, "repository": { "type": "git", diff --git a/src/BufferStream.js b/src/BufferStream.js index 861e5c38..dc7be67f 100644 --- a/src/BufferStream.js +++ b/src/BufferStream.js @@ -370,7 +370,7 @@ class ReadBufferStream extends BufferStream { throw new Error(value, "writeUint8 not implemented"); } - writeUint8Repeat(value, count) { + writeUint8Repeat(value) { throw new Error(value, "writeUint8Repeat not implemented"); } diff --git a/src/DicomMessage.js b/src/DicomMessage.js index 72b89f00..569fdf7a 100644 --- a/src/DicomMessage.js +++ b/src/DicomMessage.js @@ -277,7 +277,9 @@ class DicomMessage { // apply VR specific formatting to the original _rawValue and compare to the Value const vr = ValueRepresentation.createByTypeString(vrType); - const originalValue = tagObject._rawValue.map((val) => vr.applyFormatting(val)) + const originalValue = tagObject._rawValue.map(val => + vr.applyFormatting(val) + ); // if Value has not changed, write _rawValue unformatted back into the file if (deepEqual(tagObject.Value, originalValue)) { @@ -366,18 +368,28 @@ class DicomMessage { var times = length / vr.maxLength, i = 0; while (i++ < times) { - const { rawValue, value } = vr.read(stream, vr.maxLength, syntax, options); + const { rawValue, value } = vr.read( + stream, + vr.maxLength, + syntax, + options + ); rawValues.push(rawValue); values.push(value); } } else { - const { rawValue, value } = vr.read(stream, length, syntax, options); + const { rawValue, value } = vr.read( + stream, + length, + syntax, + options + ); if (!vr.isBinary() && singleVRs.indexOf(vr.type) == -1) { rawValues = rawValue; - values = value + values = value; if (typeof value === "string") { const delimiterChar = String.fromCharCode(VM_DELIMITER); - rawValues = vr.dropPadByte(rawValue.split(delimiterChar)) + rawValues = vr.dropPadByte(rawValue.split(delimiterChar)); values = vr.dropPadByte(value.split(delimiterChar)); } } else if (vr.type == "SQ") { @@ -388,7 +400,9 @@ class DicomMessage { values = value; } else { Array.isArray(value) ? (values = value) : values.push(value); - Array.isArray(rawValue) ? (rawValues = rawValue) : rawValues.push(rawValue); + Array.isArray(rawValue) + ? (rawValues = rawValue) + : rawValues.push(rawValue); } } stream.setEndian(oldEndian); diff --git a/src/adapters/Cornerstone/Bidirectional.js b/src/adapters/Cornerstone/Bidirectional.js index d39ff67b..836d436e 100644 --- a/src/adapters/Cornerstone/Bidirectional.js +++ b/src/adapters/Cornerstone/Bidirectional.js @@ -127,7 +127,6 @@ class Bidirectional { isCreating: false, longestDiameter, shortestDiameter, - toolType: "Bidirectional", toolName: "Bidirectional", visible: true, finding: findingGroup diff --git a/src/adapters/Cornerstone/EllipticalRoi.js b/src/adapters/Cornerstone/EllipticalRoi.js index 9dc9461b..9e2a27dd 100644 --- a/src/adapters/Cornerstone/EllipticalRoi.js +++ b/src/adapters/Cornerstone/EllipticalRoi.js @@ -1,11 +1,8 @@ import MeasurementReport from "./MeasurementReport"; import TID300Ellipse from "../../utilities/TID300/Ellipse"; import CORNERSTONE_4_TAG from "./cornerstone4Tag"; -import { toArray } from "../helpers.js"; const ELLIPTICALROI = "EllipticalRoi"; -const FINDING = "121071"; -const FINDING_SITE = "G-C0E3"; class EllipticalRoi { constructor() {} diff --git a/src/adapters/Cornerstone/Length.js b/src/adapters/Cornerstone/Length.js index 775b9eb5..704b379a 100644 --- a/src/adapters/Cornerstone/Length.js +++ b/src/adapters/Cornerstone/Length.js @@ -1,11 +1,8 @@ import MeasurementReport from "./MeasurementReport.js"; import TID300Length from "../../utilities/TID300/Length.js"; import CORNERSTONE_4_TAG from "./cornerstone4Tag"; -import { toArray } from "../helpers.js"; const LENGTH = "Length"; -const FINDING = "121071"; -const FINDING_SITE = "G-C0E3"; class Length { constructor() {} diff --git a/src/adapters/Cornerstone/Segmentation_3X.js b/src/adapters/Cornerstone/Segmentation_3X.js index 6bc0f45a..2b87d4c8 100644 --- a/src/adapters/Cornerstone/Segmentation_3X.js +++ b/src/adapters/Cornerstone/Segmentation_3X.js @@ -415,8 +415,6 @@ function addImageIdSpecificBrushToolState( const cToolsPixelData = brushDataI.pixelData; - const [rows, cols] = pixelData2D.shape; - for (let p = 0; p < cToolsPixelData.length; p++) { if (pixelData2D.data[p]) { cToolsPixelData[p] = 1; diff --git a/src/adapters/Cornerstone/Segmentation_4X.js b/src/adapters/Cornerstone/Segmentation_4X.js index d95d3a93..2ff8d858 100644 --- a/src/adapters/Cornerstone/Segmentation_4X.js +++ b/src/adapters/Cornerstone/Segmentation_4X.js @@ -6,7 +6,6 @@ import { DicomMessage } from "../../DicomMessage.js"; import { DicomMetaDictionary } from "../../DicomMetaDictionary.js"; import { Normalizer } from "../../normalizers.js"; import { Segmentation as SegmentationDerivation } from "../../derivations/index.js"; -import { mat4 } from "gl-matrix"; import { rotateDirectionCosinesInPlane, flipImageOrientationPatient as flipIOP, @@ -432,177 +431,6 @@ function generateToolState( }; } -function insertPixelDataPerpendicular( - segmentsOnFrame, - labelmapBuffer, - pixelData, - multiframe, - imageIds, - validOrientations, - metadataProvider -) { - const { - SharedFunctionalGroupsSequence, - PerFrameFunctionalGroupsSequence, - Rows, - Columns - } = multiframe; - - const firstImagePlaneModule = metadataProvider.get( - "imagePlaneModule", - imageIds[0] - ); - - const lastImagePlaneModule = metadataProvider.get( - "imagePlaneModule", - imageIds[imageIds.length - 1] - ); - - console.log(firstImagePlaneModule); - console.log(lastImagePlaneModule); - - const corners = [ - ...getCorners(firstImagePlaneModule), - ...getCorners(lastImagePlaneModule) - ]; - - console.log(`corners:`); - console.log(corners); - - const indexToWorld = mat4.create(); - - const ippFirstFrame = firstImagePlaneModule.imagePositionPatient; - const rowCosines = Array.isArray(firstImagePlaneModule.rowCosines) - ? [...firstImagePlaneModule.rowCosines] - : [ - firstImagePlaneModule.rowCosines.x, - firstImagePlaneModule.rowCosines.y, - firstImagePlaneModule.rowCosines.z - ]; - - const columnCosines = Array.isArray(firstImagePlaneModule.columnCosines) - ? [...firstImagePlaneModule.columnCosines] - : [ - firstImagePlaneModule.columnCosines.x, - firstImagePlaneModule.columnCosines.y, - firstImagePlaneModule.columnCosines.z - ]; - - const { pixelSpacing } = firstImagePlaneModule; - - mat4.set( - indexToWorld, - // Column 1 - 0, - 0, - 0, - ippFirstFrame[0], - // Column 2 - 0, - 0, - 0, - ippFirstFrame[1], - // Column 3 - 0, - 0, - 0, - ippFirstFrame[2], - // Column 4 - 0, - 0, - 0, - 1 - ); - - // TODO -> Get origin and (x,y,z) increments to build a translation matrix: - // TODO -> Equation C.7.6.2.1-1 - - // | cx*di rx* Xx 0 | |x| - // | cy*di ry Xy 0 | |y| - // | cz*di rz Xz 0 | |z| - // | tx ty tz 1 | |1| - - // const [ - // 0, 0 , 0 , 0, - // 0, 0 , 0 , 0, - // 0, 0 , 0 , 0, - // ipp[0], ipp[1] , ipp[2] , 1, - // ] - - // Each frame: - - // Find which corner the first voxel lines up with (one of 8 corners.) - - // Find how i,j,k orient with respect to source volume. - // Go through each frame, find location in source to start, and whether to increment +/ix,+/-y,+/-z - // through each voxel. - - // [1,0,0,0,1,0] - - // const [ - - // ] - - // Invert transformation matrix to get worldToIndex - - // Apply world to index on each point to fill up the matrix. - - // const sharedImageOrientationPatient = SharedFunctionalGroupsSequence.PlaneOrientationSequence - // ? SharedFunctionalGroupsSequence.PlaneOrientationSequence - // .ImageOrientationPatient - // : undefined; - // const sliceLength = Columns * Rows; -} - -function getCorners(imagePlaneModule) { - // console.log(imagePlaneModule); - - const { - rows, - columns, - rowCosines, - columnCosines, - imagePositionPatient: ipp, - rowPixelSpacing, - columnPixelSpacing - } = imagePlaneModule; - - const rowLength = columns * columnPixelSpacing; - const columnLength = rows * rowPixelSpacing; - - const entireRowVector = [ - rowLength * columnCosines[0], - rowLength * columnCosines[1], - rowLength * columnCosines[2] - ]; - - const entireColumnVector = [ - columnLength * rowCosines[0], - columnLength * rowCosines[1], - columnLength * rowCosines[2] - ]; - - const topLeft = [ipp[0], ipp[1], ipp[2]]; - const topRight = [ - topLeft[0] + entireRowVector[0], - topLeft[1] + entireRowVector[1], - topLeft[2] + entireRowVector[2] - ]; - const bottomLeft = [ - topLeft[0] + entireColumnVector[0], - topLeft[1] + entireColumnVector[1], - topLeft[2] + entireColumnVector[2] - ]; - - const bottomRight = [ - bottomLeft[0] + entireRowVector[0], - bottomLeft[1] + entireRowVector[1], - bottomLeft[2] + entireRowVector[2] - ]; - - return [topLeft, topRight, bottomLeft, bottomRight]; -} - /** * Find the reference frame of the segmentation frame in the source data. * @@ -786,7 +614,7 @@ function checkSEGsOverlapping( } } - for (let [user, role] of frameSegmentsMapping.entries()) { + for (let [, role] of frameSegmentsMapping.entries()) { let temp2DArray = new Uint16Array(sliceLength).fill(0); for (let i = 0; i < role.length; ++i) { diff --git a/src/adapters/Cornerstone3D/Bidirectional.js b/src/adapters/Cornerstone3D/Bidirectional.js index 50f90b21..b250842f 100644 --- a/src/adapters/Cornerstone3D/Bidirectional.js +++ b/src/adapters/Cornerstone3D/Bidirectional.js @@ -7,8 +7,6 @@ import { toArray } from "../helpers.js"; const BIDIRECTIONAL = "Bidirectional"; const LONG_AXIS = "Long Axis"; const SHORT_AXIS = "Short Axis"; -const FINDING = "121071"; -const FINDING_SITE = "G-C0E3"; const trackingIdentifierTextValue = `${CORNERSTONE_3D_TAG}:${BIDIRECTIONAL}`; class Bidirectional { diff --git a/src/adapters/Cornerstone3D/EllipticalROI.js b/src/adapters/Cornerstone3D/EllipticalROI.js index 3a2b1536..f68a1afe 100644 --- a/src/adapters/Cornerstone3D/EllipticalROI.js +++ b/src/adapters/Cornerstone3D/EllipticalROI.js @@ -1,11 +1,9 @@ -import { vec2, vec3 } from "gl-matrix"; +import { vec3 } from "gl-matrix"; import MeasurementReport from "./MeasurementReport"; import TID300Ellipse from "../../utilities/TID300/Ellipse"; import CORNERSTONE_3D_TAG from "./cornerstone3DTag"; const ELLIPTICALROI = "EllipticalROI"; -const FINDING = "121071"; -const FINDING_SITE = "G-C0E3"; const EPSILON = 1e-4; const trackingIdentifierTextValue = `${CORNERSTONE_3D_TAG}:${ELLIPTICALROI}`; diff --git a/src/adapters/Cornerstone3D/Length.js b/src/adapters/Cornerstone3D/Length.js index 823f14ba..cea0e67c 100644 --- a/src/adapters/Cornerstone3D/Length.js +++ b/src/adapters/Cornerstone3D/Length.js @@ -3,8 +3,6 @@ import TID300Length from "../../utilities/TID300/Length.js"; import CORNERSTONE_3D_TAG from "./cornerstone3DTag"; const LENGTH = "Length"; -const FINDING = "121071"; -const FINDING_SITE = "G-C0E3"; const trackingIdentifierTextValue = `${CORNERSTONE_3D_TAG}:${LENGTH}`; class Length { diff --git a/src/adapters/Cornerstone3D/MeasurementReport.js b/src/adapters/Cornerstone3D/MeasurementReport.js index 62bf68f4..1989b73e 100644 --- a/src/adapters/Cornerstone3D/MeasurementReport.js +++ b/src/adapters/Cornerstone3D/MeasurementReport.js @@ -379,7 +379,7 @@ export default class MeasurementReport { measurementData[key] = []; }); - measurementGroups.forEach((measurementGroup, index) => { + measurementGroups.forEach(measurementGroup => { const measurementGroupContentSequence = toArray( measurementGroup.ContentSequence ); diff --git a/src/adapters/Cornerstone3D/PlanarFreehandROI.js b/src/adapters/Cornerstone3D/PlanarFreehandROI.js index 2d0c4cd3..7f445f62 100644 --- a/src/adapters/Cornerstone3D/PlanarFreehandROI.js +++ b/src/adapters/Cornerstone3D/PlanarFreehandROI.js @@ -4,9 +4,6 @@ import CORNERSTONE_3D_TAG from "./cornerstone3DTag"; import { vec3 } from "gl-matrix"; const PLANARFREEHANDROI = "PlanarFreehandROI"; -const perimeterCodeValue = "131191004"; -const sctCodingSchemeDesignator = "SCT"; -const polylineGraphicType = "POLYLINE"; const trackingIdentifierTextValue = `${CORNERSTONE_3D_TAG}:${PLANARFREEHANDROI}`; const closedContourThreshold = 1e-5; diff --git a/src/adapters/VTKjs/Segmentation.js b/src/adapters/VTKjs/Segmentation.js index 7d4278de..82ac80d3 100644 --- a/src/adapters/VTKjs/Segmentation.js +++ b/src/adapters/VTKjs/Segmentation.js @@ -161,6 +161,7 @@ export default class Segmentation { dataset.SegmentSequence = [dataset.SegmentSequence]; } + const segments = {}; dataset.SegmentSequence.forEach(segment => { // TODO: other interesting fields could be extracted from the segment // TODO: Read SegmentsOverlay field diff --git a/src/colors.js b/src/colors.js index 4f6a8a24..5199ff07 100644 --- a/src/colors.js +++ b/src/colors.js @@ -50,7 +50,7 @@ class Colors { if (n <= 0.0031306684425005883) { return 12.92 * n; } else { - return 1.055 * Math.pow(n, 0.416666666666666667) - 0.055; + return 1.055 * Math.pow(n, 1 / 2.4) - 0.055; } } diff --git a/src/derivations/DerivedPixels.js b/src/derivations/DerivedPixels.js index 5b1fb360..4c310257 100644 --- a/src/derivations/DerivedPixels.js +++ b/src/derivations/DerivedPixels.js @@ -1,4 +1,3 @@ -import { DicomMetaDictionary } from "../DicomMetaDictionary.js"; import DerivedDataset from "./DerivedDataset"; export default class DerivedPixels extends DerivedDataset { diff --git a/src/derivations/ParametricMap.js b/src/derivations/ParametricMap.js index 31de461b..89624276 100644 --- a/src/derivations/ParametricMap.js +++ b/src/derivations/ParametricMap.js @@ -1,4 +1,3 @@ -import { DicomMetaDictionary } from "../DicomMetaDictionary.js"; import DerivedDataset from "./DerivedDataset.js"; export default class ParametricMap extends DerivedDataset { diff --git a/src/dictionary.js b/src/dictionary.js index ce6a0388..47f5df31 100644 --- a/src/dictionary.js +++ b/src/dictionary.js @@ -56022,7 +56022,7 @@ const dictionary = { name: "Unknown", vm: "1-n", version: "PrivateTag" - }, + } }; export default dictionary; diff --git a/src/sr/coding.js b/src/sr/coding.js index 3da10b03..bb34c48c 100644 --- a/src/sr/coding.js +++ b/src/sr/coding.js @@ -1,3 +1,8 @@ +const _value = Symbol("value"); +const _meaning = Symbol("meaning"); +const _schemeDesignator = Symbol("schemeDesignator"); +const _schemeVersion = Symbol("schemeVersion"); + class Code { constructor(options) { this[_value] = options.value; diff --git a/src/sr/contentItems.js b/src/sr/contentItems.js index ffd2004e..f55af2d7 100644 --- a/src/sr/contentItems.js +++ b/src/sr/contentItems.js @@ -7,7 +7,6 @@ import { GraphicTypes3D, ImageContentItem, NumContentItem, - PixelOriginInterpretations, RelationshipTypes, ScoordContentItem, Scoord3DContentItem, @@ -181,8 +180,9 @@ class VolumeSurface extends Scoord3DContentItem { } else if (options.sourceSeries) { if ( !( - options.sourceSeries || - options.sourceSeries.constructor === SourceSeriesForRegion + options.sourceSeries + // TODO: Missing SourceSeriesForRegion + // || options.sourceSeries.constructor === SourceSeriesForRegion ) ) { throw new Error( @@ -208,7 +208,7 @@ class ReferencedRealWorldValueMap extends CompositeContentItem { meaning: "Real World Value Map used for measurement", schemeDesignator: "DCM" }), - referencedSOPClassUID: option.referencedSOPClassUID, + referencedSOPClassUID: options.referencedSOPClassUID, referencedSOPInstanceUID: options.referencedSOPInstanceUID, relationshipType: RelationshipTypes.CONTAINS }); @@ -299,7 +299,7 @@ class ReferencedSegmentationFrame extends ContentSequence { "Option 'sourceImage' must have type SourceImageForSegmentation." ); } - this.push(sourceImage); + this.push(options.sourceImage); } } @@ -359,7 +359,7 @@ class ReferencedSegmentation extends ContentSequence { "Option 'sourceSeries' must have type SourceSeriesForSegmentation." ); } - this.push(sourceSeries); + this.push(options.sourceSeries); } else { throw new Error( "One of the following two options must be provided: " + diff --git a/src/sr/documents.js b/src/sr/documents.js index ebed948e..220e273d 100644 --- a/src/sr/documents.js +++ b/src/sr/documents.js @@ -1,5 +1,5 @@ import { DicomMetaDictionary } from "../DicomMetaDictionary.js"; - +import { ContentSequence } from "./valueTypes.js"; const _attributesToInclude = [ // Patient "00080054", @@ -297,7 +297,7 @@ class Comprehensive3DSR { ovserver_item.VerifyingObserverName = options.verifyingObserverName; ovserver_item.VerifyingOrganization = options.verifyingOrganization; ovserver_item.VerificationDateTime = DicomMetaDictionary.dateTime(); - this.VerifyingObserverSequence = [observer_item]; + this.VerifyingObserverSequence = [ovserver_item]; } else { this.VerificationFlag = "UNVERIFIED"; } diff --git a/src/sr/templates.js b/src/sr/templates.js index 0945a47c..6c246f9f 100644 --- a/src/sr/templates.js +++ b/src/sr/templates.js @@ -1,32 +1,22 @@ import { Code, CodedConcept } from "./coding.js"; import { CodeContentItem, - CompositeContentItem, ContainerContentItem, ContentSequence, - GraphicTypes, - GraphicTypes3D, - ImageContentItem, NumContentItem, - PixelOriginInterpretations, PNameContentItem, RelationshipTypes, - ScoordContentItem, - Scoord3DContentItem, TextContentItem, UIDRefContentItem } from "./valueTypes.js"; import { FindingSite, - LongitudinalTemporalOffsetFromEvent, ImageRegion, ImageRegion3D, ReferencedSegmentation, ReferencedSegmentationFrame, VolumeSurface, - ReferencedRealWorldValueMap, - SourceImageForSegmentation, - SourceSeriesForSegmentation + ReferencedRealWorldValueMap } from "./contentItems.js"; class Template extends ContentSequence { @@ -201,7 +191,7 @@ class MeasurementProperties extends Template { "MeasurementStatisticalProperties." ); } - this.push(...measurementStatisticalProperties); + this.push(...options.measurementStatisticalProperties); } if (options.normalRangeProperties !== undefined) { if ( @@ -212,7 +202,7 @@ class MeasurementProperties extends Template { "Option 'normalRangeProperties' must have type NormalRangeProperties." ); } - this.push(...normalRangeProperties); + this.push(...options.normalRangeProperties); } if (options.levelOfSignificance !== undefined) { const levelOfSignificanceItem = new CodeContentItem({ @@ -302,7 +292,7 @@ class MeasurementStatisticalProperties extends Template { value: options.authority, relationshipType: RelationshipTypes.HAS_PROPERTIES }); - this.push(authorityItem); + this.push(descriptionItem); } if (options.authority !== undefined) { const authorityItem = new TextContentItem({ @@ -321,7 +311,7 @@ class MeasurementStatisticalProperties extends Template { class NormalRangeProperties extends Template { constructor(options) { - super(); + super(options); if (options.values === undefined) { throw new Error( "Option 'values' is required for NormalRangeProperties." @@ -356,7 +346,7 @@ class NormalRangeProperties extends Template { value: options.authority, relationshipType: RelationshipTypes.HAS_PROPERTIES }); - this.push(authorityItem); + this.push(descriptionItem); } if (options.authority !== undefined) { const authorityItem = new TextContentItem({ @@ -786,6 +776,7 @@ class SubjectContextSpecimen extends Template { class SubjectContextDevice extends Template { constructor(options) { + super(options); if (options.name === undefined) { throw new Error( "Option 'name' is required for SubjectContextDevice." @@ -947,7 +938,7 @@ class _MeasurementsAndQualitatitiveEvaluations extends Template { "Option 'timePointContext' must have type TimePointContext." ); } - groupItem.ContentSequence.push(...timePointContext); + groupItem.ContentSequence.push(...options.timePointContext); } if (options.referencedRealWorldValueMap !== undefined) { if ( @@ -1077,7 +1068,7 @@ class _ROIMeasurementsAndQualitativeEvaluations extends _MeasurementsAndQualitat "Items of option 'referencedVolume' must have type VolumeSurface." ); } - groupItem.ContentSequence.push(referencedVolume); + groupItem.ContentSequence.push(options.referencedVolume); } else if (options.referencedSegmentation !== undefined) { if ( options.referencedSegmentation.constructor !== @@ -1090,7 +1081,7 @@ class _ROIMeasurementsAndQualitativeEvaluations extends _MeasurementsAndQualitat "ReferencedSegmentation or ReferencedSegmentationFrame." ); } - groupItem.ContentSequence.push(referencedSegmentation); + groupItem.ContentSequence.push(options.referencedSegmentation); } this[0] = groupItem; } @@ -1146,6 +1137,7 @@ class VolumetricROIMeasurementsAndQualitativeEvaluations extends _ROIMeasurement class MeasurementsDerivedFromMultipleROIMeasurements extends Template { constructor(options) { + super(options); if (options.derivation === undefined) { throw new Error( "Option 'derivation' is required for " + @@ -1406,6 +1398,7 @@ class MeasurementReport extends Template { class TimePointContext extends Template { constructor(options) { + super(options); if (options.timePoint === undefined) { throw new Error( "Option 'timePoint' is required for TimePointContext." @@ -1470,22 +1463,23 @@ class TimePointContext extends Template { this.push(protocolTimePointIdentifierItem); } if (options.temporalOffsetFromEvent !== undefined) { - if ( - options.temporalOffsetFromEvent.constructor !== - LongitudinalTemporalOffsetFromEventContentItem - ) { - throw new Error( - "Option 'temporalOffsetFromEvent' must have type " + - "LongitudinalTemporalOffsetFromEventContentItem." - ); - } - this.push(temporalOffsetFromEvent); + // TODO: Missing LongitudinalTemporalOffsetFromEventContentItem + // if ( + // options.temporalOffsetFromEvent.constructor !== + // LongitudinalTemporalOffsetFromEventContentItem + // ) { + // throw new Error( + // "Option 'temporalOffsetFromEvent' must have type " + + // "LongitudinalTemporalOffsetFromEventContentItem." + // ); + // } + this.push(options.temporalOffsetFromEvent); } } } class ImageLibrary extends Template { - constructor(options) { + constructor() { super(); const libraryItem = new ContainerContentItem({ name: new CodedConcept({ @@ -1548,7 +1542,7 @@ class AlgorithmIdentification extends Template { meaning: "Algorithm Parameter", schemeDesignator: "DCM" }), - value: param, + value: parameter, relationshipType: RelationshipTypes.HAS_CONCEPT_MOD }); this.push(parameterItem); @@ -1559,7 +1553,7 @@ class AlgorithmIdentification extends Template { class TrackingIdentifier extends Template { constructor(options) { - super(); + super(options); if (options.uid === undefined) { throw new Error("Option 'uid' is required for TrackingIdentifier."); } diff --git a/src/sr/valueTypes.js b/src/sr/valueTypes.js index 4a5a2de6..ca9989b7 100644 --- a/src/sr/valueTypes.js +++ b/src/sr/valueTypes.js @@ -70,10 +70,6 @@ function isFloat(n) { return n === +n && n !== (n | 0); } -function isInteger(n) { - return n === +n && n === (n | 0); -} - function zeroPad(value) { return (value > 9 ? "" : "0") + value; } @@ -271,7 +267,7 @@ class DateTimeContentItem extends ContentItem { ) { throw new Error("Option 'value' must have type Date."); } - this.DateTime = DT(otions.value); + this.DateTime = DT(options.value); } } @@ -641,7 +637,7 @@ class Scoord3DContentItem extends ContentItem { ) { throw new Error("Option 'fiducialUID' must have type String."); } - this.FiducialUID = fiducialUID; + this.FiducialUID = options.fiducialUID; } } } diff --git a/src/utilities/Message.js b/src/utilities/Message.js index 6d3aaf1e..1aec51cf 100644 --- a/src/utilities/Message.js +++ b/src/utilities/Message.js @@ -139,8 +139,6 @@ function multipartEncode( // Write each dataset into the multipart array let position = 0; contentArrays.forEach(contentArray => { - const contentLength = contentArray.length; - multipartArray.set(headerArray, position); multipartArray.set(contentArray, position + headerLength); @@ -183,7 +181,6 @@ function multipartDecode(response) { } const boundary = stringToUint8Array(boundaryString); - const boundaryLength = boundary.length; const components = []; let offset = headerIndex + separator.length; diff --git a/src/utilities/TID300/Bidirectional.js b/src/utilities/TID300/Bidirectional.js index fba5548c..76480b84 100644 --- a/src/utilities/TID300/Bidirectional.js +++ b/src/utilities/TID300/Bidirectional.js @@ -1,4 +1,3 @@ -import { DicomMetaDictionary } from "../../DicomMetaDictionary.js"; import TID300Measurement from "./TID300Measurement.js"; import unit2CodingValue from "./unit2CodingValue.js"; diff --git a/src/utilities/TID300/CobbAngle.js b/src/utilities/TID300/CobbAngle.js index 2b8afbf3..f0ad57b9 100644 --- a/src/utilities/TID300/CobbAngle.js +++ b/src/utilities/TID300/CobbAngle.js @@ -1,4 +1,3 @@ -import { DicomMetaDictionary } from "../../DicomMetaDictionary.js"; import TID300Measurement from "./TID300Measurement.js"; export default class CobbAngle extends TID300Measurement { diff --git a/src/utilities/TID300/Point.js b/src/utilities/TID300/Point.js index 8e70e047..f19aeed4 100644 --- a/src/utilities/TID300/Point.js +++ b/src/utilities/TID300/Point.js @@ -1,4 +1,3 @@ -import { DicomMetaDictionary } from "../../DicomMetaDictionary.js"; import TID300Measurement from "./TID300Measurement.js"; export default class Point extends TID300Measurement { diff --git a/src/utilities/TID300/Polygon.js b/src/utilities/TID300/Polygon.js index 0719a927..1a81f5e7 100644 --- a/src/utilities/TID300/Polygon.js +++ b/src/utilities/TID300/Polygon.js @@ -1,4 +1,3 @@ -import { DicomMetaDictionary } from "../../DicomMetaDictionary.js"; import TID300Measurement from "./TID300Measurement.js"; import unit2CodingValue from "./unit2CodingValue.js"; diff --git a/src/utilities/deepEqual.js b/src/utilities/deepEqual.js index 3537f70b..bb3996ee 100644 --- a/src/utilities/deepEqual.js +++ b/src/utilities/deepEqual.js @@ -13,7 +13,12 @@ export function deepEqual(obj1, obj2) { } // expect objects or a null instance if initial check failed - if (typeof obj1 !== 'object' || typeof obj2 !== 'object' || obj1 === null || obj2 === null) { + if ( + typeof obj1 !== "object" || + typeof obj2 !== "object" || + obj1 === null || + obj2 === null + ) { return false; } @@ -32,4 +37,4 @@ export function deepEqual(obj1, obj2) { } return true; -} \ No newline at end of file +} diff --git a/src/utilities/orientation/rotateMatrix902D.js b/src/utilities/orientation/rotateMatrix902D.js index 3c39346c..3600988d 100644 --- a/src/utilities/orientation/rotateMatrix902D.js +++ b/src/utilities/orientation/rotateMatrix902D.js @@ -9,8 +9,6 @@ import ndarray from "ndarray"; export default function (matrix) { const [rows, cols] = matrix.shape; - //debugPrintMatrix(matrix); - let result = ndarray(new Uint8Array(rows * cols), [cols, rows]); let resultColsMinus1 = result.shape[1] - 1; @@ -21,19 +19,5 @@ export default function (matrix) { } } - //debugPrintMatrix(result); - return result; } - -function debugPrintMatrix(m) { - console.log(`shape: (${m.shape[0]}, ${m.shape[1]})`); - - for (let i = 0; i < m.shape[0]; i++) { - let row = ""; - for (let j = 0; j < m.shape[1]; j++) { - row += `${m.get(i, j)} `; - } - console.log(row); - } -} diff --git a/test/adapters.test.js b/test/adapters.test.js index 21583091..d9472ee6 100644 --- a/test/adapters.test.js +++ b/test/adapters.test.js @@ -1,7 +1,7 @@ import "regenerator-runtime/runtime.js"; import fs from "fs"; import "../src/index.js"; -import Segmentation_4X from "../src/adapters/Cornerstone/Segmentation_4X" +import Segmentation_4X from "../src/adapters/Cornerstone/Segmentation_4X"; import { getTestDataset } from "./testUtils.js"; const mockMetadataProvider = { @@ -9,7 +9,8 @@ const mockMetadataProvider = { // Unlike CT, is missing coordinate system such as frameOfReferenceUID or rowCosines attributes if (imageId === "mg://1") { return { - seriesInstanceUID: "1.2.840.113681.167838594.1562401072.4432.2070.71100000", + seriesInstanceUID: + "1.2.840.113681.167838594.1562401072.4432.2070.71100000", rows: 3328, columns: 2560, pixelSpacing: [0.065238, 0.065238], @@ -25,22 +26,19 @@ const mockMetadataProvider = { }; } } -} +}; it("Can generate tool state (4X) with SEG sourcing MG images without throwing any errors", async () => { const url = "https://github.com/dcmjs-org/data/releases/download/mg-seg/seg-test-SEG.dcm"; - const dcmPath = await getTestDataset(url, "seg-test-SEG.dcm") - const arrayBuffer = fs.readFileSync(dcmPath).buffer + const dcmPath = await getTestDataset(url, "seg-test-SEG.dcm"); + const arrayBuffer = fs.readFileSync(dcmPath).buffer; - expect( - () => { - Segmentation_4X.generateToolState( - ["mg://1"], - arrayBuffer, - mockMetadataProvider - ) - }, - ).not.toThrowError(); - -}); \ No newline at end of file + expect(() => { + Segmentation_4X.generateToolState( + ["mg://1"], + arrayBuffer, + mockMetadataProvider + ); + }).not.toThrowError(); +}); diff --git a/test/data-encoding.test.js b/test/data-encoding.test.js index 78c048ec..d001a22b 100644 --- a/test/data-encoding.test.js +++ b/test/data-encoding.test.js @@ -1,15 +1,12 @@ import "regenerator-runtime/runtime.js"; -import { jest } from "@jest/globals"; import { getZippedTestDataset } from "./testUtils.js"; import dcmjs from "../src/index.js"; import fs from "fs"; import fsPromises from "fs/promises"; -import os from "os"; import path from "path"; -const { DicomMetaDictionary, DicomDict, DicomMessage, ReadBufferStream } = - dcmjs.data; +const { DicomMetaDictionary, DicomMessage } = dcmjs.data; const expectedPatientNames = { SCSARAB: "قباني^لنزار", diff --git a/test/data-options.test.js b/test/data-options.test.js index 6b22eec8..e1f1e99d 100644 --- a/test/data-options.test.js +++ b/test/data-options.test.js @@ -139,7 +139,6 @@ it("noCopy test_multiframe_1", async () => { const mrHeadPath = path.join(unzipPath, "MRHead"); const fileNames = await fsPromises.readdir(mrHeadPath); - const datasets = []; fileNames.forEach(fileName => { const arrayBuffer = fs.readFileSync( path.join(mrHeadPath, fileName) diff --git a/test/data.test.js b/test/data.test.js index 10c78ae6..fd488685 100644 --- a/test/data.test.js +++ b/test/data.test.js @@ -357,9 +357,13 @@ it("test_exponential_notation", () => { dataset.ImagePositionPatient[2] = 7.1945578383e-5; const buffer = data.write(); const copy = dcmjs.data.DicomMessage.readFile(buffer); - const datasetCopy = dcmjs.data.DicomMetaDictionary.naturalizeDataset(copy.dict); + const datasetCopy = dcmjs.data.DicomMetaDictionary.naturalizeDataset( + copy.dict + ); - expect(dataset.ImagePositionPatient).toEqual(datasetCopy.ImagePositionPatient); + expect(dataset.ImagePositionPatient).toEqual( + datasetCopy.ImagePositionPatient + ); }); it("test_output_equality", () => { @@ -1113,15 +1117,19 @@ describe("test_un_vr", () => { const expectedExposureIndex = 662; const expectedDeviationIndex = -1.835; - const url = "https://github.com/dcmjs-org/data/releases/download/unknown-VR/sample-dicom-with-un-vr.dcm"; - const dcmPath = await getTestDataset(url, "sample-dicom-with-un-vr.dcm"); + const url = + "https://github.com/dcmjs-org/data/releases/download/unknown-VR/sample-dicom-with-un-vr.dcm"; + const dcmPath = await getTestDataset( + url, + "sample-dicom-with-un-vr.dcm" + ); const file = await promisify(fs.readFile)(dcmPath); const dicomData = dcmjs.data.DicomMessage.readFile(file.buffer, { ignoreErrors: false, untilTag: null, includeUntilTagValue: false, - noCopy: false, + noCopy: false }); const dataset = dcmjs.data.DicomMetaDictionary.naturalizeDataset( dicomData.dict @@ -1134,201 +1142,294 @@ describe("test_un_vr", () => { describe("Test other VRs encoded as UN", () => { test.each([ [ - '00000600', - 'AE', - new Uint8Array([0x20, 0x20, 0x54, 0x45, 0x53, 0x54, 0x5F, 0x41, 0x45, 0x20]).buffer, + "00000600", + "AE", + new Uint8Array([ + 0x20, 0x20, 0x54, 0x45, 0x53, 0x54, 0x5f, 0x41, 0x45, 0x20 + ]).buffer, [" TEST_AE "], ["TEST_AE"] ], [ - '00101010', - 'AS', + "00101010", + "AS", new Uint8Array([0x30, 0x34, 0x35, 0x59]).buffer, ["045Y"], ["045Y"] ], [ - '00280009', - 'AT', + "00280009", + "AT", new Uint8Array([0x63, 0x10, 0x18, 0x00]).buffer, [0x10630018], [0x10630018] ], [ - '00041130', - 'CS', - new Uint8Array([0x4F, 0x52, 0x49, 0x47, 0x49, 0x4E, 0x41, 0x4C, 0x20, 0x20, 0x5C, 0x20, 0x50, 0x52, 0x49, 0x4D, 0x41, 0x52, 0x59, 0x20]).buffer, + "00041130", + "CS", + new Uint8Array([ + 0x4f, 0x52, 0x49, 0x47, 0x49, 0x4e, 0x41, 0x4c, 0x20, 0x20, + 0x5c, 0x20, 0x50, 0x52, 0x49, 0x4d, 0x41, 0x52, 0x59, 0x20 + ]).buffer, ["ORIGINAL ", " PRIMARY"], ["ORIGINAL", "PRIMARY"] ], [ - '00181012', - 'DA', - new Uint8Array([0x32, 0x30, 0x32, 0x34, 0x30, 0x31, 0x30, 0x31]).buffer, + "00181012", + "DA", + new Uint8Array([0x32, 0x30, 0x32, 0x34, 0x30, 0x31, 0x30, 0x31]) + .buffer, ["20240101"], ["20240101"] ], [ - '00181041', - 'DS', - new Uint8Array([0x30, 0x30, 0x30, 0x30, 0x31, 0x32, 0x33, 0x2E, 0x34, 0x35]).buffer, + "00181041", + "DS", + new Uint8Array([ + 0x30, 0x30, 0x30, 0x30, 0x31, 0x32, 0x33, 0x2e, 0x34, 0x35 + ]).buffer, ["0000123.45"], [123.45] ], [ - '00181078', - 'DT', - new Uint8Array([0x32, 0x30, 0x32, 0x34, 0x30, 0x31, 0x30, 0x31, 0x31, 0x32, 0x33, 0x30, 0x34, 0x35, 0x2E, 0x31, 0x20, 0x20]).buffer, + "00181078", + "DT", + new Uint8Array([ + 0x32, 0x30, 0x32, 0x34, 0x30, 0x31, 0x30, 0x31, 0x31, 0x32, + 0x33, 0x30, 0x34, 0x35, 0x2e, 0x31, 0x20, 0x20 + ]).buffer, ["20240101123045.1 "], ["20240101123045.1 "] ], [ - '00182043', - 'FL', - new Uint8Array([0x66, 0x66, 0xA6, 0x3F, 0x66, 0x66, 0xA6, 0x3F]).buffer, + "00182043", + "FL", + new Uint8Array([0x66, 0x66, 0xa6, 0x3f, 0x66, 0x66, 0xa6, 0x3f]) + .buffer, [1.2999999523162842, 1.2999999523162842], [1.2999999523162842, 1.2999999523162842] ], [ - '00186028', - 'FD', - new Uint8Array([0x11, 0x2D, 0x44, 0x54, 0xFB, 0x21, 0x09, 0x40]).buffer, + "00186028", + "FD", + new Uint8Array([0x11, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40]) + .buffer, [3.14159265358979], [3.14159265358979] ], [ - '00200012', - 'IS', - new Uint8Array([0x20,0x2B,0x32,0x37,0x38,0x39,0x33,0x20]).buffer, + "00200012", + "IS", + new Uint8Array([0x20, 0x2b, 0x32, 0x37, 0x38, 0x39, 0x33, 0x20]) + .buffer, [" +27893 "], [27893] ], [ - '0018702A', - 'LO', - new Uint8Array([0x20,0x20,0x46,0x65,0x65,0x6C,0x69,0x6E,0x67,0x20,0x6E,0x61,0x75,0x73,0x65,0x6F,0x75,0x73,0x20,0x20]).buffer, + "0018702A", + "LO", + new Uint8Array([ + 0x20, 0x20, 0x46, 0x65, 0x65, 0x6c, 0x69, 0x6e, 0x67, 0x20, + 0x6e, 0x61, 0x75, 0x73, 0x65, 0x6f, 0x75, 0x73, 0x20, 0x20 + ]).buffer, [" Feeling nauseous "], ["Feeling nauseous"] ], [ - '00187040', - 'LT', - new Uint8Array([0x20,0x20,0x46,0x65,0x65,0x6C,0x69,0x6E,0x67,0x20,0x6E,0x61,0x75,0x73,0x65,0x6F,0x75,0x73,0x20,0x20]).buffer, + "00187040", + "LT", + new Uint8Array([ + 0x20, 0x20, 0x46, 0x65, 0x65, 0x6c, 0x69, 0x6e, 0x67, 0x20, + 0x6e, 0x61, 0x75, 0x73, 0x65, 0x6f, 0x75, 0x73, 0x20, 0x20 + ]).buffer, [" Feeling nauseous "], [" Feeling nauseous"] ], [ - '00282000', - 'OB', - new Uint8Array([0x13, 0x40, 0x80, 0x88, 0x88, 0x90, 0x88, 0x88]).buffer, - [new Uint8Array([0x13, 0x40, 0x80, 0x88, 0x88, 0x90, 0x88, 0x88]).buffer], - [new Uint8Array([0x13, 0x40, 0x80, 0x88, 0x88, 0x90, 0x88, 0x88]).buffer] + "00282000", + "OB", + new Uint8Array([0x13, 0x40, 0x80, 0x88, 0x88, 0x90, 0x88, 0x88]) + .buffer, + [ + new Uint8Array([ + 0x13, 0x40, 0x80, 0x88, 0x88, 0x90, 0x88, 0x88 + ]).buffer + ], + [ + new Uint8Array([ + 0x13, 0x40, 0x80, 0x88, 0x88, 0x90, 0x88, 0x88 + ]).buffer + ] ], [ - '00701A07', - 'OD', - new Uint8Array([0x00, 0x00, 0x00, 0x54, 0x34, 0x6F, 0x9D, 0x41]).buffer, - [new Uint8Array([0x00, 0x00, 0x00, 0x54, 0x34, 0x6F, 0x9D, 0x41]).buffer], - [new Uint8Array([0x00, 0x00, 0x00, 0x54, 0x34, 0x6F, 0x9D, 0x41]).buffer] + "00701A07", + "OD", + new Uint8Array([0x00, 0x00, 0x00, 0x54, 0x34, 0x6f, 0x9d, 0x41]) + .buffer, + [ + new Uint8Array([ + 0x00, 0x00, 0x00, 0x54, 0x34, 0x6f, 0x9d, 0x41 + ]).buffer + ], + [ + new Uint8Array([ + 0x00, 0x00, 0x00, 0x54, 0x34, 0x6f, 0x9d, 0x41 + ]).buffer + ] ], [ - '00720067', - 'OF', - new Uint8Array([0x00, 0x00, 0x28, 0x41, 0x00, 0x00, 0x30, 0xC0, 0x00, 0x00, 0xF6, 0x42]).buffer, - [new Uint8Array([0x00, 0x00, 0x28, 0x41, 0x00, 0x00, 0x30, 0xC0, 0x00, 0x00, 0xF6, 0x42]).buffer], - [new Uint8Array([0x00, 0x00, 0x28, 0x41, 0x00, 0x00, 0x30, 0xC0, 0x00, 0x00, 0xF6, 0x42]).buffer] + "00720067", + "OF", + new Uint8Array([ + 0x00, 0x00, 0x28, 0x41, 0x00, 0x00, 0x30, 0xc0, 0x00, 0x00, + 0xf6, 0x42 + ]).buffer, + [ + new Uint8Array([ + 0x00, 0x00, 0x28, 0x41, 0x00, 0x00, 0x30, 0xc0, 0x00, + 0x00, 0xf6, 0x42 + ]).buffer + ], + [ + new Uint8Array([ + 0x00, 0x00, 0x28, 0x41, 0x00, 0x00, 0x30, 0xc0, 0x00, + 0x00, 0xf6, 0x42 + ]).buffer + ] ], [ - '00281224', - 'OW', - new Uint8Array([0x13, 0x40, 0x80, 0x88, 0x88, 0x90, 0x88, 0x88]).buffer, - [new Uint8Array([0x13, 0x40, 0x80, 0x88, 0x88, 0x90, 0x88, 0x88]).buffer], - [new Uint8Array([0x13, 0x40, 0x80, 0x88, 0x88, 0x90, 0x88, 0x88]).buffer] + "00281224", + "OW", + new Uint8Array([0x13, 0x40, 0x80, 0x88, 0x88, 0x90, 0x88, 0x88]) + .buffer, + [ + new Uint8Array([ + 0x13, 0x40, 0x80, 0x88, 0x88, 0x90, 0x88, 0x88 + ]).buffer + ], + [ + new Uint8Array([ + 0x13, 0x40, 0x80, 0x88, 0x88, 0x90, 0x88, 0x88 + ]).buffer + ] ], [ - '00080090', - 'PN', - new Uint8Array([0x44, 0x6F, 0x65, 0x5E, 0x4A, 0x6F, 0x68, 0x6E, 0x5E, 0x41, 0x5E, 0x4A, 0x72, 0x2E, 0x5E, 0x4D, 0x44, 0x3D, 0x44, 0x6F, 0x65, 0x5E, 0x4A, 0x61, 0x79, 0x5E, 0x41, 0x5E, 0x4A, 0x72, 0x2E, 0x20]).buffer, + "00080090", + "PN", + new Uint8Array([ + 0x44, 0x6f, 0x65, 0x5e, 0x4a, 0x6f, 0x68, 0x6e, 0x5e, 0x41, + 0x5e, 0x4a, 0x72, 0x2e, 0x5e, 0x4d, 0x44, 0x3d, 0x44, 0x6f, + 0x65, 0x5e, 0x4a, 0x61, 0x79, 0x5e, 0x41, 0x5e, 0x4a, 0x72, + 0x2e, 0x20 + ]).buffer, ["Doe^John^A^Jr.^MD=Doe^Jay^A^Jr."], - [{"Alphabetic": "Doe^John^A^Jr.^MD", "Ideographic": "Doe^Jay^A^Jr."}] + [ + { + Alphabetic: "Doe^John^A^Jr.^MD", + Ideographic: "Doe^Jay^A^Jr." + } + ] ], [ - '00080094', - 'SH', - new Uint8Array([0x43,0x54,0x5F,0x53,0x43,0x41,0x4E,0x5F,0x30,0x31]).buffer, + "00080094", + "SH", + new Uint8Array([ + 0x43, 0x54, 0x5f, 0x53, 0x43, 0x41, 0x4e, 0x5f, 0x30, 0x31 + ]).buffer, ["CT_SCAN_01"], ["CT_SCAN_01"] ], [ - '00186020', - 'SL', - new Uint8Array([0x40, 0xE2, 0x01, 0x00, 0x40, 0xE2, 0x01, 0x00]).buffer, + "00186020", + "SL", + new Uint8Array([0x40, 0xe2, 0x01, 0x00, 0x40, 0xe2, 0x01, 0x00]) + .buffer, [123456, 123456], [123456, 123456] ], [ - '00189219', - 'SS', - new Uint8Array([0xD2, 0x04, 0xD2, 0x04, 0xD2, 0x04]).buffer, + "00189219", + "SS", + new Uint8Array([0xd2, 0x04, 0xd2, 0x04, 0xd2, 0x04]).buffer, [1234, 1234, 1234], [1234, 1234, 1234] ], [ - '00189373', - 'ST', - new Uint8Array([0x20,0x20,0x46,0x65,0x65,0x6C,0x69,0x6E,0x67,0x20,0x6E,0x61,0x75,0x73,0x65,0x6F,0x75,0x73,0x20,0x20]).buffer, + "00189373", + "ST", + new Uint8Array([ + 0x20, 0x20, 0x46, 0x65, 0x65, 0x6c, 0x69, 0x6e, 0x67, 0x20, + 0x6e, 0x61, 0x75, 0x73, 0x65, 0x6f, 0x75, 0x73, 0x20, 0x20 + ]).buffer, [" Feeling nauseous "], [" Feeling nauseous"] ], [ - '21000050', - 'TM', - new Uint8Array([0x34,0x32,0x35,0x33,0x30,0x2E,0x31,0x32,0x33,0x34,0x35,0x36]).buffer, + "21000050", + "TM", + new Uint8Array([ + 0x34, 0x32, 0x35, 0x33, 0x30, 0x2e, 0x31, 0x32, 0x33, 0x34, + 0x35, 0x36 + ]).buffer, ["42530.123456"], ["42530.123456"] ], [ - '3010001B', - 'UC', - new Uint8Array([0x54, 0x72, 0x61, 0x69, 0x6C, 0x69, 0x6E, 0x67, 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x20, 0x61, 0x6C, 0x6C, 0x6F, 0x77, 0x65, 0x64, 0x20, 0x20, 0x20]).buffer, + "3010001B", + "UC", + new Uint8Array([ + 0x54, 0x72, 0x61, 0x69, 0x6c, 0x69, 0x6e, 0x67, 0x20, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x6f, + 0x77, 0x65, 0x64, 0x20, 0x20, 0x20 + ]).buffer, ["Trailing spaces allowed "], ["Trailing spaces allowed"] ], [ - '00041510', - 'UI', - new Uint8Array([0x31,0x2E,0x32,0x2E,0x38,0x34,0x30,0x2E,0x31,0x30,0x30,0x30,0x38,0x2E,0x31,0x2E,0x32,0x2E,0x31]).buffer, + "00041510", + "UI", + new Uint8Array([ + 0x31, 0x2e, 0x32, 0x2e, 0x38, 0x34, 0x30, 0x2e, 0x31, 0x30, + 0x30, 0x30, 0x38, 0x2e, 0x31, 0x2e, 0x32, 0x2e, 0x31 + ]).buffer, ["1.2.840.10008.1.2.1"], ["1.2.840.10008.1.2.1"] ], [ - '30100092', - 'UL', - new Uint8Array([0x40, 0xE2, 0x01, 0x00]).buffer, + "30100092", + "UL", + new Uint8Array([0x40, 0xe2, 0x01, 0x00]).buffer, [123456], [123456] ], [ - '0008010E', - 'UR', - new Uint8Array([0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x64,0x69,0x63,0x6F,0x6D,0x2E,0x6E,0x65,0x6D,0x61,0x2E,0x6F,0x72,0x67, 0x20]).buffer, - ["http://dicom.nema.org "], + "0008010E", + "UR", + new Uint8Array([ + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x64, 0x69, 0x63, + 0x6f, 0x6d, 0x2e, 0x6e, 0x65, 0x6d, 0x61, 0x2e, 0x6f, 0x72, + 0x67, 0x20 + ]).buffer, ["http://dicom.nema.org "], + ["http://dicom.nema.org "] ], [ - '00080301', - 'US', - new Uint8Array([0xD2, 0x04]).buffer, - [1234], + "00080301", + "US", + new Uint8Array([0xd2, 0x04]).buffer, [1234], + [1234] ], [ - '0008030E', - 'UT', - new Uint8Array([0x20,0x20,0x46,0x65,0x65,0x6C,0x69,0x6E,0x67,0x20,0x6E,0x61,0x75,0x73,0x65,0x6F,0x75,0x73,0x20,0x20]).buffer, + "0008030E", + "UT", + new Uint8Array([ + 0x20, 0x20, 0x46, 0x65, 0x65, 0x6c, 0x69, 0x6e, 0x67, 0x20, + 0x6e, 0x61, 0x75, 0x73, 0x65, 0x6f, 0x75, 0x73, 0x20, 0x20 + ]).buffer, [" Feeling nauseous "], [" Feeling nauseous"] - ], + ] ])( "for tag %s with expected VR %p", (tag, vr, byteArray, expectedRawValue, expectedValue) => { @@ -1337,19 +1438,24 @@ describe("test_un_vr", () => { [tag]: { vr: "UN", _rawValue: [byteArray], - Value: [byteArray], - }, + Value: [byteArray] + } }; const dicomDict = new DicomDict({}); dicomDict.dict = dataset; // Write and re-read - const outputDicomDict = DicomMessage.readFile(dicomDict.write(), { forceStoreRaw: true }); + const outputDicomDict = DicomMessage.readFile( + dicomDict.write(), + { forceStoreRaw: true } + ); // Expect tag to be parsed correctly based on meta dictionary vr lookup expect(outputDicomDict.dict[tag].vr).toEqual(vr); - expect(outputDicomDict.dict[tag]._rawValue).toEqual(expectedRawValue); + expect(outputDicomDict.dict[tag]._rawValue).toEqual( + expectedRawValue + ); expect(outputDicomDict.dict[tag].Value).toEqual(expectedValue); } ); @@ -1381,24 +1487,25 @@ it.each([ } ); -describe('test OtherDouble ValueRepresentation', () => { - it('Treat OD as explicit VR with correct length', async () => { +describe("test OtherDouble ValueRepresentation", () => { + it("Treat OD as explicit VR with correct length", async () => { const url = "https://github.com/dcmjs-org/data/releases/download/od-encoding-data/OD-single-word-example.dcm"; - const dcmPath = await getTestDataset( - url, - "OD-single-word-example" - ); + const dcmPath = await getTestDataset(url, "OD-single-word-example"); const file = fs.readFileSync(dcmPath); - const data = dcmjs.data.DicomMessage.readFile(new Uint8Array(file).buffer); + const data = dcmjs.data.DicomMessage.readFile( + new Uint8Array(file).buffer + ); // expect OD VR data element (VolumetricCurveUpDirections) to be read with expected value - expect(data.dict['00701A07']).toBeTruthy(); - const odBuffer = data.dict['00701A07'].Value[0] - expect(new Uint8Array(odBuffer)).toEqual(new Uint8Array([0, 0, 0, 0, 0, 0, 0, 64])) + expect(data.dict["00701A07"]).toBeTruthy(); + const odBuffer = data.dict["00701A07"].Value[0]; + expect(new Uint8Array(odBuffer)).toEqual( + new Uint8Array([0, 0, 0, 0, 0, 0, 0, 64]) + ); // expect arbitrary tag (BlendingInputNumber, US VR) after OD VR to be read without issue - expect(data.dict['00701B02']).toBeTruthy(); - expect(data.dict['00701B02'].Value[0]).toBe(1); - }) -}); \ No newline at end of file + expect(data.dict["00701B02"]).toBeTruthy(); + expect(data.dict["00701B02"].Value[0]).toBe(1); + }); +}); diff --git a/test/lossless-read-write.test.js b/test/lossless-read-write.test.js index 0105c4d7..e4209b2f 100644 --- a/test/lossless-read-write.test.js +++ b/test/lossless-read-write.test.js @@ -8,14 +8,12 @@ import { getTestDataset } from "./testUtils"; const { DicomDict, DicomMessage } = dcmjs.data; - -describe('lossless-read-write', () => { - - describe('storeRaw option', () => { +describe("lossless-read-write", () => { + describe("storeRaw option", () => { const dataset = { - '00080008': { + "00080008": { vr: "CS", - Value: ["DERIVED"], + Value: ["DERIVED"] }, "00082112": { vr: "SQ", @@ -23,32 +21,32 @@ describe('lossless-read-write', () => { { "00081150": { vr: "UI", - Value: [ - "1.2.840.10008.5.1.4.1.1.7" - ], - }, + Value: ["1.2.840.10008.5.1.4.1.1.7"] + } } ] }, "00180050": { vr: "DS", - Value: [1], + Value: [1] }, "00181708": { vr: "IS", - Value: [426], + Value: [426] }, "00189328": { vr: "FD", - Value: [30.98], + Value: [30.98] }, "0020000D": { vr: "UI", - Value: ["1.3.6.1.4.1.5962.99.1.2280943358.716200484.1363785608958.3.0"], + Value: [ + "1.3.6.1.4.1.5962.99.1.2280943358.716200484.1363785608958.3.0" + ] }, "00400254": { vr: "LO", - Value: ["DUCTO/GALACTOGRAM 1 DUCT LT"], + Value: ["DUCTO/GALACTOGRAM 1 DUCT LT"] }, "7FE00010": { vr: "OW", @@ -56,8 +54,8 @@ describe('lossless-read-write', () => { } }; - test('storeRaw flag on VR should be respected by read', () => { - const tagsWithoutRaw = ['00082112', '7FE00010']; + test("storeRaw flag on VR should be respected by read", () => { + const tagsWithoutRaw = ["00082112", "7FE00010"]; const dicomDict = new DicomDict({}); dicomDict.dict = dataset; @@ -73,14 +71,14 @@ describe('lossless-read-write', () => { } }); - test('forceStoreRaw read option should override VR setting', () => { - const tagsWithoutRaw = ['00082112', '7FE00010']; - + test("forceStoreRaw read option should override VR setting", () => { const dicomDict = new DicomDict({}); dicomDict.dict = dataset; // write and re-read - const outputDicomDict = DicomMessage.readFile(dicomDict.write(), {forceStoreRaw: true}); + const outputDicomDict = DicomMessage.readFile(dicomDict.write(), { + forceStoreRaw: true + }); for (const tag in outputDicomDict.dict) { expect(outputDicomDict.dict[tag]._rawValue).toBeTruthy(); @@ -88,13 +86,13 @@ describe('lossless-read-write', () => { }); }); - test('test DS value with additional allowed characters is written to file', () => { + test("test DS value with additional allowed characters is written to file", () => { const dataset = { - '00181041': { + "00181041": { _rawValue: [" +1.4000 ", "-0.00", "1.2345e2", "1E34"], - Value: [1.4, -0, 123.45, 1e+34], - vr: 'DS' - }, + Value: [1.4, -0, 123.45, 1e34], + vr: "DS" + } }; const dicomDict = new DicomDict({}); @@ -104,17 +102,24 @@ describe('lossless-read-write', () => { const outputDicomDict = DicomMessage.readFile(dicomDict.write()); // expect raw value to be unchanged, and Value parsed as Number to lose precision - expect(outputDicomDict.dict['00181041']._rawValue).toEqual([" +1.4000 ", "-0.00", "1.2345e2", "1E34"]) - expect(outputDicomDict.dict['00181041'].Value).toEqual([1.4, -0, 123.45, 1e+34]) + expect(outputDicomDict.dict["00181041"]._rawValue).toEqual([ + " +1.4000 ", + "-0.00", + "1.2345e2", + "1E34" + ]); + expect(outputDicomDict.dict["00181041"].Value).toEqual([ + 1.4, -0, 123.45, 1e34 + ]); }); - test('test DS value that exceeds Number.MAX_SAFE_INTEGER is written to file', () => { + test("test DS value that exceeds Number.MAX_SAFE_INTEGER is written to file", () => { const dataset = { - '00181041': { + "00181041": { _rawValue: ["9007199254740993"], Value: [9007199254740993], - vr: 'DS' - }, + vr: "DS" + } }; const dicomDict = new DicomDict({}); @@ -124,15 +129,22 @@ describe('lossless-read-write', () => { const outputDicomDict = DicomMessage.readFile(dicomDict.write()); // expect raw value to be unchanged, and Value parsed as Number to lose precision - expect(outputDicomDict.dict['00181041']._rawValue).toEqual(["9007199254740993"]) - expect(outputDicomDict.dict['00181041'].Value).toEqual([9007199254740992]) + expect(outputDicomDict.dict["00181041"]._rawValue).toEqual([ + "9007199254740993" + ]); + expect(outputDicomDict.dict["00181041"].Value).toEqual([ + 9007199254740992 + ]); }); - test('test DS with multiplicity > 1 and added space for even padding is read and written correctly', () => { + test("test DS with multiplicity > 1 and added space for even padding is read and written correctly", () => { const dataset = { - '00200037': { - vr: 'DS', - Value: [0.99924236548978, -0.0322633220972, -0.0217663285287, 0.02949870928067, 0.99267261121054, -0.1171789789306] + "00200037": { + vr: "DS", + Value: [ + 0.99924236548978, -0.0322633220972, -0.0217663285287, + 0.02949870928067, 0.99267261121054, -0.1171789789306 + ] } }; @@ -144,28 +156,49 @@ describe('lossless-read-write', () => { // ensure _rawValue strings have no added trailing spaces const expectedDataset = { - '00200037': { - vr: 'DS', - Value: [0.99924236548978, -0.0322633220972, -0.0217663285287, 0.02949870928067, 0.99267261121054, -0.1171789789306], - _rawValue: ["0.99924236548978", "-0.0322633220972", "-0.0217663285287", "0.02949870928067", "0.99267261121054", "-0.1171789789306"] + "00200037": { + vr: "DS", + Value: [ + 0.99924236548978, -0.0322633220972, -0.0217663285287, + 0.02949870928067, 0.99267261121054, -0.1171789789306 + ], + _rawValue: [ + "0.99924236548978", + "-0.0322633220972", + "-0.0217663285287", + "0.02949870928067", + "0.99267261121054", + "-0.1171789789306" + ] } }; expect(deepEqual(expectedDataset, outputDicomDict.dict)).toBeTruthy(); // re-write should succeeed - const outputDicomDictPass2 = DicomMessage.readFile(outputDicomDict.write()); + const outputDicomDictPass2 = DicomMessage.readFile( + outputDicomDict.write() + ); // dataset should still be equal - expect(deepEqual(expectedDataset, outputDicomDictPass2.dict)).toBeTruthy(); + expect( + deepEqual(expectedDataset, outputDicomDictPass2.dict) + ).toBeTruthy(); }); - test('test DS with multiplicity > 1 with padding byte on last element within VR max length is losslessly read', () => { + test("test DS with multiplicity > 1 with padding byte on last element within VR max length is losslessly read", () => { const dataset = { - '00200037': { - vr: 'DS', - Value: [0.99924236548978, -0.0322633220972, -0.0217663285287, 0], - _rawValue: ["0.99924236548978", "-0.0322633220972", "-0.0217663285287", " +0.00 "] + "00200037": { + vr: "DS", + Value: [ + 0.99924236548978, -0.0322633220972, -0.0217663285287, 0 + ], + _rawValue: [ + "0.99924236548978", + "-0.0322633220972", + "-0.0217663285287", + " +0.00 " + ] } }; @@ -177,26 +210,35 @@ describe('lossless-read-write', () => { // ensure _rawValue strings have no added trailing spaces and retain original encoding details for + and spaces const expectedDataset = { - '00200037': { - vr: 'DS', - Value: [0.99924236548978, -0.0322633220972, -0.0217663285287, 0], - _rawValue: ["0.99924236548978", "-0.0322633220972", "-0.0217663285287", " +0.00"] + "00200037": { + vr: "DS", + Value: [ + 0.99924236548978, -0.0322633220972, -0.0217663285287, 0 + ], + _rawValue: [ + "0.99924236548978", + "-0.0322633220972", + "-0.0217663285287", + " +0.00" + ] } }; expect(outputDicomDict.dict).toEqual(expectedDataset); // re-write should succeeed - const outputDicomDictPass2 = DicomMessage.readFile(outputDicomDict.write()); + const outputDicomDictPass2 = DicomMessage.readFile( + outputDicomDict.write() + ); // dataset should still be equal expect(outputDicomDictPass2.dict).toEqual(expectedDataset); }); - test('test IS with multiplicity > 1 and added space for even padding is read and written correctly', () => { + test("test IS with multiplicity > 1 and added space for even padding is read and written correctly", () => { const dataset = { - '00081160': { - vr: 'IS', + "00081160": { + vr: "IS", Value: [1234, 5678] } }; @@ -209,8 +251,8 @@ describe('lossless-read-write', () => { // last _rawValue strings does allow trailing space as it does not exceed max length const expectedDataset = { - '00081160': { - vr: 'IS', + "00081160": { + vr: "IS", Value: [1234, 5678], _rawValue: ["1234", "5678"] } @@ -219,84 +261,103 @@ describe('lossless-read-write', () => { expect(outputDicomDict.dict).toEqual(expectedDataset); // re-write should succeeed - const outputDicomDictPass2 = DicomMessage.readFile(outputDicomDict.write()); + const outputDicomDictPass2 = DicomMessage.readFile( + outputDicomDict.write() + ); // dataset should still be equal expect(outputDicomDictPass2.dict).toEqual(expectedDataset); }); - describe('Multiplicity for non-binary String VRs', () => { + describe("Multiplicity for non-binary String VRs", () => { const maxLengthCases = [ { - vr: 'AE', + vr: "AE", Value: ["MAX_LENGTH_CHARS", "MAX_LENGTH_CHARS"], _rawValue: ["MAX_LENGTH_CHARS", "MAX_LENGTH_CHARS"] }, { - vr: 'AS', + vr: "AS", Value: ["120D", "045Y"], _rawValue: ["120D", "045Y"] }, { - vr: 'AT', - Value: [0x00207E14, 0x0012839A], - _rawValue: [0x00207E14, 0x0012839A], + vr: "AT", + Value: [0x00207e14, 0x0012839a], + _rawValue: [0x00207e14, 0x0012839a] }, { - vr: 'CS', + vr: "CS", Value: ["MAX_LENGTH_CHARS", "MAX_LENGTH_CHARS"], _rawValue: ["MAX_LENGTH_CHARS", "MAX_LENGTH_CHARS"] }, { - vr: 'DA', + vr: "DA", Value: ["20230826", "20230826"], _rawValue: ["20230826", "20230826"] }, { - vr: 'DS', + vr: "DS", Value: [123456789012.345, 123456789012.345], _rawValue: ["123456789012.345", "123456789012.345"] }, { - vr: 'DT', - Value: ["20230826123045.123456+0100", "20230826123045.123456+0100"], - _rawValue: ["20230826123045.123456+0100", "20230826123045.123456+0100"] + vr: "DT", + Value: [ + "20230826123045.123456+0100", + "20230826123045.123456+0100" + ], + _rawValue: [ + "20230826123045.123456+0100", + "20230826123045.123456+0100" + ] }, { - vr: 'IS', + vr: "IS", Value: [123456789012, 123456789012], _rawValue: ["123456789012", "123456789012"] }, { - vr: 'LO', - Value: ["ABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOP", "ABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOP"], - _rawValue: ["ABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOP", "ABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOP"] + vr: "LO", + Value: [ + "ABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOP", + "ABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOP" + ], + _rawValue: [ + "ABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOP", + "ABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOP" + ] }, { - vr: 'SH', + vr: "SH", Value: ["ABCDEFGHIJKLMNOP", "ABCDEFGHIJKLMNOP"], _rawValue: ["ABCDEFGHIJKLMNOP", "ABCDEFGHIJKLMNOP"] }, { - vr: 'UI', - Value: ["1.2.840.12345678901234567890123456789012345678901234567890123456", "1.2.840.12345678901234567890123456789012345678901234567890123456"], - _rawValue: ["1.2.840.12345678901234567890123456789012345678901234567890123456", "1.2.840.12345678901234567890123456789012345678901234567890123456"] + vr: "UI", + Value: [ + "1.2.840.12345678901234567890123456789012345678901234567890123456", + "1.2.840.12345678901234567890123456789012345678901234567890123456" + ], + _rawValue: [ + "1.2.840.12345678901234567890123456789012345678901234567890123456", + "1.2.840.12345678901234567890123456789012345678901234567890123456" + ] }, { - vr: 'TM', + vr: "TM", Value: ["142530.1234567", "142530.1234567"], - _rawValue: ["142530.1234567", "142530.1234567"], - }, - + _rawValue: ["142530.1234567", "142530.1234567"] + } ]; test.each(maxLengthCases)( `Test multiple values with VR max length handle pad byte correctly during read and write - $vr`, - (dataElement) => { + dataElement => { const dataset = { - '00081160': { + "00081160": { vr: dataElement.vr, - Value: dataElement.Value, + Value: dataElement.Value } }; @@ -304,103 +365,136 @@ describe('lossless-read-write', () => { dicomDict.dict = dataset; // write and re-read - const outputDicomDict = DicomMessage.readFile(dicomDict.write()); + const outputDicomDict = DicomMessage.readFile( + dicomDict.write() + ); // expect full _rawValue to match following read const expectedDataset = { - '00081160': { - ...dataElement, + "00081160": { + ...dataElement } }; expect(outputDicomDict.dict).toEqual(expectedDataset); // re-write should succeed without max length issues - const outputDicomDictPass2 = DicomMessage.readFile(outputDicomDict.write()); + const outputDicomDictPass2 = DicomMessage.readFile( + outputDicomDict.write() + ); // dataset should still be equal expect(expectedDataset).toEqual(outputDicomDictPass2.dict); } ); - }) - - describe('Individual VR comparisons', () => { + }); + describe("Individual VR comparisons", () => { const unchangedTestCases = [ { vr: "AE", _rawValue: [" TEST_AE "], // spaces non-significant for interpretation but allowed - Value: ["TEST_AE"], + Value: ["TEST_AE"] }, { vr: "AS", _rawValue: ["045Y"], - Value: ["045Y"], + Value: ["045Y"] }, { vr: "AT", - _rawValue: [0x00207E14, 0x0012839A], - Value: [0x00207E14, 0x0012839A], + _rawValue: [0x00207e14, 0x0012839a], + Value: [0x00207e14, 0x0012839a] }, { vr: "CS", _rawValue: ["ORIGINAL ", " PRIMARY"], // spaces non-significant for interpretation but allowed - Value: ["ORIGINAL", "PRIMARY"], + Value: ["ORIGINAL", "PRIMARY"] }, { vr: "DA", _rawValue: ["20240101"], - Value: ["20240101"], + Value: ["20240101"] }, { vr: "DS", _rawValue: ["0000123.45"], // leading zeros allowed - Value: [123.45], + Value: [123.45] }, { - vr: 'DT', + vr: "DT", _rawValue: ["20240101123045.1 "], // trailing spaces allowed - Value: ["20240101123045.1 "], + Value: ["20240101123045.1 "] }, { - vr: 'FL', + vr: "FL", _rawValue: [3.125], - Value: [3.125], + Value: [3.125] }, { - vr: 'FD', + vr: "FD", _rawValue: [3.14159265358979], // trailing spaces allowed - Value: [3.14159265358979], + Value: [3.14159265358979] }, { - vr: 'IS', + vr: "IS", _rawValue: [" -123 "], // leading/trailing spaces & sign allowed - Value: [-123], + Value: [-123] }, { - vr: 'LO', + vr: "LO", _rawValue: [" A long string with spaces "], // leading/trailing spaces allowed - Value: ["A long string with spaces"], + Value: ["A long string with spaces"] }, { - vr: 'LT', - _rawValue: [" It may contain the Graphic Character set and the Control Characters, CR\r, LF\n, FF\f, and ESC\x1b. "], // leading spaces significant, trailing spaces allowed - Value: [" It may contain the Graphic Character set and the Control Characters, CR\r, LF\n, FF\f, and ESC\x1b."], + vr: "LT", + _rawValue: [ + " It may contain the Graphic Character set and the Control Characters, CR\r, LF\n, FF\f, and ESC\x1b. " + ], // leading spaces significant, trailing spaces allowed + Value: [ + " It may contain the Graphic Character set and the Control Characters, CR\r, LF\n, FF\f, and ESC\x1b." + ] }, { - vr: 'OB', - _rawValue: [new Uint8Array([0x13, 0x40, 0x80, 0x88, 0x88, 0x90, 0x88, 0x88]).buffer], - Value: [new Uint8Array([0x13, 0x40, 0x80, 0x88, 0x88, 0x90, 0x88, 0x88]).buffer], + vr: "OB", + _rawValue: [ + new Uint8Array([ + 0x13, 0x40, 0x80, 0x88, 0x88, 0x90, 0x88, 0x88 + ]).buffer + ], + Value: [ + new Uint8Array([ + 0x13, 0x40, 0x80, 0x88, 0x88, 0x90, 0x88, 0x88 + ]).buffer + ] }, { - vr: 'OD', - _rawValue: [new Uint8Array([0x00, 0x00, 0x00, 0x54, 0x34, 0x6F, 0x9D, 0x41]).buffer], - Value: [new Uint8Array([0x00, 0x00, 0x00, 0x54, 0x34, 0x6F, 0x9D, 0x41]).buffer], + vr: "OD", + _rawValue: [ + new Uint8Array([ + 0x00, 0x00, 0x00, 0x54, 0x34, 0x6f, 0x9d, 0x41 + ]).buffer + ], + Value: [ + new Uint8Array([ + 0x00, 0x00, 0x00, 0x54, 0x34, 0x6f, 0x9d, 0x41 + ]).buffer + ] }, { - vr: 'OF', - _rawValue: [new Uint8Array([0x00, 0x00, 0x28, 0x41, 0x00, 0x00, 0x30, 0xC0, 0x00, 0x00, 0xF6, 0x42]).buffer], - Value: [new Uint8Array([0x00, 0x00, 0x28, 0x41, 0x00, 0x00, 0x30, 0xC0, 0x00, 0x00, 0xF6, 0x42]).buffer], + vr: "OF", + _rawValue: [ + new Uint8Array([ + 0x00, 0x00, 0x28, 0x41, 0x00, 0x00, 0x30, 0xc0, 0x00, + 0x00, 0xf6, 0x42 + ]).buffer + ], + Value: [ + new Uint8Array([ + 0x00, 0x00, 0x28, 0x41, 0x00, 0x00, 0x30, 0xc0, 0x00, + 0x00, 0xf6, 0x42 + ]).buffer + ] }, // TODO: VRs currently unimplemented // { @@ -414,34 +508,44 @@ describe('lossless-read-write', () => { // Value: [new Uint8Array([0x00, 0x00, 0x30, 0xC0, 0x00, 0x00, 0x28, 0x41]).buffer], // }, { - vr: 'OW', - _rawValue: [new Uint8Array([0x13, 0x40, 0x80, 0x88, 0x88, 0x90, 0x88, 0x88]).buffer], - Value: [new Uint8Array([0x13, 0x40, 0x80, 0x88, 0x88, 0x90, 0x88, 0x88]).buffer], + vr: "OW", + _rawValue: [ + new Uint8Array([ + 0x13, 0x40, 0x80, 0x88, 0x88, 0x90, 0x88, 0x88 + ]).buffer + ], + Value: [ + new Uint8Array([ + 0x13, 0x40, 0x80, 0x88, 0x88, 0x90, 0x88, 0x88 + ]).buffer + ] }, { - vr: 'PN', + vr: "PN", _rawValue: ["Doe^John^A^Jr.^MD "], // trailing spaces allowed - Value: [{"Alphabetic": "Doe^John^A^Jr.^MD "}], + Value: [{ Alphabetic: "Doe^John^A^Jr.^MD " }] }, { - vr: 'SH', + vr: "SH", _rawValue: [" CT_SCAN_01 "], // leading/trailing spaces allowed - Value: ["CT_SCAN_01"], + Value: ["CT_SCAN_01"] }, { - vr: 'SL', + vr: "SL", _rawValue: [-2147483648], - Value: [-2147483648], + Value: [-2147483648] }, { - vr: 'SS', + vr: "SS", _rawValue: [-32768, 1234, 832], - Value: [-32768, 1234, 832], + Value: [-32768, 1234, 832] }, { - vr: 'ST', - _rawValue: ["Patient complains of headaches over the last week. "], // trailing spaces allowed - Value: ["Patient complains of headaches over the last week."], + vr: "ST", + _rawValue: [ + "Patient complains of headaches over the last week. " + ], // trailing spaces allowed + Value: ["Patient complains of headaches over the last week."] }, // TODO: VR currently unimplemented // { @@ -450,40 +554,48 @@ describe('lossless-read-write', () => { // Value: [9007199254740993], // }, { - vr: 'TM', + vr: "TM", _rawValue: ["42530.123456 "], // trailing spaces allowed - Value: ["42530.123456"], + Value: ["42530.123456"] }, { - vr: 'UC', - _rawValue: ["Detailed description of procedure or clinical notes that could be very long. "], // trailing spaces allowed - Value: ["Detailed description of procedure or clinical notes that could be very long."], + vr: "UC", + _rawValue: [ + "Detailed description of procedure or clinical notes that could be very long. " + ], // trailing spaces allowed + Value: [ + "Detailed description of procedure or clinical notes that could be very long." + ] }, { - vr: 'UI', + vr: "UI", _rawValue: ["1.2.840.10008.1.2.1"], - Value: ["1.2.840.10008.1.2.1"], + Value: ["1.2.840.10008.1.2.1"] }, { - vr: 'UL', + vr: "UL", _rawValue: [4294967295], - Value: [4294967295], + Value: [4294967295] }, { - vr: 'UR', + vr: "UR", _rawValue: ["http://dicom.nema.org "], // trailing spaces ignored but allowed - Value: ["http://dicom.nema.org "], + Value: ["http://dicom.nema.org "] }, { - vr: 'US', + vr: "US", _rawValue: [65535], - Value: [65535], + Value: [65535] }, { - vr: 'UT', - _rawValue: [" This is a detailed explanation that can span multiple lines and paragraphs in the DICOM dataset. "], // leading spaces significant, trailing spaces allowed - Value: [" This is a detailed explanation that can span multiple lines and paragraphs in the DICOM dataset."], - }, + vr: "UT", + _rawValue: [ + " This is a detailed explanation that can span multiple lines and paragraphs in the DICOM dataset. " + ], // leading spaces significant, trailing spaces allowed + Value: [ + " This is a detailed explanation that can span multiple lines and paragraphs in the DICOM dataset." + ] + } // TODO: VR currently unimplemented // { // vr: 'UV', @@ -493,50 +605,57 @@ describe('lossless-read-write', () => { ]; test.each(unchangedTestCases)( `Test unchanged value is retained following read and write - $vr`, - (dataElement) => { + dataElement => { const dataset = { - '00181041': { + "00181041": { ...dataElement - }, + } }; const dicomDict = new DicomDict({}); dicomDict.dict = dataset; // write and re-read - const outputDicomDict = DicomMessage.readFile(dicomDict.write(), {forceStoreRaw: true}); + const outputDicomDict = DicomMessage.readFile( + dicomDict.write(), + { forceStoreRaw: true } + ); // expect raw value to be unchanged, and Value parsed as Number to lose precision - expect(outputDicomDict.dict['00181041']._rawValue).toEqual(dataElement._rawValue) - expect(outputDicomDict.dict['00181041'].Value).toEqual(dataElement.Value) + expect(outputDicomDict.dict["00181041"]._rawValue).toEqual( + dataElement._rawValue + ); + expect(outputDicomDict.dict["00181041"].Value).toEqual( + dataElement.Value + ); } - ) + ); const changedTestCases = [ { vr: "AE", _rawValue: [" TEST_AE "], // spaces non-significant for interpretation but allowed - Value: ["NEW_AE"], + Value: ["NEW_AE"] }, { vr: "AS", _rawValue: ["045Y"], - Value: ["999Y"], + Value: ["999Y"] }, { vr: "AT", - _rawValue: [0x00207E14, 0x0012839A], - Value: [0x00200010], + _rawValue: [0x00207e14, 0x0012839a], + Value: [0x00200010] }, { vr: "CS", _rawValue: ["ORIGINAL ", " PRIMARY "], // spaces non-significant for interpretation but allowed - Value: ["ORIGINAL", "PRIMARY", "SECONDARY"], + Value: ["ORIGINAL", "PRIMARY", "SECONDARY"] }, { vr: "DA", _rawValue: ["20240101"], - Value: ["20231225"], + Value: ["20231225"] }, { vr: "DS", @@ -545,50 +664,74 @@ describe('lossless-read-write', () => { newRawValue: ["123.456 "] }, { - vr: 'DT', + vr: "DT", _rawValue: ["20240101123045.1 "], // trailing spaces allowed - Value: ["20240101123045.3"], + Value: ["20240101123045.3"] }, { - vr: 'FL', + vr: "FL", _rawValue: [3.125], - Value: [22], + Value: [22] }, { - vr: 'FD', + vr: "FD", _rawValue: [3.14159265358979], // trailing spaces allowed - Value: [50.1242], + Value: [50.1242] }, { - vr: 'IS', + vr: "IS", _rawValue: [" -123 "], // leading/trailing spaces & sign allowed Value: [0], newRawValue: ["0 "] }, { - vr: 'LO', + vr: "LO", _rawValue: [" A long string with spaces "], // leading/trailing spaces allowed - Value: ["A changed string that is still long."], + Value: ["A changed string that is still long."] }, { - vr: 'LT', - _rawValue: [" It may contain the Graphic Character set and the Control Characters, CR\r, LF\n, FF\f, and ESC\x1b. "], // leading spaces significant, trailing spaces allowed - Value: [" A modified string of text"], + vr: "LT", + _rawValue: [ + " It may contain the Graphic Character set and the Control Characters, CR\r, LF\n, FF\f, and ESC\x1b. " + ], // leading spaces significant, trailing spaces allowed + Value: [" A modified string of text"] }, { - vr: 'OB', - _rawValue: [new Uint8Array([0x13, 0x40, 0x80, 0x88, 0x88, 0x90, 0x88, 0x88]).buffer], - Value: [new Uint8Array([0x01, 0x02]).buffer], + vr: "OB", + _rawValue: [ + new Uint8Array([ + 0x13, 0x40, 0x80, 0x88, 0x88, 0x90, 0x88, 0x88 + ]).buffer + ], + Value: [new Uint8Array([0x01, 0x02]).buffer] }, { - vr: 'OD', - _rawValue: [new Uint8Array([0x00, 0x00, 0x00, 0x54, 0x34, 0x6F, 0x9D, 0x41]).buffer], - Value: [new Uint8Array([0x00, 0x00, 0x00, 0x54, 0x35, 0x6E, 0x9E, 0x42]).buffer], + vr: "OD", + _rawValue: [ + new Uint8Array([ + 0x00, 0x00, 0x00, 0x54, 0x34, 0x6f, 0x9d, 0x41 + ]).buffer + ], + Value: [ + new Uint8Array([ + 0x00, 0x00, 0x00, 0x54, 0x35, 0x6e, 0x9e, 0x42 + ]).buffer + ] }, { - vr: 'OF', - _rawValue: [new Uint8Array([0x00, 0x00, 0x28, 0x41, 0x00, 0x00, 0x30, 0xC0, 0x00, 0x00, 0xF6, 0x42]).buffer], - Value: [new Uint8Array([0x00, 0x00, 0x28, 0x41, 0x00, 0x00, 0x30, 0xC0, 0x00, 0x00, 0xF6, 0x43]).buffer], + vr: "OF", + _rawValue: [ + new Uint8Array([ + 0x00, 0x00, 0x28, 0x41, 0x00, 0x00, 0x30, 0xc0, 0x00, + 0x00, 0xf6, 0x42 + ]).buffer + ], + Value: [ + new Uint8Array([ + 0x00, 0x00, 0x28, 0x41, 0x00, 0x00, 0x30, 0xc0, 0x00, + 0x00, 0xf6, 0x43 + ]).buffer + ] }, // TODO: VRs currently unimplemented // { @@ -600,35 +743,45 @@ describe('lossless-read-write', () => { // _rawValue: [new Uint8Array([0x00, 0x00, 0x30, 0xC0, 0x00, 0x00, 0x28, 0x41]).buffer], // }, { - vr: 'OW', - _rawValue: [new Uint8Array([0x13, 0x40, 0x80, 0x88, 0x89, 0x91, 0x89, 0x89]).buffer], - Value: [new Uint8Array([0x13, 0x40, 0x80, 0x88, 0x88, 0x90, 0x88, 0x88]).buffer], + vr: "OW", + _rawValue: [ + new Uint8Array([ + 0x13, 0x40, 0x80, 0x88, 0x89, 0x91, 0x89, 0x89 + ]).buffer + ], + Value: [ + new Uint8Array([ + 0x13, 0x40, 0x80, 0x88, 0x88, 0x90, 0x88, 0x88 + ]).buffer + ] }, { - vr: 'PN', + vr: "PN", _rawValue: ["Doe^John^A^Jr.^MD "], // trailing spaces allowed - Value: [{"Alphabetic": "Doe^Jane^A^Jr.^MD"}], + Value: [{ Alphabetic: "Doe^Jane^A^Jr.^MD" }], newRawValue: ["Doe^Jane^A^Jr.^MD"] }, { - vr: 'SH', + vr: "SH", _rawValue: [" CT_SCAN_01 "], // leading/trailing spaces allowed - Value: ["MR_SCAN_91"], + Value: ["MR_SCAN_91"] }, { - vr: 'SL', + vr: "SL", _rawValue: [-2147483648], - Value: [-2147481234], + Value: [-2147481234] }, { - vr: 'SS', + vr: "SS", _rawValue: [-32768, 1234, 832], - Value: [1234], + Value: [1234] }, { - vr: 'ST', - _rawValue: ["Patient complains of headaches over the last week. "], // trailing spaces allowed - Value: ["Patient complains of headaches"], + vr: "ST", + _rawValue: [ + "Patient complains of headaches over the last week. " + ], // trailing spaces allowed + Value: ["Patient complains of headaches"] }, // TODO: VR currently unimplemented // { @@ -636,41 +789,45 @@ describe('lossless-read-write', () => { // _rawValue: [9007199254740993], // trailing spaces allowed // }, { - vr: 'TM', + vr: "TM", _rawValue: ["42530.123456 "], // trailing spaces allowed Value: ["42530"], newRawValue: ["42530 "] }, { - vr: 'UC', - _rawValue: ["Detailed description of procedure or clinical notes that could be very long. "], // trailing spaces allowed - Value: ["Detailed description of procedure and other things"], + vr: "UC", + _rawValue: [ + "Detailed description of procedure or clinical notes that could be very long. " + ], // trailing spaces allowed + Value: ["Detailed description of procedure and other things"] }, { - vr: 'UI', + vr: "UI", _rawValue: ["1.2.840.10008.1.2.1"], - Value: ["1.2.840.10008.1.2.2"], + Value: ["1.2.840.10008.1.2.2"] }, { - vr: 'UL', + vr: "UL", _rawValue: [4294967295], - Value: [1], + Value: [1] }, { - vr: 'UR', + vr: "UR", _rawValue: ["http://dicom.nema.org "], // trailing spaces ignored but allowed - Value: ["https://github.com/dcmjs-org"], + Value: ["https://github.com/dcmjs-org"] }, { - vr: 'US', + vr: "US", _rawValue: [65535], - Value: [1], + Value: [1] }, { - vr: 'UT', - _rawValue: [" This is a detailed explanation that can span multiple lines and paragraphs in the DICOM dataset. "], // leading spaces significant, trailing spaces allowed - Value: [""], - }, + vr: "UT", + _rawValue: [ + " This is a detailed explanation that can span multiple lines and paragraphs in the DICOM dataset. " + ], // leading spaces significant, trailing spaces allowed + Value: [""] + } // TODO: VR currently unimplemented // { // vr: 'UV', @@ -680,37 +837,45 @@ describe('lossless-read-write', () => { test.each(changedTestCases)( `Test changed value overwrites original value following read and write - $vr`, - (dataElement) => { + dataElement => { const dataset = { - '00181041': { + "00181041": { ...dataElement - }, + } }; const dicomDict = new DicomDict({}); dicomDict.dict = dataset; // write and re-read - const outputDicomDict = DicomMessage.readFile(dicomDict.write(), {forceStoreRaw: true}); + const outputDicomDict = DicomMessage.readFile( + dicomDict.write(), + { forceStoreRaw: true } + ); // expect raw value to be updated to match new Value parsed as Number to lose precision - expect(outputDicomDict.dict['00181041']._rawValue).toEqual(dataElement.newRawValue ?? dataElement.Value) - expect(outputDicomDict.dict['00181041'].Value).toEqual(dataElement.Value) + expect(outputDicomDict.dict["00181041"]._rawValue).toEqual( + dataElement.newRawValue ?? dataElement.Value + ); + expect(outputDicomDict.dict["00181041"].Value).toEqual( + dataElement.Value + ); } - ) - + ); }); - describe('sequences', () => { - test('nested sequences should support lossless round trip', () => { + describe("sequences", () => { + test("nested sequences should support lossless round trip", () => { const dataset = { - "52009229": { + 52009229: { vr: "SQ", Value: [ { "0020000E": { vr: "UI", - Value: ["1.3.6.1.4.1.5962.99.1.2280943358.716200484.1363785608958.1.1"] + Value: [ + "1.3.6.1.4.1.5962.99.1.2280943358.716200484.1363785608958.1.1" + ] }, "00089123": { vr: "SQ", @@ -719,24 +884,24 @@ describe('lossless-read-write', () => { "00181030": { vr: "AE", _rawValue: [" TEST_AE "], - Value: ["TEST_AE"], + Value: ["TEST_AE"] }, "00180050": { vr: "DS", Value: [5.0], - _rawValue: ["5.000 "] + _rawValue: ["5.000 "] } }, { "00181030": { vr: "AE", _rawValue: [" TEST_AE "], - Value: ["TEST_AE"], + Value: ["TEST_AE"] }, "00180050": { vr: "DS", Value: [6.0], - _rawValue: ["6.000 "] + _rawValue: ["6.000 "] } } ] @@ -745,7 +910,9 @@ describe('lossless-read-write', () => { { "0020000E": { vr: "UI", - Value: ["1.3.6.1.4.1.5962.99.1.2280943358.716200484.1363785608958.1.2"] + Value: [ + "1.3.6.1.4.1.5962.99.1.2280943358.716200484.1363785608958.1.2" + ] }, "00089123": { vr: "SQ", @@ -756,9 +923,9 @@ describe('lossless-read-write', () => { Value: ["ABDOMEN MRI"] }, "00180050": { - vr: 'IS', + vr: "IS", _rawValue: [" -123 "], // leading/trailing spaces & sign allowed - Value: [-123], + Value: [-123] } } ] @@ -767,7 +934,7 @@ describe('lossless-read-write', () => { ] } }; - + const dicomDict = new DicomDict({}); dicomDict.dict = dataset; @@ -776,35 +943,48 @@ describe('lossless-read-write', () => { const outputDicomDict = DicomMessage.readFile(outputBuffer); // lossless read/write should match entire data set - deepEqual(dicomDict.dict, outputDicomDict.dict) - }) - }) + deepEqual(dicomDict.dict, outputDicomDict.dict); + }); + }); - test('File dataset should be equal after read and write', async () => { - const inputBuffer = await getDcmjsDataFile("unknown-VR", "sample-dicom-with-un-vr.dcm"); + test("File dataset should be equal after read and write", async () => { + const inputBuffer = await getDcmjsDataFile( + "unknown-VR", + "sample-dicom-with-un-vr.dcm" + ); const dicomDict = DicomMessage.readFile(inputBuffer); // confirm raw string representation of DS contains extra additional metadata // represented by bytes [30 2E 31 34 30 5C 30 2E 31 34 30 20] - expect(dicomDict.dict['00280030']._rawValue).toEqual(["0.140", "0.140 "]) - expect(dicomDict.dict['00280030'].Value).toEqual([0.14, 0.14]) + expect(dicomDict.dict["00280030"]._rawValue).toEqual([ + "0.140", + "0.140 " + ]); + expect(dicomDict.dict["00280030"].Value).toEqual([0.14, 0.14]); // confirm after write raw values are re-encoded const outputBuffer = dicomDict.write(); const outputDicomDict = DicomMessage.readFile(outputBuffer); // explicitly verify for DS for clarity - expect(outputDicomDict.dict['00280030']._rawValue).toEqual(["0.140", "0.140 "]) - expect(outputDicomDict.dict['00280030'].Value).toEqual([0.14, 0.14]) + expect(outputDicomDict.dict["00280030"]._rawValue).toEqual([ + "0.140", + "0.140 " + ]); + expect(outputDicomDict.dict["00280030"].Value).toEqual([0.14, 0.14]); // lossless read/write should match entire data set - deepEqual(dicomDict.dict, outputDicomDict.dict) + deepEqual(dicomDict.dict, outputDicomDict.dict); }); }); const getDcmjsDataFile = async (release, fileName) => { - const url = "https://github.com/dcmjs-org/data/releases/download/" + release + "/" + fileName; + const url = + "https://github.com/dcmjs-org/data/releases/download/" + + release + + "/" + + fileName; const dcmPath = await getTestDataset(url, fileName); return fs.readFileSync(dcmPath).buffer; -} +}; diff --git a/test/testUtils.js b/test/testUtils.js index 7353077d..4fb4c12d 100644 --- a/test/testUtils.js +++ b/test/testUtils.js @@ -29,7 +29,6 @@ function unzip(zipFilePath, targetPath) { try { // reading archives var zip = new AdmZip(zipFilePath); - var zipEntries = zip.getEntries(); // an array of ZipEntry records // extracts everything zip.extractAllTo(targetPath, true); resolve(); @@ -76,7 +75,7 @@ async function getTestDataset(url, filename) { let filePromise = asyncDownloadMap.get(targetPath); if (!filePromise && !fs.existsSync(targetPath)) { filePromise = downloadToFile(url, targetPath); - asyncDownloadMap.set(targetPath,filePromise); + asyncDownloadMap.set(targetPath, filePromise); } // This returns immediately if filePromise is undefined - eg if the file already downloaded. await filePromise; diff --git a/test/utilities/deepEqual.test.js b/test/utilities/deepEqual.test.js index 700000d0..5ac14787 100644 --- a/test/utilities/deepEqual.test.js +++ b/test/utilities/deepEqual.test.js @@ -1,78 +1,87 @@ import { deepEqual } from "../../src/utilities/deepEqual"; -describe('deepEqual', () => { - test('returns true for identical primitives', () => { +describe("deepEqual", () => { + test("returns true for identical primitives", () => { expect(deepEqual(42, 42)).toBe(true); - expect(deepEqual('hello', 'hello')).toBe(true); + expect(deepEqual("hello", "hello")).toBe(true); expect(deepEqual(true, true)).toBe(true); expect(deepEqual(null, null)).toBe(true); }); - test('returns false for different primitives', () => { + test("returns false for different primitives", () => { expect(deepEqual(42, 43)).toBe(false); - expect(deepEqual('hello', 'world')).toBe(false); + expect(deepEqual("hello", "world")).toBe(false); expect(deepEqual(true, false)).toBe(false); expect(deepEqual(null, undefined)).toBe(false); }); - test('returns same value check for signed zeros and special numbers', () => { + test("returns same value check for signed zeros and special numbers", () => { expect(deepEqual(Math.NaN, Math.NaN)).toBe(true); expect(deepEqual(-0, 0)).toBe(false); expect(deepEqual(-0, +0)).toBe(false); - }) + }); - test('returns true for deeply equal objects', () => { + test("returns true for deeply equal objects", () => { const obj1 = { a: 1, b: { c: 2 } }; const obj2 = { a: 1, b: { c: 2 } }; expect(deepEqual(obj1, obj2)).toBe(true); }); - test('returns false for objects with different structures', () => { + test("returns false for objects with different structures", () => { const obj1 = { a: 1, b: { c: 2 } }; const obj2 = { a: 1, b: { d: 2 } }; expect(deepEqual(obj1, obj2)).toBe(false); }); - test('returns false for objects with different values', () => { + test("returns false for objects with different values", () => { const obj1 = { a: 1, b: { c: 2 } }; const obj2 = { a: 1, b: { c: 3 } }; expect(deepEqual(obj1, obj2)).toBe(false); }); - test('returns true for deeply equal arrays', () => { + test("returns true for deeply equal arrays", () => { const arr1 = [1, 2, { a: 3 }]; const arr2 = [1, 2, { a: 3 }]; expect(deepEqual(arr1, arr2)).toBe(true); }); - test('returns false for arrays with different values', () => { + test("returns false for arrays with different values", () => { const arr1 = [1, 2, { a: 3 }]; const arr2 = [1, 2, { a: 4 }]; expect(deepEqual(arr1, arr2)).toBe(false); }); - test('returns false for objects compared with arrays', () => { + test("returns false for objects compared with arrays", () => { const obj = { a: 1, b: 2 }; const arr = [1, 2]; expect(deepEqual(obj, arr)).toBe(false); }); - test('returns false for different object types', () => { + test("returns false for different object types", () => { const date1 = new Date(2024, 0, 1); - const date2 = new Date(2024, 0, 1); const obj1 = { a: 1, b: 2 }; expect(deepEqual(date1, obj1)).toBe(false); }); - test('returns true for nested objects with arrays', () => { + test("returns true for nested objects with arrays", () => { const obj1 = { a: 1, b: [1, 2, { c: 3 }] }; const obj2 = { a: 1, b: [1, 2, { c: 3 }] }; expect(deepEqual(obj1, obj2)).toBe(true); }); - test('returns false for functions, as they should not be equal', () => { - const obj1 = { a: 1, b: function() { return 2; } }; - const obj2 = { a: 1, b: function() { return 2; } }; + test("returns false for functions, as they should not be equal", () => { + const obj1 = { + a: 1, + b: function () { + return 2; + } + }; + const obj2 = { + a: 1, + b: function () { + return 2; + } + }; expect(deepEqual(obj1, obj2)).toBe(false); }); -}); \ No newline at end of file +}); From 5313cbedee325c2052110b9600372d706dd80f8e Mon Sep 17 00:00:00 2001 From: Igor Octaviano Date: Tue, 15 Oct 2024 18:01:54 -0300 Subject: [PATCH 2/7] fix(src/test): Address lint issues --- src/BufferStream.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BufferStream.js b/src/BufferStream.js index dc7be67f..861e5c38 100644 --- a/src/BufferStream.js +++ b/src/BufferStream.js @@ -370,7 +370,7 @@ class ReadBufferStream extends BufferStream { throw new Error(value, "writeUint8 not implemented"); } - writeUint8Repeat(value) { + writeUint8Repeat(value, count) { throw new Error(value, "writeUint8Repeat not implemented"); } From 23049ca41c8482392c642335197261c4e34df328 Mon Sep 17 00:00:00 2001 From: Igor Octaviano Date: Tue, 15 Oct 2024 18:15:24 -0300 Subject: [PATCH 3/7] Add lint workflow and use yarn --- .github/workflows/lint-and-format.yml | 27 +++++++++++++++++++++++++++ .github/workflows/tests.yml | 4 ++-- 2 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/lint-and-format.yml diff --git a/.github/workflows/lint-and-format.yml b/.github/workflows/lint-and-format.yml new file mode 100644 index 00000000..fecad9a3 --- /dev/null +++ b/.github/workflows/lint-and-format.yml @@ -0,0 +1,27 @@ +name: Lint and Format + +on: + - push + - pull_request + +jobs: + lint-and-format: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 18 + + - name: Install packages + run: yarn install --frozen-lockfile + + - name: Run ESLint + run: yarn lint + + - name: Check Prettier formatting + run: yarn format:check diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 453e730b..f74ccde3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,7 +18,7 @@ jobs: node-version: 18 - name: Install packages - uses: bahmutov/npm-install@v1 + run: yarn install --frozen-lockfile - name: Run tests - run: npm run test + run: yarn test From 37a08a1bf3ba42f8f5c3d8b7d1f8bf594366603a Mon Sep 17 00:00:00 2001 From: Igor Octaviano Date: Tue, 15 Oct 2024 18:19:25 -0300 Subject: [PATCH 4/7] Update lint rules --- .eslintrc.json | 4 ++++ src/BufferStream.js | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.eslintrc.json b/.eslintrc.json index 45b66cda..236fcaa9 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -14,5 +14,9 @@ "parserOptions": { "ecmaVersion": 11, "sourceType": "module" + }, + + "rules": { + "no-loss-of-precision": "off" } } diff --git a/src/BufferStream.js b/src/BufferStream.js index 861e5c38..9167ba9e 100644 --- a/src/BufferStream.js +++ b/src/BufferStream.js @@ -371,7 +371,9 @@ class ReadBufferStream extends BufferStream { } writeUint8Repeat(value, count) { - throw new Error(value, "writeUint8Repeat not implemented"); + throw new Error( + `writeUint8Repeat not implemented (value: ${value}, count: ${count})` + ); } writeInt8(value) { From 9bb679d1114eebb2312d191d7344bed2ef7c4795 Mon Sep 17 00:00:00 2001 From: Igor Octaviano Date: Tue, 15 Oct 2024 18:21:28 -0300 Subject: [PATCH 5/7] Add prettier check --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 7102abac..9dbc324e 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "build:examples": "npm run build && npx cpx 'build/**/*.{js,map}' examples/js", "start": "rollup -c -w", "format": "prettier --write 'src/**/*.js' 'test/**/*.js'", + "format:check": "prettier --check 'src/**/*.js' 'test/**/*.js'", "lint": "eslint --fix 'src/**/*.js' 'test/**/*.js'" }, "repository": { From c7a4b92359113192eb90ed05748c0528c1b658be Mon Sep 17 00:00:00 2001 From: Igor Octaviano Date: Tue, 15 Oct 2024 18:22:40 -0300 Subject: [PATCH 6/7] Run frmat --- src/ValueRepresentation.js | 46 ++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/ValueRepresentation.js b/src/ValueRepresentation.js index 39e4e336..21c0b1d6 100644 --- a/src/ValueRepresentation.js +++ b/src/ValueRepresentation.js @@ -170,7 +170,6 @@ class ValueRepresentation { return values; } - read(stream, length, syntax, readOptions = { forceStoreRaw: false }) { if (this.fixed && this.maxLength) { if (!length) return this.defaultValue; @@ -639,14 +638,16 @@ class CodeString extends AsciiStringRepresentation { readBytes(stream, length) { const BACKSLASH = String.fromCharCode(VM_DELIMITER); - return this.dropPadByte(stream.readAsciiString(length).split(BACKSLASH)); + return this.dropPadByte( + stream.readAsciiString(length).split(BACKSLASH) + ); } applyFormatting(value) { - const trim = (str) => str.trim(); + const trim = str => str.trim(); if (Array.isArray(value)) { - return value.map((str) => trim(str)); + return value.map(str => trim(str)); } return trim(value); @@ -697,7 +698,6 @@ class DateValue extends AsciiStringRepresentation { } class NumericStringRepresentation extends AsciiStringRepresentation { - readBytes(stream, length) { const BACKSLASH = String.fromCharCode(VM_DELIMITER); const numStr = stream.readAsciiString(length); @@ -714,10 +714,10 @@ class DecimalString extends NumericStringRepresentation { } applyFormatting(value) { - const formatNumber = (numberStr) => { + const formatNumber = numberStr => { let returnVal = numberStr.trim().replace(/[^0-9.\\\-+e]/gi, ""); return returnVal === "" ? null : Number(returnVal); - } + }; if (Array.isArray(value)) { return value.map(formatNumber); @@ -728,7 +728,7 @@ class DecimalString extends NumericStringRepresentation { convertToString(value) { if (value === null) return ""; - if (typeof value === 'string') return value; + if (typeof value === "string") return value; let str = String(value); if (str.length > this.maxLength) { @@ -841,10 +841,10 @@ class IntegerString extends NumericStringRepresentation { } applyFormatting(value) { - const formatNumber = (numberStr) => { + const formatNumber = numberStr => { let returnVal = numberStr.trim().replace(/[^0-9.\\\-+e]/gi, ""); return returnVal === "" ? null : Number(returnVal); - } + }; if (Array.isArray(value)) { return value.map(formatNumber); @@ -854,7 +854,7 @@ class IntegerString extends NumericStringRepresentation { } convertToString(value) { - if (typeof value === 'string') return value; + if (typeof value === "string") return value; return value === null ? "" : String(value); } @@ -967,14 +967,17 @@ class PersonName extends EncodedStringRepresentation { } readBytes(stream, length) { - return this.readPaddedEncodedString(stream, length).split(String.fromCharCode(VM_DELIMITER)); + return this.readPaddedEncodedString(stream, length).split( + String.fromCharCode(VM_DELIMITER) + ); } applyFormatting(value) { - const parsePersonName = (valueStr) => dicomJson.pnConvertToJsonObject(valueStr, false); + const parsePersonName = valueStr => + dicomJson.pnConvertToJsonObject(valueStr, false); if (Array.isArray(value)) { - return value.map((valueStr) => parsePersonName(valueStr)); + return value.map(valueStr => parsePersonName(valueStr)); } return parsePersonName(value); @@ -1321,16 +1324,16 @@ class UniqueIdentifier extends AsciiStringRepresentation { // https://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_6.4.html if (result.indexOf(BACKSLASH) === -1) { - return result + return result; } else { return this.dropPadByte(result.split(BACKSLASH)); } } applyFormatting(value) { - const removeInvalidUidChars = (uidStr) => { + const removeInvalidUidChars = uidStr => { return uidStr.replace(/[^0-9.]/g, ""); - } + }; if (Array.isArray(value)) { return value.map(removeInvalidUidChars); @@ -1385,7 +1388,12 @@ class ParsedUnknownValue extends BinaryRepresentation { i = 0; while (i++ < times) { - const { rawValue, value } = vr.read(streamFromBuffer, vr.maxLength, syntax, readOptions); + const { rawValue, value } = vr.read( + streamFromBuffer, + vr.maxLength, + syntax, + readOptions + ); rawValues.push(rawValue); values.push(value); } @@ -1466,4 +1474,4 @@ let VRinstances = { UT: new UnlimitedText() }; -export {ValueRepresentation}; +export { ValueRepresentation }; From d9eac4f603d12f0c8ff6064e605d34d661349db7 Mon Sep 17 00:00:00 2001 From: Igor Octaviano Date: Tue, 15 Oct 2024 18:23:25 -0300 Subject: [PATCH 7/7] Update action title --- .github/workflows/lint-and-format.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint-and-format.yml b/.github/workflows/lint-and-format.yml index fecad9a3..bbf34ac7 100644 --- a/.github/workflows/lint-and-format.yml +++ b/.github/workflows/lint-and-format.yml @@ -1,4 +1,4 @@ -name: Lint and Format +name: Run lint and format check on: - push