-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: frontend expiring certification warnings and sign-in prevention #205
base: main
Are you sure you want to change the base?
Conversation
f2d8c5e
to
614450e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was some talk about whether we should warn on someone not having any certifications in the database. I don't remember the resolution of that discussion. Should anything like that be incorporated into this feature?
return list.map(certificationFromData); | ||
}; | ||
|
||
export const useCertifications = (badge: string | null) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: Can you add a return type to this? Sometimes its obvious, but in this case it isn't (you have to go through the type of useApiResult
and parse
to figure it out), and this is a pretty important abstraction boundary so should be clearly typed.
|
||
export type Certification = { | ||
type: string; | ||
rail_line: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: Should this be railLine
? The json data below definitely shouldn't be, but this one is the type we'd use within the react app.
</form> | ||
</> | ||
: <> | ||
<Instructions displayName={name} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
naming: Instructions
is a bit ambiguous, when I first saw this I thought it meant instructions for filling out the form normally, but this is specifically instructions for dealing with an expired cert.
test("displays expired mode if expired", () => { | ||
const view = render( | ||
<Attestation | ||
badge="123" | ||
prefill={false} | ||
onComplete={jest.fn()} | ||
loading={false} | ||
employees={EMPLOYEES} | ||
certifications={CERTIFICATIONS_ONE_EXPIRED} | ||
/>, | ||
); | ||
expect(view.getByText("Expired card")).toBeInTheDocument(); | ||
expect( | ||
view.getByText("Continue to Fit for Duty Check."), | ||
).toBeInTheDocument(); | ||
}); | ||
|
||
test("can bypass expired mode", async () => { | ||
const view = render( | ||
<Attestation | ||
badge="123" | ||
prefill={false} | ||
onComplete={jest.fn()} | ||
loading={false} | ||
employees={EMPLOYEES} | ||
certifications={CERTIFICATIONS_ONE_EXPIRED} | ||
/>, | ||
); | ||
await userEvent.click( | ||
view.getByRole("button", { name: "Continue to Fit for Duty Check →" }), | ||
); | ||
expect( | ||
view.getByRole("button", { name: "Complete Fit for Duty Check" }), | ||
).toBeInTheDocument(); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these tests are redundant. You don't need to assert the existence of the button and then assert that you can click it, just one test that asserts clicking the button would work.
I suggest deleting the first test, and copying the one unique assertion from that test (getByText("ExpiredCard")
) into the second.
test("displays expired mode if expired", () => { | |
const view = render( | |
<Attestation | |
badge="123" | |
prefill={false} | |
onComplete={jest.fn()} | |
loading={false} | |
employees={EMPLOYEES} | |
certifications={CERTIFICATIONS_ONE_EXPIRED} | |
/>, | |
); | |
expect(view.getByText("Expired card")).toBeInTheDocument(); | |
expect( | |
view.getByText("Continue to Fit for Duty Check."), | |
).toBeInTheDocument(); | |
}); | |
test("can bypass expired mode", async () => { | |
const view = render( | |
<Attestation | |
badge="123" | |
prefill={false} | |
onComplete={jest.fn()} | |
loading={false} | |
employees={EMPLOYEES} | |
certifications={CERTIFICATIONS_ONE_EXPIRED} | |
/>, | |
); | |
await userEvent.click( | |
view.getByRole("button", { name: "Continue to Fit for Duty Check →" }), | |
); | |
expect( | |
view.getByRole("button", { name: "Complete Fit for Duty Check" }), | |
).toBeInTheDocument(); | |
}); | |
}); | |
test("can bypass expired mode", async () => { | |
const view = render( | |
<Attestation | |
badge="123" | |
prefill={false} | |
onComplete={jest.fn()} | |
loading={false} | |
employees={EMPLOYEES} | |
certifications={CERTIFICATIONS_ONE_EXPIRED} | |
/>, | |
); | |
expect(view.getByText("Expired card")).toBeInTheDocument(); | |
await userEvent.click( | |
view.getByRole("button", { name: "Continue to Fit for Duty Check →" }), | |
); | |
expect( | |
view.getByRole("button", { name: "Complete Fit for Duty Check" }), | |
).toBeInTheDocument(); | |
}); | |
}); |
@@ -216,4 +227,42 @@ describe("Attestation", () => { | |||
expect(onComplete).not.toHaveBeenCalled(); | |||
jest.useRealTimers(); | |||
}); | |||
|
|||
describe("expiry", () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
attestation.test.tsx
now has a test for the expired case. But does it need a test that it shows the about to expire warning? I see that the details of that situation are asserted in expiry.test.tsx
, but I'm thinking about a test that proves that CertificationBoxes
is called.
return ( | ||
<WarningParagraph | ||
className={className([ | ||
"border-0 light:bg-opacity-40 dark:bg-opacity-30 dark:text-white", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
refactoring suggestion: This is the only use of WarningParagraph
, and these styles don't need to be controlled dynamically, so should they be part of WarningParagraph
instead of passed in as an argument?
And should the background color be controlled by passing in "warning"
or "error"
instead of by giving an extraClassName?
Or, alternatively, WarningParagraph is just 2 divs and some styles, and it's not used anywhere else, so maybe it should be inlined here instead of abstracted?
<CertificateBox | ||
now={now} | ||
mode="error" | ||
title={expired.length === 2 ? "Expired cards" : "Expired card"} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: This is fragile if anybody ever has more than 2 certifications (could happen if we track more than two types, or if someone has certs on more than one rail line). It should be expired.length === 1 ? singular : plural
or expired.length > 1 ? plural : singular
.
Also applies to the previous call 10 lines above this.
<ol className="mb-4 ml-10 mr-0 mt-2 list-decimal"> | ||
<li className="m-0"> | ||
Take a picture of{" "} | ||
{expireds.length === 2 ? "both cards" : "the card"}. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if length > 2?
now: DateTime; | ||
onContinue: () => void; | ||
}): ReactElement => { | ||
const expireds = certifications.filter((c) => isExpired(c, now)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this would actually cause problems, but it seems like we shouldn't have to recalculate which cards are expired at this point. The calculations are all in sync now but what if something changes? Perhaps the caller should calculate expireds
once, and use the same value both for deciding whether to show Bypass
and for passing in as an argument:
expireds = certifications.filter((c) => isExpired(c, now));
...
(expireds.length > 0) ? <Bypass expireds={expireds} displayName={...} onContinue={...} />
@@ -161,7 +176,6 @@ describe("OperatorSignInModal", () => { | |||
await userEvent.type(view.getByRole("textbox"), "123"); | |||
await userEvent.click(view.getByRole("button", { name: "OK" })); | |||
|
|||
expect(view.getByText("Step 2 of 2")).toBeInTheDocument(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are these assertions removed?
Asana Task: 📐 Implement Warnings for Expiring Certifications
Checklist
(x)
Has tests( )
Doesn't need tests( )
Tests deferred (with justification)(x)
Okayed the plan for the feature (e.g. the design files, or the Asana task)( )
Reviewed the feature as implemented (e.g. on dev-green, or saw screenshots)( )
No review needed