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
MLR E2E Updates #11389
MLR E2E Updates #11389
Changes from 27 commits
ccdefe4
38716a6
96539ff
e3a8fac
0dcf00f
20e584f
33982f9
cb8f218
56c0473
911a858
ff7a030
c2a60dc
18d3b57
067d253
0273f76
564dd54
5121172
f960d1d
0fb9f7e
9dcfa0e
af7c702
7bff752
66803b9
bcf6a0d
9a32ebe
c777343
385246d
7dd49ca
6c80116
a4e7175
8d588c3
82b20d5
0ca73e1
ef347c2
87867ef
46995ad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,12 +22,9 @@ jobs: | |
run: ./.github/build-vars.sh set_values | ||
env: | ||
CODE_CLIMATE_ID: ${{ secrets.CODE_CLIMATE_ID }} | ||
- name: read .nvmrc | ||
id: node_version | ||
run: echo ::set-output name=NODE_VERSION::$(cat .nvmrc) | ||
- uses: actions/setup-node@v1 | ||
- uses: actions/setup-node@v3 | ||
with: | ||
node-version: ${{ steps.node_version.outputs.NODE_VERSION }} | ||
node-version-file: ".nvmrc" | ||
- uses: actions/cache@v2 | ||
with: | ||
path: | | ||
|
@@ -89,12 +86,9 @@ jobs: | |
run: ./.github/github-lock.sh $branch_name | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
- name: read .nvmrc | ||
id: node_version | ||
run: echo ::set-output name=NODE_VERSION::$(cat .nvmrc) | ||
- uses: actions/setup-node@v1 | ||
- uses: actions/setup-node@v3 | ||
with: | ||
node-version: ${{ steps.node_version.outputs.NODE_VERSION }} | ||
node-version-file: ".nvmrc" | ||
- uses: actions/cache@v2 | ||
with: | ||
path: | | ||
|
@@ -131,9 +125,11 @@ jobs: | |
if: ${{ github.ref != 'refs/heads/production' }} | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v3 | ||
- name: Checkout | ||
uses: actions/checkout@v3 | ||
|
||
- name: Run Cypress Tests | ||
uses: cypress-io/github-action@v4.2.0 | ||
uses: cypress-io/github-action@v5 | ||
Comment on lines
+128
to
+132
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See comment above about attempting to get cypress to use our node version |
||
with: | ||
working-directory: tests/cypress | ||
spec: | | ||
|
@@ -148,6 +144,7 @@ jobs: | |
CYPRESS_STATE_USER_PASSWORD: ${{ secrets.CYPRESS_STATE_USER_PASSWORD }} | ||
CYPRESS_ADMIN_USER_EMAIL: ${{ secrets.CYPRESS_ADMIN_USER_EMAIL }} | ||
CYPRESS_ADMIN_USER_PASSWORD: ${{ secrets.CYPRESS_ADMIN_USER_PASSWORD }} | ||
|
||
- name: Upload screenshots | ||
uses: actions/upload-artifact@v2 | ||
if: failure() | ||
|
@@ -163,9 +160,11 @@ jobs: | |
if: ${{ github.ref != 'refs/heads/production' }} | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v3 | ||
- name: Checkout | ||
uses: actions/checkout@v3 | ||
|
||
- name: Check Project A11y | ||
uses: cypress-io/github-action@v4.2.0 | ||
uses: cypress-io/github-action@v5 | ||
with: | ||
working-directory: tests/cypress | ||
spec: tests/accessibility/*.feature | ||
|
@@ -179,6 +178,7 @@ jobs: | |
CYPRESS_ADMIN_USER_EMAIL: ${{ secrets.CYPRESS_ADMIN_USER_EMAIL }} | ||
CYPRESS_ADMIN_USER_PASSWORD: ${{ secrets.CYPRESS_ADMIN_USER_PASSWORD }} | ||
RUN_PA11Y: true | ||
|
||
- name: Upload screenshots | ||
uses: actions/upload-artifact@v2 | ||
if: failure() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,13 @@ import { | |
isFieldElement, | ||
ReportStatus, | ||
} from "types"; | ||
import { filterFormData, useUser } from "utils"; | ||
import { | ||
entityWasUpdated, | ||
filterFormData, | ||
getEntriesToClear, | ||
setClearedEntriesToDefaultValue, | ||
useUser, | ||
} from "utils"; | ||
import accordionVerbiage from "../../verbiage/pages/accordion"; | ||
import overlayVerbiage from "../../verbiage/pages/overlays"; | ||
import { EntityContext } from "components/reports/EntityProvider"; | ||
|
@@ -29,13 +35,8 @@ export const EntityDetailsOverlay = ({ | |
const { report, updateReport } = useContext(ReportContext); | ||
const { full_name, state } = useUser().user ?? {}; | ||
const onError = () => {}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be noted that this line in particular was causing huge errors we had no insight into. It was being passed into the form and hiding validation errors. I've removed the setting of a blank error handler and the validation errors we've been seeing should clear up. |
||
const { | ||
entities, | ||
updateEntities, | ||
setEntities, | ||
setSelectedEntity, | ||
setEntityType, | ||
} = useContext(EntityContext); | ||
const { setEntities, setSelectedEntity, setEntityType } = | ||
useContext(EntityContext); | ||
|
||
useEffect(() => { | ||
setSelectedEntity(selectedEntity); | ||
|
@@ -49,33 +50,56 @@ export const EntityDetailsOverlay = ({ | |
}; | ||
}, [entityType, selectedEntity]); | ||
|
||
const reportFieldDataEntities = report?.fieldData[entityType] || []; | ||
|
||
const onSubmit = async (enteredData: AnyObject) => { | ||
setSubmitting(true); | ||
const filteredFormData = filterFormData( | ||
enteredData, | ||
form.fields.filter(isFieldElement) | ||
); | ||
const newEntity = { | ||
...selectedEntity, | ||
...filteredFormData, | ||
}; | ||
updateEntities(newEntity); | ||
const reportKeys = { | ||
reportType: report?.reportType, | ||
state: state, | ||
id: report?.id, | ||
}; | ||
const dataToWrite = { | ||
metadata: { | ||
status: ReportStatus.IN_PROGRESS, | ||
lastAlteredBy: full_name, | ||
}, | ||
fieldData: { | ||
program: entities, | ||
}, | ||
}; | ||
await updateReport(reportKeys, dataToWrite); | ||
setSubmitting(false); | ||
if (userIsEndUser) { | ||
setSubmitting(true); | ||
const reportKeys = { | ||
reportType: report?.reportType, | ||
state: state, | ||
id: report?.id, | ||
}; | ||
const currentEntities = [...(report?.fieldData[entityType] || [])]; | ||
const selectedEntityIndex = report?.fieldData[entityType].findIndex( | ||
(entity: EntityShape) => entity.id === selectedEntity?.id | ||
); | ||
const filteredFormData = filterFormData( | ||
enteredData, | ||
form.fields.filter(isFieldElement) | ||
); | ||
const entriesToClear = getEntriesToClear( | ||
enteredData, | ||
form.fields.filter(isFieldElement) | ||
); | ||
const newEntity = { | ||
...selectedEntity, | ||
...filteredFormData, | ||
}; | ||
let newEntities = currentEntities; | ||
newEntities[selectedEntityIndex] = newEntity; | ||
newEntities[selectedEntityIndex] = setClearedEntriesToDefaultValue( | ||
newEntities[selectedEntityIndex], | ||
entriesToClear | ||
); | ||
const shouldSave = entityWasUpdated( | ||
reportFieldDataEntities[selectedEntityIndex], | ||
newEntity | ||
); | ||
if (shouldSave) { | ||
const dataToWrite = { | ||
metadata: { | ||
status: ReportStatus.IN_PROGRESS, | ||
lastAlteredBy: full_name, | ||
}, | ||
fieldData: { | ||
[entityType]: newEntities, | ||
}, | ||
}; | ||
await updateReport(reportKeys, dataToWrite); | ||
} | ||
setSubmitting(false); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alllllll this code was updated because, it turns out, the submit button wasn't hooked up to anything. The MLR Overlay was entirely reliant on autosave, and we found that if a user goes quick enough the autosave call will get cancelled when leaving the overlay page and their last item wouldn't save. This would cause a validation error and give us an error about the submit button being disabled. All this code, plus the |
||
closeEntityDetailsOverlay(); | ||
setSidebarHidden(false); | ||
}; | ||
|
@@ -140,15 +164,11 @@ export const EntityDetailsOverlay = ({ | |
autosave={true} | ||
disabled={!userIsEndUser} | ||
validateOnRender={validateOnRender || false} | ||
dontReset={false} | ||
dontReset={true} | ||
/> | ||
<Box sx={sx.footerBox}> | ||
<Flex sx={sx.buttonFlex}> | ||
<Button | ||
onClick={() => closeOverlay()} | ||
type="submit" | ||
sx={sx.saveButton} | ||
> | ||
<Button type="submit" sx={sx.saveButton} form={form.id}> | ||
{submitting ? ( | ||
<Spinner size="md" /> | ||
) : report?.locked ? ( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,6 @@ module.exports = defineConfig({ | |
screenshotsFolder: "screenshots", | ||
videosFolder: "videos", | ||
downloadsFolder: "downloads", | ||
defaultCommandTimeout: 2000000000, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed the 2 billion millisecond timeout that was causing prs to spin endlessly. |
||
types: ["cypress", "cypress-axe"], | ||
env: { | ||
STATE_USER_EMAIL: "cypressstateuser@test.com", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,8 @@ | ||
// element selectors | ||
const cognitoEmailInputField = "//input[@name='email']"; | ||
const cognitoPasswordInputField = "//input[@name='password']"; | ||
const cognitoEmailInputField = 'input[name="email"]'; | ||
const cognitoPasswordInputField = 'input[name="password"]'; | ||
const cognitoLoginButton = "[data-testid='cognito-login-button']"; | ||
const myAccountButton = '[aria-label="my account"'; | ||
|
||
const stateUserPassword = Cypress.env("STATE_USER_PASSWORD"); | ||
const adminUserPassword = Cypress.env("ADMIN_USER_PASSWORD"); | ||
|
@@ -41,6 +42,7 @@ Cypress.Commands.add("clearSession", () => { | |
Cypress.Commands.add("authenticate", (userType, userCredentials) => { | ||
cy.session([userType, userCredentials], () => { | ||
cy.visit("/"); | ||
cy.wait(2000); | ||
let credentials = {}; | ||
|
||
if (userType && userCredentials) { | ||
|
@@ -65,8 +67,8 @@ Cypress.Commands.add("authenticate", (userType, userCredentials) => { | |
throw new Error("Must specify either userType or userCredentials."); | ||
} | ||
|
||
cy.xpath(cognitoEmailInputField).type(credentials.email); | ||
cy.xpath(cognitoPasswordInputField).type(credentials.password, { | ||
cy.get(cognitoEmailInputField).type(credentials.email); | ||
cy.get(cognitoPasswordInputField).type(credentials.password, { | ||
log: false, | ||
}); | ||
cy.get(cognitoLoginButton).click(); | ||
|
@@ -76,17 +78,7 @@ Cypress.Commands.add("authenticate", (userType, userCredentials) => { | |
* This ensures reused sessions maintain these tokens | ||
* We expect at least three for the id, access, and refresh tokens | ||
*/ | ||
cy.waitUntil(() => | ||
cy | ||
.window() | ||
.then( | ||
(window) => | ||
Object.keys(window.localStorage).filter((key) => | ||
key.match( | ||
/CognitoIdentityServiceProvider.+(refresh|access|id)Token/ | ||
) | ||
).length === 3 | ||
) | ||
); | ||
Comment on lines
-79
to
-90
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've updated the authentication call. It was using xpath which is deprecated, and I've removed with waitUtil and key check. Wait until wasn't in the docs from what I could find, and so I really tried to simplify the login process to be safe. That being said, calling login from the ui is an anti-pattern I guess? At least according to cypress. Some information about how we're actually supposed to call it is here but I simplified it to work with ours. |
||
cy.wait(4500); | ||
cy.get(myAccountButton).should("exist"); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,23 +18,33 @@ When("these form elements are edited:/filled:", function (dataTable) { | |
const inputValue = row[2]; | ||
switch (inputType) { | ||
case "singleCheckbox": | ||
if (inputValue == "true") input.check().blur(); | ||
else input.uncheck(); | ||
if (inputValue == "true") { | ||
input.check(); | ||
input.blur(); | ||
} else input.uncheck(); | ||
Comment on lines
+21
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Separating out chaining commands. You can read why from the cypress docs here: https://docs.cypress.io/guides/core-concepts/introduction-to-cypress#Chains-of-Commands |
||
break; | ||
case "radio": | ||
input.check(inputValue).blur(); | ||
input.check(inputValue); | ||
input.blur(); | ||
break; | ||
case "checkbox": | ||
input.check(inputValue).blur(); | ||
input.check(inputValue); | ||
input.blur(); | ||
break; | ||
case "dropdown": | ||
input.select(inputValue).blur(); | ||
input.select(inputValue); | ||
input.blur(); | ||
break; | ||
case "repeated": | ||
input.eq(repeatedInput).clear().type(inputValue).blur(); | ||
input.eq(repeatedInput); | ||
input.clear(); | ||
input.type(inputValue); | ||
input.blur(); | ||
break; | ||
default: | ||
input.clear().type(inputValue).blur(); | ||
input.clear(); | ||
input.type(inputValue); | ||
input.blur(); | ||
break; | ||
} | ||
if (this.delay) cy.wait(this.delay); | ||
|
@@ -45,9 +55,8 @@ When("these form elements are prefilled and disabled:", (dataTable) => { | |
dataTable.rawTable.forEach((row) => { | ||
switch (row[1]) { | ||
default: | ||
cy.get(`[name='${row[0]}']`) | ||
.should("have.value", row[2]) | ||
.should("be.disabled"); | ||
cy.get(`[name='${row[0]}']`).should("have.value", row[2]); | ||
cy.get(`[name='${row[0]}']`).should("be.disabled"); | ||
break; | ||
} | ||
}); | ||
|
@@ -69,7 +78,8 @@ When("these form elements are prefilled:", (dataTable) => { | |
|
||
When("the form is submitted", () => { | ||
const submitButton = "[type='submit']"; | ||
cy.get(submitButton).focus().click(); | ||
cy.get(submitButton).focus(); | ||
cy.get(submitButton).click(); | ||
}); | ||
|
||
When("I click the {string} button", (name) => { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,15 +17,9 @@ describe("Footer integration tests", () => { | |
}); | ||
|
||
it("Footer accessibility statement link navigates to the right external URL", () => { | ||
cy.contains(accessibilityStatementLinkText) | ||
.invoke("attr", "href") | ||
.should( | ||
"eq", | ||
"https://www.cms.gov/About-CMS/Agency-Information/Aboutwebsite/CMSNondiscriminationNotice" | ||
); | ||
cy.contains(accessibilityStatementLinkText) | ||
.invoke("attr", "target") | ||
.should("eq", "_blank"); | ||
cy.get( | ||
'a[href="https://www.cms.gov/About-CMS/Agency-Information/Aboutwebsite/CMSNondiscriminationNotice"]' | ||
).contains(accessibilityStatementLinkText); | ||
Comment on lines
+20
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moved to a slightly better way of calling and erroring if the footer link isn't found. |
||
|
||
cy.contains(accessibilityStatementLinkText).then((link) => { | ||
cy.request(link.prop("href")).its("status").should("eq", 200); | ||
|
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.
All the deploy updates were an attempt to get Cypress to use the same node version we use. Unfortunately, Cypress doesn't respect the node version you give it. So setting up node doesn't matter. It seems its tied to the git actions node version. Heres a thread with all sorts of other threads to dig down the rabbit hole (cypress-io/github-action#637).
That being said, we were on v1 which was no longer supported, so swapping to v3 here is a solid upgrade.