fix(app): display study not found message for all modes (#5752)
* fix(app): move study validation to ModeRoute component - Moved validation from defaultRouteInit.ts to Mode.tsx - Added dedicated useEffect hook for study validation * test: add Playwright test for study not found error page across multiple modes
This commit is contained in:
parent
3c0dd39054
commit
7f9e59ee30
@ -122,11 +122,6 @@ function PanelStudyBrowser({
|
||||
studyInstanceUid: StudyInstanceUID,
|
||||
});
|
||||
|
||||
if (!qidoForStudyUID?.length) {
|
||||
navigate('/notfoundstudy', '_self');
|
||||
throw new Error('Invalid study URL');
|
||||
}
|
||||
|
||||
let qidoStudiesForPatient = qidoForStudyUID;
|
||||
|
||||
// try to fetch the prior studies based on the patientID if the
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import { useParams, useLocation } from 'react-router';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import { utils } from '@ohif/core';
|
||||
import { ImageViewerProvider, DragAndDropProvider } from '@ohif/ui-next';
|
||||
@ -31,6 +32,8 @@ export default function ModeRoute({
|
||||
// The URL's query search parameters where the keys casing is maintained
|
||||
const query = useSearchParams();
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
mode?.onModeInit?.({
|
||||
servicesManager,
|
||||
extensionManager,
|
||||
@ -130,6 +133,38 @@ export default function ModeRoute({
|
||||
};
|
||||
}, [location, ExtensionDependenciesLoaded]);
|
||||
|
||||
/**
|
||||
* Validates study existence before loading the viewer.
|
||||
* Moved from PanelStudyBrowser.tsx to ensure validation runs in all modes
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!ExtensionDependenciesLoaded || !studyInstanceUIDs?.length || !dataSource) {
|
||||
return;
|
||||
}
|
||||
|
||||
const validateStudies = async () => {
|
||||
for (const studyInstanceUID of studyInstanceUIDs) {
|
||||
try {
|
||||
const qidoForStudyUID = await dataSource.query.studies.search({
|
||||
studyInstanceUid: studyInstanceUID,
|
||||
});
|
||||
|
||||
if (!qidoForStudyUID?.length) {
|
||||
console.warn('Study not found:', studyInstanceUID);
|
||||
navigate('/notfoundstudy');
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error validating study:', studyInstanceUID, error);
|
||||
navigate('/notfoundstudy');
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
validateStudies();
|
||||
}, [studyInstanceUIDs, ExtensionDependenciesLoaded, dataSource, navigate]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!ExtensionDependenciesLoaded || !studyInstanceUIDs?.length) {
|
||||
return;
|
||||
|
||||
@ -38,12 +38,22 @@ const NotFoundStudy = () => {
|
||||
return (
|
||||
<div className="absolute flex h-full w-full items-center justify-center text-white">
|
||||
<div>
|
||||
<h4>
|
||||
<h4 data-cy="study-not-found-message">
|
||||
One or more of the requested studies are not available at this time.
|
||||
</h4>
|
||||
{showStudyList && (
|
||||
<p className="mt-2">
|
||||
Return to the <Link className="text-primary-light" to="/">study list</Link> to select a different study to view.
|
||||
<p
|
||||
className="mt-2"
|
||||
data-cy="return-to-study-list-message"
|
||||
>
|
||||
Return to the{' '}
|
||||
<Link
|
||||
className="text-primary-light"
|
||||
to="/"
|
||||
>
|
||||
study list
|
||||
</Link>{' '}
|
||||
to select a different study to view.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
83
tests/StudyValidation.spec.ts
Normal file
83
tests/StudyValidation.spec.ts
Normal file
@ -0,0 +1,83 @@
|
||||
import { expect, test } from './utils';
|
||||
|
||||
test.describe('Study Validation', () => {
|
||||
const invalidStudyUID = '9.9.9.9.9.9.9.9.9.9.9.9.9.9.9.9.9.9.9.9.9.9.9.9';
|
||||
const validStudyUID = '1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5';
|
||||
|
||||
const modes = ['viewer', 'segmentation', 'microscopy', 'tmtv'];
|
||||
|
||||
// Test 1: Single invalid study
|
||||
modes.forEach(mode => {
|
||||
test(`should display error page when study UID is not found in ${mode} mode`, async ({
|
||||
page,
|
||||
notFoundStudyPageObject,
|
||||
}) => {
|
||||
await page.goto(`/${mode}/ohif?StudyInstanceUIDs=${invalidStudyUID}`);
|
||||
|
||||
await page.waitForURL('**/notfoundstudy', { timeout: 15000 });
|
||||
|
||||
await expect(notFoundStudyPageObject.errorMessage).toBeVisible();
|
||||
await expect(notFoundStudyPageObject.errorMessage).toHaveText(
|
||||
'One or more of the requested studies are not available at this time.'
|
||||
);
|
||||
|
||||
await expect(notFoundStudyPageObject.returnMessage).toBeVisible();
|
||||
await expect(notFoundStudyPageObject.returnMessage).toContainText(
|
||||
'Return to the study list to select a different study to view.'
|
||||
);
|
||||
|
||||
await expect(notFoundStudyPageObject.studyListLink).toBeVisible();
|
||||
await expect(notFoundStudyPageObject.studyListLink).toHaveAttribute('href', '/');
|
||||
});
|
||||
});
|
||||
|
||||
// Test 2: Multiple studies - valid first, then invalid
|
||||
modes.forEach(mode => {
|
||||
test(`should display error page when second study UID is invalid in ${mode} mode`, async ({
|
||||
page,
|
||||
notFoundStudyPageObject,
|
||||
}) => {
|
||||
await page.goto(`/${mode}/ohif?StudyInstanceUIDs=${validStudyUID},${invalidStudyUID}`);
|
||||
|
||||
await page.waitForURL('**/notfoundstudy', { timeout: 15000 });
|
||||
|
||||
await expect(notFoundStudyPageObject.errorMessage).toBeVisible();
|
||||
await expect(notFoundStudyPageObject.errorMessage).toHaveText(
|
||||
'One or more of the requested studies are not available at this time.'
|
||||
);
|
||||
|
||||
await expect(notFoundStudyPageObject.returnMessage).toBeVisible();
|
||||
await expect(notFoundStudyPageObject.returnMessage).toContainText(
|
||||
'Return to the study list to select a different study to view.'
|
||||
);
|
||||
|
||||
await expect(notFoundStudyPageObject.studyListLink).toBeVisible();
|
||||
await expect(notFoundStudyPageObject.studyListLink).toHaveAttribute('href', '/');
|
||||
});
|
||||
});
|
||||
|
||||
// Test 3: Multiple studies - invalid first, then valid
|
||||
modes.forEach(mode => {
|
||||
test(`should display error page when first study UID is invalid in ${mode} mode`, async ({
|
||||
page,
|
||||
notFoundStudyPageObject,
|
||||
}) => {
|
||||
await page.goto(`/${mode}/ohif?StudyInstanceUIDs=${invalidStudyUID},${validStudyUID}`);
|
||||
|
||||
await page.waitForURL('**/notfoundstudy', { timeout: 15000 });
|
||||
|
||||
await expect(notFoundStudyPageObject.errorMessage).toBeVisible();
|
||||
await expect(notFoundStudyPageObject.errorMessage).toHaveText(
|
||||
'One or more of the requested studies are not available at this time.'
|
||||
);
|
||||
|
||||
await expect(notFoundStudyPageObject.returnMessage).toBeVisible();
|
||||
await expect(notFoundStudyPageObject.returnMessage).toContainText(
|
||||
'Return to the study list to select a different study to view.'
|
||||
);
|
||||
|
||||
await expect(notFoundStudyPageObject.studyListLink).toBeVisible();
|
||||
await expect(notFoundStudyPageObject.studyListLink).toHaveAttribute('href', '/');
|
||||
});
|
||||
});
|
||||
});
|
||||
21
tests/pages/NotFoundStudyPageObject.ts
Normal file
21
tests/pages/NotFoundStudyPageObject.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { Locator, Page } from '@playwright/test';
|
||||
|
||||
export class NotFoundStudyPageObject {
|
||||
readonly page: Page;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
get errorMessage(): Locator {
|
||||
return this.page.locator('[data-cy="study-not-found-message"]');
|
||||
}
|
||||
|
||||
get returnMessage(): Locator {
|
||||
return this.page.locator('[data-cy="return-to-study-list-message"]');
|
||||
}
|
||||
|
||||
get studyListLink(): Locator {
|
||||
return this.page.getByRole('link', { name: 'study list' });
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,7 @@ import { MainToolbarPageObject } from './MainToolbarPageObject';
|
||||
import { LeftPanelPageObject } from './LeftPanelPageObject';
|
||||
import { RightPanelPageObject } from './RightPanelPageObject';
|
||||
import { ViewportPageObject } from './ViewportPageObject';
|
||||
import { NotFoundStudyPageObject } from './NotFoundStudyPageObject';
|
||||
|
||||
export {
|
||||
DOMOverlayPageObject,
|
||||
@ -10,4 +11,5 @@ export {
|
||||
LeftPanelPageObject,
|
||||
RightPanelPageObject,
|
||||
ViewportPageObject,
|
||||
NotFoundStudyPageObject,
|
||||
};
|
||||
|
||||
@ -5,6 +5,7 @@ import {
|
||||
LeftPanelPageObject,
|
||||
RightPanelPageObject,
|
||||
ViewportPageObject,
|
||||
NotFoundStudyPageObject,
|
||||
} from '../pages';
|
||||
|
||||
type PageObjects = {
|
||||
@ -13,6 +14,7 @@ type PageObjects = {
|
||||
leftPanelPageObject: LeftPanelPageObject;
|
||||
rightPanelPageObject: RightPanelPageObject;
|
||||
viewportPageObject: ViewportPageObject;
|
||||
notFoundStudyPageObject: NotFoundStudyPageObject;
|
||||
};
|
||||
|
||||
export const test = base.extend<PageObjects>({
|
||||
@ -31,6 +33,9 @@ export const test = base.extend<PageObjects>({
|
||||
viewportPageObject: async ({ page }, use) => {
|
||||
await use(new ViewportPageObject(page));
|
||||
},
|
||||
notFoundStudyPageObject: async ({ page }, use) => {
|
||||
await use(new NotFoundStudyPageObject(page));
|
||||
},
|
||||
});
|
||||
|
||||
export { expect } from 'playwright-test-coverage';
|
||||
|
||||
Loading…
Reference in New Issue
Block a user