Skip to content

Commit

Permalink
Update exercise 3: cypress/end-to-end
Browse files Browse the repository at this point in the history
  • Loading branch information
marcysutton committed May 12, 2022
1 parent 94b51b8 commit 1ab5522
Show file tree
Hide file tree
Showing 12 changed files with 253 additions and 21 deletions.
5 changes: 3 additions & 2 deletions App.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useState } from "react"
import AppContext from './components/AppContext'
import "wicg-inert"
import AppContext from "./components/AppContext"
import { Router } from "@reach/router"
import "./global-styles/variables.scss"
import "./global-styles/styles.scss"
Expand Down Expand Up @@ -41,7 +42,7 @@ export function App() {
<TripIdeasPage path="/trip-ideas" />
</Router>
</div>
<div id="footer">
<div id="footer" inert={inertMarkupValue ? 'inert' : null}>
<div className="layout">
<div id="footer-logo">
<img src={imgFooterLogo} />
Expand Down
8 changes: 2 additions & 6 deletions components/page-passes.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,6 @@ const PassesPage = () => {

appContext.setInertMarkupValue(false)
}
const handleDialogEscape = (event) => {
if (event.key === 'Escape') {
setDialogState(false)
}
}
const openDialog = (dialogName, btnRef) => {
setCurrentDialog(dialogName)
setDialogState(true)
Expand All @@ -54,15 +49,16 @@ const PassesPage = () => {
<div
aria-label="Subscription details"
className={`payment-modal ${currentDialog}`}
data-testid="payment-dialog"
role="dialog"
hidden={dialogActive ? null : 'hidden'}
onKeyUp={(event)=> handleDialogEscape(event)}
ref={dialogRef}
tabIndex="-1"
>
<header>
<button
className="btn-close-dialog"
data-testid="btn-close-dialog"
onClick={handleDialogClose}
ref={closeBtnRef}
>
Expand Down
5 changes: 5 additions & 0 deletions exercise3-cypress-integration/HomePage.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
describe('HomePage', () => {
beforeEach(() => {
cy.visit('http://localhost:1234/')
})
})
11 changes: 11 additions & 0 deletions exercise3-cypress-integration/PassesPage.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
describe('Passes Page', () => {
beforeEach(() => {
cy.visit('http://localhost:1234/passes')

cy.injectAxe()
})
it('should have no accessibility violations on load', () => {
cy.checkA11y()
})

})
18 changes: 13 additions & 5 deletions exercise3-cypress-integration/answer/App.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import React, {useRef} from "react"
import "wicg-inert"
import AppContext from "./components/AppContext"
import { Router } from "@reach/router"
import "./global-styles/variables.scss"
import "./global-styles/styles.scss"
Expand All @@ -17,9 +19,15 @@ import HikesPage from "components/page-adventures-hikes"
import imgFooterLogo from "images/icons/footer-logo.svg"

export function App() {
return <>
<Header />
<main id="main">
const [inertMarkupValue, setInertMarkupValue] = useState(false)

const context = {
inert: inertMarkupValue,
setInertMarkupValue
}
return <AppContext.Provider value={context}>
<Header inert={inertMarkupValue ? 'inert' : null} />
<main id="main" inert={inertMarkupValue ? 'inert' : null}>
<Router>
<HomePage path="/" />
<AboutPage path="/about" />
Expand All @@ -33,12 +41,12 @@ export function App() {
<TripIdeasPage path="/trip-ideas" />
</Router>
</main>
<footer id="footer">
<footer id="footer" inert={inertMarkupValue ? 'inert' : null}>
<div className="layout">
<div id="footer-logo">
<img src={imgFooterLogo} alt="CampSpots" />
</div>
</div>
</footer>
</>
</AppContext.Provider>
}
6 changes: 3 additions & 3 deletions exercise3-cypress-integration/answer/HomePage.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ describe('HomePage', () => {

cy.injectAxe()
})
it('should have an aria-current home link', () => {
cy.get('#header-logo a').should('have.attr', 'aria-current', 'page')
})
xit('should have no accessibility violations on load', () => {
cy.checkA11y()
})
it('should have a Header', () => {
cy.get('#header').contains('CampSpots')
})
})
22 changes: 22 additions & 0 deletions exercise3-cypress-integration/answer/PassesPage.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
describe('Passes Page', () => {
beforeEach(() => {
cy.visit('http://localhost:1234/passes')

cy.injectAxe()
})
xit('should have no accessibility violations on load', () => {
cy.checkA11y()
})
it('should have an accessible payment dialog', () => {
cy.get('#btn-join-basic').focus().click()

// focus is sent into dialog
cy.focused().should('have.attr', 'role', 'dialog')

cy.realPress(["Tab"])
cy.focused().should('have.attr', 'aria-label', 'Close Dialog')

cy.realPress(["Escape"])
cy.focused().should('have.attr', 'id', 'btn-join-basic')
})
})
12 changes: 8 additions & 4 deletions exercise3-cypress-integration/answer/header.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import React from "react"
import React, {useEffect, useState} from "react"
import "./styles/header.scss"

import CampSpotsLogo from "images/icons/camp-spots-logo.svg"
import MegaNav from "./meganav/"

const Header = () => {
const Header = ({inert}) => {
const [currentPathname, setCurrentPathname] = useState(null)
useEffect(() => {
setCurrentPathname(document.location.pathname)
},[])
return (
<header id="header">
<header id="header" inert={inert}>
<div id="header-nav">
<div id="header-logo">
<h1>
<a href="/" className="header-main-item" data-testid="logo-link">
<a aria-current={currentPathname === '/' ? 'page' : null} href="/" className="header-main-item" data-testid="logo-link">
<span className="logo-img">
<img src={CampSpotsLogo} alt="" />
</span>
Expand Down
175 changes: 175 additions & 0 deletions exercise3-cypress-integration/answer/page-passes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import React, { useContext, useEffect, useRef, useState } from "react"
import BodyClassName from "react-body-classname"
import {Helmet} from "react-helmet"

import AppContext from './AppContext'
import HeaderPortal from "components/header-portal"
import "components/styles/page-passes.scss"
import PaymentForm from "components/payment-form/payment-form"
import iconProPlan from "images/icons/icon-proplan.svg"

const PassesPage = () => {
const appContext = useContext(AppContext)

const dialogRef = useRef(null)
const closeBtnRef = useRef(null)
const basicDialogTriggerBtnRef = useRef(null)
const proDialogTriggerBtnRef = useRef(null)

const [dialogActive, setDialogState] = useState(false)
const [currentDialog, setCurrentDialog] = useState('')
const [activeTriggerBtn, setActiveTriggerBtn] = useState(null)

const handleDialogClose = () => {
setDialogState(false)

appContext.setInertMarkupValue(false)
}
const handleDialogEscape = (event) => {
if (event.key === 'Escape') {
setDialogState(false)
appContext.setInertMarkupValue(false)
}
}
const openDialog = (dialogName, btnRef) => {
setCurrentDialog(dialogName)
setDialogState(true)

appContext.setInertMarkupValue(true)

setActiveTriggerBtn(btnRef)
}
useEffect(() => {
if (dialogActive) {
dialogRef.current.focus()
} else {
// send focus back to triggering button
if (activeTriggerBtn && activeTriggerBtn.current) {
activeTriggerBtn.current.focus()
}
}
},[dialogActive, activeTriggerBtn])

const Dialog = () => (
<>
<div
aria-label="Subscription details"
className={`payment-modal ${currentDialog}`}
data-testid="payment-dialog"
role="dialog"
hidden={dialogActive ? null : 'hidden'}
onKeyUp={(event)=> handleDialogEscape(event)}
ref={dialogRef}
tabIndex="-1"
>
<header>
<button
aria-label="Close dialog"
className="btn-close-dialog"
data-testid="btn-close-dialog"
onClick={handleDialogClose}
ref={closeBtnRef}
>
X
</button>
<img src={iconProPlan} alt="" />
<div id="header-basic">
<h2>CampSpots<span>Basic</span></h2>
<h3>$99/year</h3>
</div>
<div id="header-pro">
<h2>CampSpots<span>Pro</span></h2>
<h3>$139/year</h3>
</div>
</header>
<section className="payment-form">
<PaymentForm />
</section>
</div>
<div className="modal-curtain" hidden={dialogActive ? null : 'hidden'}></div>
</>
)
return (
<BodyClassName className="page-plans">
<>
<HeaderPortal>
<h1 className="visually-hidden">CampSpots</h1>
<Dialog />
</HeaderPortal>
<section className="layout-centered">
<header className="page-header">
<div className="page-header-content layout">
<h2 className="primary-heading h1-style">Pick a Plan and Start Your Adventure Today!</h2>
</div>
</header>
<article className="layout">
<ul className="plan-list">
<li className="plan-basic">
<div>
<h3>CampSpots <span>Basic</span></h3>
<h4>$99<span>/year</span></h4>
<p className="subhead">Renewed annually</p>

<ul>
<li>
<span className="icon-checkmark-gray"></span>
<span className="text">Unlimited overnight stays with No Camping Fees</span>
</li>
<li>
<span className="icon-checkmark-gray"></span>
<span className="text">100% money back guarantee</span>
</li>
<li>
<span className="icon-x"></span>
<span className="text"><del>Access to spa facilities &amp; restaurants</del></span>
</li>
</ul>
<button
id="btn-join-basic"
onClick={() => { openDialog('basic', basicDialogTriggerBtnRef) }}
ref={basicDialogTriggerBtnRef}
>
Join Now
</button>
<p>Vitae maximus turpis tempor <strong>nec</strong>.</p>
</div>
</li>
<li className="plan-pro">
<div>
<span className="icon-proplan"></span>
<h3>CampSpots <span>Pro</span></h3>
<h4>$139<span>/year</span></h4>
<p className="subhead">Renewed annually</p>

<ul>
<li>
<span className="icon-checkmark-orange"></span>
<span className="text">Unlimited overnight stays with No Camping Fees</span></li>
<li>
<span className="icon-checkmark-orange"></span>
<span className="text">100% money back guarantee</span>
</li>
<li>
<span className="icon-checkmark-orange"></span>
<span className="text">Access to spa facilities &amp; restaurants</span>
</li>
</ul>
<button
id="btn-join-pro"
onClick={() => { openDialog('pro', proDialogTriggerBtnRef) }}
ref={proDialogTriggerBtnRef}
>
Join Now
</button>
<p>Lorem ipsum dolor <strong>sit</strong> amet, consectetur adipiscing elit.</p>
</div>
</li>
</ul>
</article>
</section>
</>
</BodyClassName>
)
}

export default PassesPage
4 changes: 4 additions & 0 deletions global-styles/styles.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
* {
box-sizing: border-box;
}
#portal-root {
position: relative;
z-index: 2;
}
.visually-hidden {
clip: rect(0 0 0 0);
clip-path: inset(50%);
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@
"react-dom": "^17.0.2",
"react-helmet": "^6.1.0",
"sanitize-html": "^2.7.0",
"sass-loader": "^10"
"sass-loader": "^10",
"wicg-inert": "^3.1.1"
},
"devDependencies": {
"@babel/core": "^7.16.0",
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14287,6 +14287,11 @@ which@^2.0.1:
dependencies:
isexe "^2.0.0"

wicg-inert@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/wicg-inert/-/wicg-inert-3.1.1.tgz#b033fd4fbfb9e3fd709e5d84becbdf2e06e5c229"
integrity sha512-PhBaNh8ur9Xm4Ggy4umelwNIP6pPP1bv3EaWaKqfb/QNme2rdLjm7wIInvV4WhxVHhzA4Spgw9qNSqWtB/ca2A==

wide-align@^1.1.2:
version "1.1.5"
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3"
Expand Down

0 comments on commit 1ab5522

Please sign in to comment.