Skip to content

Commit

Permalink
Release v2.5.0
Browse files Browse the repository at this point in the history
  • Loading branch information
adriancofie committed Jul 1, 2024
2 parents 86b56ae + 609cf94 commit 117c185
Show file tree
Hide file tree
Showing 26 changed files with 434 additions and 227 deletions.
8 changes: 8 additions & 0 deletions cypress/e2e/ErrorBoundary/ErrorBoundary.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Feature: As a user, when the listing API encounters and error, I am presented with an error page
Scenario: User sees an error page when the CTS API encounters errors while rendering a trial description page
And the CTS API is responding with a server error
Given the user navigates to "/v?a=40&id=NCI-2014-01507&loc=0&rl=2"
Then page title on error page is "An error occurred. Please try again later."
And the page contains meta tags with the following names
| name | content |
| prerender-status-code | 500 |
8 changes: 4 additions & 4 deletions cypress/e2e/TrialDescriptionPage/PageNotFound.feature
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ Feature: Page Not Found
Scenario: User is able to see a page not found when trying to access an invalid page
Given screen breakpoint is set to "desktop"
Given the user navigates to "/v?id=NCI-0000-00000&loc=0&rl=1"
Then the page title is "Page Not Found"
Then page title on error page is "Page Not Found"
And the title tag should be "Page Not Found"

Scenario: User is able to see a page not found when trying to access an invalid page, then looks for multiple links in the page
Given screen breakpoint is set to "desktop"
Given the user navigates to "/v?id=NCI-0000-00000&loc=0&rl=1"
Then the page title is "Page Not Found"
Then page title on error page is "Page Not Found"
And the title tag should be "Page Not Found"
And "homepage" link has a href "https://www.cancer.gov"
And "cancer type" link has a href "https://www.cancer.gov/types"
Expand All @@ -19,7 +19,7 @@ Feature: Page Not Found
Scenario: Page not found displays when given an incorrect NCI ID
Given screen breakpoint is set to "desktop"
Given the user navigates to "/v?id=NCI-chicken-nugget&loc=0&rl=1"
Then the page title is "Page Not Found"
Then page title on error page is "Page Not Found"
And the title tag should be "Page Not Found"
And the text "We can't find the page you're looking for." appears on the page
And the following links and texts exist on the page
Expand All @@ -35,7 +35,7 @@ Feature: Page Not Found
Scenario: Page not found displays when user navigates to non-existent path
Given screen breakpoint is set to "desktop"
Given the user navigates to "/chicken"
Then the page title is "Page Not Found"
Then page title on error page is "Page Not Found"
And the title tag should be "Page Not Found"
And the text "We can't find the page you're looking for." appears on the page
And the following links and texts exist on the page
Expand Down
32 changes: 32 additions & 0 deletions cypress/e2e/common/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,33 @@
import { And, Given, Then, When } from 'cypress-cucumber-preprocessor/steps';
import { fieldMap } from '../../utils/ctsFields.js';

// https://docs.cypress.io/guides/guides/error-handling#Handling-uncaught-exceptions
// May have issues with async code. Explicitly handle as needed. Also, this hides errors.
cy.on('uncaught:exception', () => {
// returning false here prevents Cypress from
// failing the test
return false;
});

// Intercepts CTS API Calls
Cypress.Commands.add('triggerServerError', () => {
cy.intercept('/cts/mock-api/v2/*', {
statusCode: 500,
}).as('mockApiError');

cy.intercept('/cts/mock-api/v2/trials/NCI-2014-01507', {
statusCode: 500,
}).as('mockApiError');

cy.intercept('/cts/proxy-api/v2/*', {
statusCode: 500,
}).as('proxyApiError');

cy.intercept('https://clinicaltrialsapi.cancer.gov/api/v2/*', {
statusCode: 500,
}).as('ctsApiError');
});

Given('the user navigates to {string}', (destURL) => {
cy.visit(destURL);
});
Expand All @@ -26,6 +47,13 @@ Then('the page title is {string}', (title) => {
cy.get('h1').should('contain', title);
});

Then('page title on error page is {string}', (title) => {
Cypress.on('uncaught:exception', () => {
return false;
});
cy.get('h1').should('contain', title);
});

And('browser waits', () => {
cy.wait(6000);
});
Expand Down Expand Up @@ -203,3 +231,7 @@ Then(
.contains(ageText);
}
);

Given('the CTS API is responding with a server error', () => {
cy.triggerServerError();
});
44 changes: 32 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nciocpl/clinical-trials-search-app",
"version": "2.4.0",
"version": "2.5.0",
"author": "National Cancer Institute",
"license": "ISC",
"main": "build/static/js/main.js",
Expand Down Expand Up @@ -50,6 +50,7 @@
"react": "^16.14.0",
"react-app-polyfill": "^1.0.2",
"react-dom": "^16.14.0",
"react-error-boundary": "^4.0.13",
"react-helmet": "^5.2.1",
"react-redux": "^7.1.1",
"react-router": "^6.0.0-beta.7",
Expand Down
6 changes: 3 additions & 3 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import './styles/main.scss';
import { BasicSearchPage, AdvancedSearchPage } from './views/SearchPage';
import ResultsPage from './views/ResultsPage';
import TrialDescriptionPage from './views/TrialDescriptionPage';
import ErrorPage from './views/ErrorPage';
import PageNotFound from './views/PageNotFound/PageNotFound';
import InvalidCriteriaPage from './views/InvalidCriteriaPage';
import { PageNotFound } from './views/ErrorBoundary';
import { useAppSettings } from './store/store.js';
import { PrintContextProvider } from './store/printContext';
import { useAppInitializer } from './hooks';
Expand Down Expand Up @@ -53,7 +53,7 @@ const App = ({ zipConversionEndpoint }) => {
</Routes>
</PrintContextProvider>
) : (
<ErrorPage initErrorsList={initErrorsList} />
<InvalidCriteriaPage initErrorsList={initErrorsList} />
)}
</>
);
Expand Down
7 changes: 6 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ import App from './App';
import './index.css';
import clinicalTrialsSearchClientFactory from './services/api/clinical-trials-search-api';

import { ErrorBoundary } from 'react-error-boundary';
import { NCIErrorBoundary } from './views/ErrorBoundary';

const initialize = ({
appHasBeenVisited = false,
appHasBeenInitialized = false,
Expand Down Expand Up @@ -154,7 +157,9 @@ const initialize = ({
<Provider store={store}>
<AnalyticsHoC>
<Router>
<App zipConversionEndpoint={zipConversionEndpoint} />
<ErrorBoundary FallbackComponent={NCIErrorBoundary}>
<App zipConversionEndpoint={zipConversionEndpoint} />
</ErrorBoundary>
</Router>
</AnalyticsHoC>
</Provider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,22 @@
* @param {import("axios").AxiosInstance} client An axios instance with the correct baseURL
* @param {object} query the cts query
*/

export const getClinicalTrialDescription = async (client, query) => {
try {
const res = await client.get(`/trials/${query}`);
if (res.status === 200) {
return res.data;
} else {
// This condition will be hit for anything < 300.
if (res.status !== 200) {
// ie. Either a non-200 2xx status code or 3xx redirect
throw new Error(
`Unexpected status ${res.status} for fetching clinical trial description`
);
}
return res.data;
} catch (error) {
// This conditional will be hit for any status >= 300.
// 4xx or 5xx error
if (error.response) {
throw new Error(
`Unexpected status ${error.response.status} for fetching clinical trial description`
);
// We want to clarify the default message but propogate the extra data
error.message = `Unexpected status ${error.response.status} for fetching clinical trial description`;
}
throw error;
}
Expand Down
6 changes: 3 additions & 3 deletions src/styles/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,10 @@
'../components/search-modules/TrialType/TrialType';

// Views
@import
'../views/ErrorPage/ErrorPage',
@import '../views/InvalidCriteriaPage/InvalidCriteriaPage',
'../views/ResultsPage/ResultsPage',
'../views/SearchPage/SearchPage',
'../views/TrialDescriptionPage/TrialDescriptionPage',
'../views/PageNotFound/PageNotFound';
'../views/ErrorBoundary/PageNotFound/PageNotFound',
'../views/ErrorBoundary/GenericErrorPage/GenericErrorPage';

45 changes: 45 additions & 0 deletions src/views/ErrorBoundary/GenericErrorPage/GenericErrorPage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React, { useEffect } from 'react';
import { Helmet } from 'react-helmet';
import { useTracking } from 'react-tracking';

import { useAppSettings } from '../../../store/store';

const GenericErrorPage = () => {
const [{ canonicalHost }] = useAppSettings();
const tracking = useTracking();

useEffect(() => {
const pageTitle = 'Errors Occurred';
tracking.trackEvent({
event: 'ClinicalTrialsSearchApp:Load:Error',
metaTitle: pageTitle,
name: `${canonicalHost.replace(/^(http|https):\/\//g, '')}${
window.location.pathname
}`,
title: pageTitle,
type: 'PageLoad',
});
}, []);

const renderHelmet = () => {
return (
<Helmet>
<title>Errors Occurred</title>
<meta property="dcterms.subject" content="Error Page" />
<meta property="dcterms.type" content="errorpage" />
<meta name="prerender-status-code" content="500" />
</Helmet>
);
};

return (
<>
{renderHelmet()}
<div className="error-container">
<h1>An error occurred. Please try again later.</h1>
</div>
</>
);
};

export default GenericErrorPage;
10 changes: 10 additions & 0 deletions src/views/ErrorBoundary/GenericErrorPage/GenericErrorPage.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.error-container h1 {
height: 31px;
width: 573px;
color: #606060;
font-family: Montserrat;
font-size: 28px;
font-weight: bold;
letter-spacing: 0;
line-height: 30.8px;
}
46 changes: 46 additions & 0 deletions src/views/ErrorBoundary/NCIError.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Shamelessly inspired by https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#differentiate_between_similar_errors
export class NCIError extends Error {
constructor(message = '') {
super(message);

// Maintains proper stack trace for where our error was thrown
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
}

this.name = this.constructor.name;
this.date = new Date();
}
}
export class NotFoundError extends NCIError {
constructor(message = 'Resource not found') {
super(message);
}
}

export class InvalidCriteriaError extends NCIError {
constructor(errors = [], message = 'Invalid criteria') {
super(message);
this.errors = errors;
}
}

export class GenericError extends NCIError {
constructor(message = 'An error occurred') {
super(message);
}
}

export class ApiError extends NCIError {
constructor(message = 'API error', statusCode = 500) {
super(message);
this.statusCode = statusCode;
}
}

export class ApiServerError extends NCIError {
constructor(message = 'API server error', statusCode = 500) {
super(message);
this.statusCode = statusCode;
}
}
Loading

0 comments on commit 117c185

Please sign in to comment.