Gherkin cypress spike (#673)

* integrate gherkin/cucumber w/ cypress

- change cypress.json config from e2e back to standard integration
- add cypress-cucumber-preprocessor
- add integration test of about page

* add a title to each page

* add intl to the 404 page

* Refactor explore tool page

- add intl to static strings
- replace component css with <Grid> layout

* Add title to contact page

* add intl to title of page

* Add gherkin tests for nav to about page

- navigate from any page to the about page
- ensure each link has the title correct on that page
This commit is contained in:
Vim 2021-09-15 12:06:13 -07:00 committed by GitHub
parent 1c0d87d84b
commit 47df35b77e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 2782 additions and 110 deletions

View file

@ -1,6 +1,6 @@
{ {
"baseUrl": "http://localhost:8000/", "baseUrl": "http://localhost:8000/",
"integrationFolder": "cypress/e2e", "integrationFolder": "cypress/integration",
"chromeWebSecurity": false, "chromeWebSecurity": false,
"trashAssetsBeforeRuns": true "trashAssetsBeforeRuns": true
} }

View file

@ -1,3 +0,0 @@
export const ENDPOINTS = {
EXPLORE_THE_TOOL: '/en/cejst',
};

View file

@ -0,0 +1,17 @@
Feature: Does the About page open?
I want to open the about page
Scenario: About page from Explore Tool page
Given I am on the Explore Tool page
When I click on the "About" page in the navigation
Then I see "About" in the title
Scenario: About page from Methodology page
Given I am on the Explore Tool page
When I click on the "Methodology" page in the navigation
Then I see "Data and Methodology" in the title
Scenario: About page from Contact page
Given I am on the Explore Tool page
When I click on the "Contact" page in the navigation
Then I see "Contact" in the title

View file

@ -0,0 +1,32 @@
// / <reference types="Cypress" />
import {Given, When} from 'cypress-cucumber-preprocessor/steps';
import {ENDPOINTS} from '../LegacyTests/constants';
// eslint-disable-next-line new-cap
Given('I am on the About page', () => {
cy.viewport(1060, 800);
cy.visit(ENDPOINTS.ABOUT);
});
// eslint-disable-next-line new-cap
Given('I am on the Explore Tool page', () => {
cy.viewport(1060, 800);
cy.visit(ENDPOINTS.EXPLORE_THE_TOOL);
});
// eslint-disable-next-line new-cap
Given('I am on the Data & Methodology page', () => {
cy.viewport(1060, 800);
cy.visit(ENDPOINTS.METHODOLOGY);
});
// eslint-disable-next-line new-cap
Given('I open the Contact page', () => {
cy.viewport(1060, 800);
cy.visit(ENDPOINTS.CONTACT);
});
// eslint-disable-next-line new-cap
When(`I click on the {string} page in the navigation`, (page) => {
cy.get(`[data-cy="nav-link-${page.toLowerCase()}"]`).click();
});

View file

@ -0,0 +1,6 @@
export const ENDPOINTS = {
ABOUT: 'en',
EXPLORE_THE_TOOL: '/en/cejst',
METHODOLOGY: '/en/methodology',
CONTACT: 'en/contact',
};

View file

@ -1,12 +1,12 @@
// / <reference types="Cypress" /> // / <reference types="Cypress" />
describe('Census Block Group download', () => { describe('Census Block Group packet download', () => {
const filename = `Screening_Tool_Data.zip`;
it('validate file download', () => { it('validate file download', () => {
const filename = `Screening+Tool+Data.zip`;
cy.visit('localhost:8000/en/methodology'); cy.visit('localhost:8000/en/methodology');
cy.get('[data-cy="download-link"]').invoke('attr', 'target', '_blank'); cy.get('[data-cy="download-link"]').invoke('attr', 'target', '_blank');
cy.intercept(`https://justice40-data.s3.amazonaws.com/data-pipeline/data/score/downloadable/${filename}`, cy.intercept(`https://d3jqyw10j8e7p9.cloudfront.net/data-pipeline/data/score/downloadable/${filename}`,
{ {
body: 'success', body: 'success',
headers: { headers: {

View file

@ -0,0 +1,4 @@
// eslint-disable-next-line new-cap
Then(`I see {string} in the title`, (title) => {
cy.title().should('include', title);
});

View file

@ -12,6 +12,8 @@
// This function is called when a project is opened or re-opened (e.g. due to // This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing) // the project's config changing)
const cucumber = require('cypress-cucumber-preprocessor').default;
/** /**
* @type {Cypress.PluginConfig} * @type {Cypress.PluginConfig}
*/ */
@ -31,4 +33,5 @@ module.exports = (on, config) => {
return null; return null;
}, },
}); });
on('file:preprocessor', cucumber());
}; };

2555
client/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -48,6 +48,7 @@
"babel-preset-gatsby": "^1.6.0", "babel-preset-gatsby": "^1.6.0",
"cypress": "^7.4.0", "cypress": "^7.4.0",
"cypress-axe": "^0.12.2", "cypress-axe": "^0.12.2",
"cypress-cucumber-preprocessor": "^4.2.0",
"eslint": "^7.27.0", "eslint": "^7.27.0",
"eslint-config-google": "^0.14.0", "eslint-config-google": "^0.14.0",
"eslint-plugin-cypress": "^2.11.3", "eslint-plugin-cypress": "^2.11.3",
@ -85,5 +86,8 @@
"react-intl": "^5.20.4", "react-intl": "^5.20.4",
"react-map-gl": "^6.1.16", "react-map-gl": "^6.1.16",
"uswds": "^2.10.3" "uswds": "^2.10.3"
},
"cypress-cucumber-preprocessor": {
"nonGlobalStepDefinitions": true
} }
} }

View file

@ -60,25 +60,45 @@ const J40Header = () => {
to={'/'} to={'/'}
key={'about'} key={'about'}
activeClassName="usa-current" activeClassName="usa-current"
className={'j40-header'}>{intl.formatMessage(messages.about)}</Link>], className={'j40-header'}
data-cy={'nav-link-about'}
>
{intl.formatMessage(messages.about)}
</Link>,
],
['cejst', ['cejst',
<Link <Link
to={'/cejst'} to={'/cejst'}
key={'cejst'} key={'cejst'}
activeClassName="usa-current" activeClassName="usa-current"
className={'j40-header'}>{intl.formatMessage(messages.explore)}</Link>], className={'j40-header'}
data-cy={'nav-link-explore'}
>
{intl.formatMessage(messages.explore)}
</Link>,
],
['methodology', ['methodology',
<Link <Link
to={'/methodology'} to={'/methodology'}
key={'methodology'} key={'methodology'}
activeClassName="usa-current" activeClassName="usa-current"
className={'j40-header'}>{intl.formatMessage(messages.methodology)}</Link>], className={'j40-header'}
data-cy={'nav-link-methodology'}
>
{intl.formatMessage(messages.methodology)}
</Link>,
],
['contact', ['contact',
<Link <Link
to={'/contact'} to={'/contact'}
key={'contact'} key={'contact'}
activeClassName="usa-current" activeClassName="usa-current"
className={'j40-header'}>{intl.formatMessage(messages.contact)}</Link>], className={'j40-header'}
data-cy={'nav-link-contact'}
>
{intl.formatMessage(messages.contact)}
</Link>,
],
]); ]);
const menu =['about', 'cejst', 'methodology', 'contact']; const menu =['about', 'cejst', 'methodology', 'contact'];

View file

@ -2,22 +2,27 @@ import React, {ReactNode} from 'react';
import J40Header from './J40Header'; import J40Header from './J40Header';
import J40Footer from './J40Footer'; import J40Footer from './J40Footer';
import {URLFlagProvider} from '../contexts/FlagContext'; import {URLFlagProvider} from '../contexts/FlagContext';
import {Helmet} from 'react-helmet';
interface ILayoutProps { interface ILayoutProps {
children: ReactNode, children: ReactNode,
location: Location location: Location,
title: string,
} }
const Layout = ({children, location}: ILayoutProps) => { const Layout = ({children, location, title}: ILayoutProps) => {
// @ts-ignore // @ts-ignore
return ( return (
<URLFlagProvider location={location}> <>
<J40Header /> <Helmet title={title} defer={false} />
<main id={'main-content'}> <URLFlagProvider location={location}>
{children} <J40Header />
</main> <main id={'main-content'}>
<J40Footer/> {children}
</URLFlagProvider> </main>
<J40Footer/>
</URLFlagProvider>
</>
); );
}; };

View file

@ -1,19 +1,11 @@
import * as React from 'react'; import * as React from 'react';
import Layout from '../components/layout'; import {defineMessages} from 'react-intl';
import J40MainGridContainer from '../components/J40MainGridContainer';
import {Link} from 'gatsby-plugin-intl'; import {Link} from 'gatsby-plugin-intl';
import {useIntl} from 'gatsby-plugin-intl';
import {Grid} from '@trussworks/react-uswds'; import {Grid} from '@trussworks/react-uswds';
// styles import J40MainGridContainer from '../components/J40MainGridContainer';
const headingStyles = { import Layout from '../components/layout';
marginTop: 32,
marginBottom: 64,
maxWidth: 320,
};
const paragraphStyles = {
marginBottom: 48,
};
const codeStyles = { const codeStyles = {
color: '#8A6534', color: '#8A6534',
@ -29,33 +21,76 @@ interface I404PageProps {
// markup // markup
const NotFoundPage =({location}: I404PageProps) => { const NotFoundPage =({location}: I404PageProps) => {
return (<Layout location={location}> const intl = useIntl();
<J40MainGridContainer> const messages = defineMessages({
<Grid row><Grid col> pageNotFoundTitle: {
<h1 style={headingStyles}>Page not found</h1> id: 'pageNotFound.title.text',
</Grid></Grid> defaultMessage: 'Page not found',
<Grid row><Grid col> description: 'page not found title text',
<p style={paragraphStyles}> },
Sorry{' '} pageNotFoundHeading: {
<span role="img" aria-label="Pensive emoji"> id: 'pageNotFound.heading.text',
defaultMessage: 'Page not found',
description: 'page not found heading text',
},
pageNotFoundApology: {
id: 'pageNotFound.apology.text',
defaultMessage: 'Sorry',
description: 'page not found apology text',
},
pageNotFoundApologyDescription: {
id: 'pageNotFound.apology.description.text',
defaultMessage: 'we couldnt find what you were looking for.',
description: 'page not found apology description text',
},
pageNotFoundGuidance: {
id: 'pageNotFound.Guidance.text',
defaultMessage: 'Try creating a page in',
description: 'page not found guidance text',
},
pageNotFoundLinkToGoHome: {
id: 'pageNotFound.link.to.go.home.text',
defaultMessage: 'Go home',
description: 'page not found link to go home text',
},
});
return (
<Layout location={location} title={intl.formatMessage(messages.pageNotFoundTitle)}>
<J40MainGridContainer>
<Grid row>
<h1>{intl.formatMessage(messages.pageNotFoundHeading)}</h1>
</Grid>
<Grid row>
<p>
{intl.formatMessage(messages.pageNotFoundApology)}
{' '}
<span role="img" aria-label="Pensive emoji">
😔 😔
</span>{' '} </span>{' '}
we couldnt find what you were looking for. {intl.formatMessage(messages.pageNotFoundApologyDescription)}
<br/> </p>
</Grid>
<Grid row>
{process.env.NODE_ENV === 'development' ? ( {process.env.NODE_ENV === 'development' ? (
<> <p>
<br/> {intl.formatMessage(messages.pageNotFoundGuidance)}
Try creating a page <code style={codeStyles}>src/pages/</code>.
in <code style={codeStyles}>src/pages/</code>. </p>
<br/>
</>
) : null} ) : null}
<br/> </Grid>
<Link to="/">Go home</Link>.
</p> <Grid>
</Grid></Grid> <p>
</J40MainGridContainer> <Link to="/">{intl.formatMessage(messages.pageNotFoundLinkToGoHome)}</Link>.
</Layout> </p>
</Grid>
</J40MainGridContainer>
</Layout>
); );
}; };

View file

@ -1,13 +0,0 @@
@import "../components/utils.scss";
.explorePageSubHeader {
display: flex;
flex-wrap: wrap;
line-height: 25px;
color: $headingFontColor;
.explorePageHeaderText {
max-width: 34rem;
font-size: large;
}
}

View file

@ -1,13 +0,0 @@
declare namespace CejstModuleScssNamespace {
export interface ICejstModuleScss {
explorePageHeader;
explorePageHeaderText: string;
}
}
declare const CejstModuleScssModule: CejstModuleScssNamespace.ICejstModuleScss & {
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
locals: CejstModuleScssNamespace.ICejstModuleScss;
};
export = CejstModuleScssModule;

View file

@ -1,5 +1,8 @@
import React from 'react'; import React from 'react';
import {defineMessages} from 'react-intl';
import {Link} from 'gatsby-plugin-intl'; import {Link} from 'gatsby-plugin-intl';
import {useIntl} from 'gatsby-plugin-intl';
import {Grid} from '@trussworks/react-uswds';
import AlertWrapper from '../components/AlertWrapper'; import AlertWrapper from '../components/AlertWrapper';
import HowYouCanHelp from '../components/HowYouCanHelp'; import HowYouCanHelp from '../components/HowYouCanHelp';
@ -8,10 +11,6 @@ import Layout from '../components/layout';
import MapWrapper from '../components/MapWrapper'; import MapWrapper from '../components/MapWrapper';
import MapLegend from '../components/MapLegend'; import MapLegend from '../components/MapLegend';
import * as styles from './cejst.module.scss';
import {Grid} from '@trussworks/react-uswds';
interface IMapPageProps { interface IMapPageProps {
location: Location; location: Location;
} }
@ -19,33 +18,52 @@ interface IMapPageProps {
const CEJSTPage = ({location}: IMapPageProps) => { const CEJSTPage = ({location}: IMapPageProps) => {
// We temporarily removed MapControls, which would enable you to `setFeatures` also, for now // We temporarily removed MapControls, which would enable you to `setFeatures` also, for now
// We will bring back later when we have interactive controls. // We will bring back later when we have interactive controls.
return (<Layout location={location}>
const intl = useIntl();
const messages = defineMessages({
exploreToolTitleText: {
id: 'exploreTool.title.text',
defaultMessage: 'Explore the tool',
description: 'explore the tool title text',
},
exploreToolHeadingText: {
id: 'exploreTool.heading.text',
defaultMessage: 'Explore the tool',
description: 'explore the tool heading text',
},
exploreToolPageText: {
id: 'exploreTool.page.text',
defaultMessage: 'Zoom into the map to see which communities the tool has currently'+
'identified as prioritized (the top 25% of communities) or on the'+
'threshold. Learn more about the formula and datasets that were'+
'used to prioritize these communities on the',
description: 'explore the tool page text',
},
});
return (<Layout location={location} title={intl.formatMessage(messages.exploreToolTitleText)}>
<J40MainGridContainer> <J40MainGridContainer>
<AlertWrapper showBetaAlert={true} showLimitedDataAlert={false}/> <AlertWrapper showBetaAlert={true} showLimitedDataAlert={false}/>
</J40MainGridContainer> </J40MainGridContainer>
<J40MainGridContainer> <J40MainGridContainer>
<h1>{intl.formatMessage(messages.exploreToolTitleText)}</h1>
<Grid row className={'j40-mb-5'}> <Grid row className={'j40-mb-5'}>
<Grid col> <Grid col={12} tablet={{col: 6}}>
<section> <section>
<h1 className={styles.explorePageHeader}>Explore the tool</h1> <p>
<div className={styles.explorePageSubHeader}> {intl.formatMessage(messages.exploreToolPageText)}
<div className={styles.explorePageHeaderText}> {` `}
<p> <Link to={'/methodology'}>Data & methodology</Link>
Zoom into the map to see which communities the tool has currently {` `}
identified as prioritized (the top 25% of communities) or on the page.
threshold. Learn more about the formula and datasets that were </p>
used to prioritize these communities on the
{` `}
<Link to={'/methodology'}>Data & methodology</Link>
{` `}
page.
</p>
</div>
<MapLegend />
</div>
</section> </section>
</Grid> </Grid>
<Grid col={12} tablet={{col: 6}}>
<MapLegend />
</Grid>
</Grid> </Grid>
<Grid row> <Grid row>

View file

@ -14,7 +14,7 @@ const ContactPage = ({location}: ContactPageProps) => {
const generalEmail = 'screeningtool.feedback@usds.gov'; const generalEmail = 'screeningtool.feedback@usds.gov';
return ( return (
<Layout location={location}> <Layout location={location} title={'Contact'}>
<J40MainGridContainer> <J40MainGridContainer>
<AlertWrapper showBetaAlert={true} showLimitedDataAlert={false}/> <AlertWrapper showBetaAlert={true} showLimitedDataAlert={false}/>

View file

@ -74,7 +74,7 @@ const IndexPage = ({location}: IndexPageProps) => {
}); });
return ( return (
<Layout location={location}> <Layout location={location} title={'About'}>
<J40MainGridContainer> <J40MainGridContainer>
<AlertWrapper showBetaAlert={true} showLimitedDataAlert={false}/> <AlertWrapper showBetaAlert={true} showLimitedDataAlert={false}/>
</J40MainGridContainer> </J40MainGridContainer>

View file

@ -18,6 +18,11 @@ interface MethodPageProps {
const IndexPage = ({location}: MethodPageProps) => { const IndexPage = ({location}: MethodPageProps) => {
const intl = useIntl(); const intl = useIntl();
const messages = defineMessages({ const messages = defineMessages({
methodologyPageTitle: {
id: 'methodology.page.title.text',
defaultMessage: 'Data and Methodology',
description: 'methodology page title text',
},
methodologyPageHeader: { methodologyPageHeader: {
id: 'methodology.page.header.text', id: 'methodology.page.header.text',
defaultMessage: 'Methodology', defaultMessage: 'Methodology',
@ -32,7 +37,7 @@ const IndexPage = ({location}: MethodPageProps) => {
}); });
return ( return (
<Layout location={location}> <Layout location={location} title={intl.formatMessage(messages.methodologyPageTitle)}>
<J40MainGridContainer> <J40MainGridContainer>
<AlertWrapper showBetaAlert={true} showLimitedDataAlert={false}/> <AlertWrapper showBetaAlert={true} showLimitedDataAlert={false}/>

View file

@ -1,7 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import Layout from '../components/layout'; import Layout from '../components/layout';
// THIS COMPONENT IS NOT BEING USED // THIS COMPONENT IS NOT BEING USED
// @ts-ignore // @ts-ignore
@ -12,7 +11,7 @@ interface TimelinePageProps {
} }
const TimelinePage = ({location}: TimelinePageProps) => { const TimelinePage = ({location}: TimelinePageProps) => {
return (<Layout location={location}> return (<Layout location={location} title={'Timeline'}>
<section> <section>
{/* <h1>Timelin</h1> {/* <h1>Timelin</h1>
<h2>Throughout the Process</h2> <h2>Throughout the Process</h2>