forked from StateVoicesNational/Spoke
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Set up cypress and add campaign and sign up tests
- Loading branch information
Showing
18 changed files
with
725 additions
and
63 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,3 +23,5 @@ coverage/ | |
package-lock.json | ||
CONFIG_FILE.json | ||
scratch/ | ||
cypress/screenshots | ||
cypress/videos |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
export default { | ||
users: { | ||
admin1: { | ||
name: "admin1", | ||
email: "[email protected]", | ||
password: "SpokeAdmin1!", | ||
first_name: "Admin1First", | ||
last_name: "Admin1Last", | ||
cell: "5555550000", | ||
role: "OWNER" | ||
}, | ||
texter1: { | ||
name: "texter1", | ||
email: "[email protected]", | ||
password: "SpokeTexter1!", | ||
first_name: "Texter1First", | ||
last_name: "Texter1Last", | ||
cell: "5555550001", | ||
role: "TEXTER" | ||
} | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
first_name,last_name,cell,favorite_color | ||
ContactFirst1,ContactLast1,12025550175,green | ||
ContactFirst2,ContactLast2,12025550176,orange |
103 changes: 103 additions & 0 deletions
103
__test__/cypress/integration/basic-campaign-e2e.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import TestData from "../fixtures/test-data"; | ||
|
||
describe("End-to-end campaign flow", () => { | ||
before(() => { | ||
// ensure texter one exists so they can be assigned | ||
cy.task("createOrUpdateUser", TestData.users.texter1); | ||
}); | ||
|
||
it("with an assigned texter", () => { | ||
// ADMIN | ||
const campaignTitle = `E2E basic flow ${new Date().getTime()}`; | ||
const campaignDescription = "Basic campaign with assignments"; | ||
|
||
cy.login("admin1"); | ||
cy.visit("/"); | ||
cy.get("button[data-test=addCampaign]").click(); | ||
|
||
// Fill out basics | ||
cy.get("input[data-test=title]").type(campaignTitle); | ||
cy.get("input[data-test=description]").type(campaignDescription); | ||
cy.get("input[data-test=dueBy]").click(); | ||
|
||
// Very brittle DatePicker interaction to pick the first day of the next month | ||
// Note: newer versions of Material UI appear to have better hooks for integration | ||
// testing. | ||
cy.get( | ||
"body > div:nth-child(5) > div > div:nth-child(1) > div > div > div > div > div:nth-child(2) > div:nth-child(1) > div:nth-child(1) > button:nth-child(3)" | ||
).click(); | ||
cy.get("button") | ||
.contains("1") | ||
.click(); | ||
// wait for modal to get dismissed, see if there is a better way to do this | ||
cy.wait(200); | ||
cy.get("[data-test=campaignBasicsForm]").submit(); | ||
|
||
// Upload Contacts | ||
cy.get("#contact-upload").attachFile("two-contacts.csv"); | ||
cy.get("button[data-test=submitContactsCsvUpload]").click(); | ||
|
||
// Assignments | ||
// Note: Material UI v0 AutoComplete component appears to require a click on the element | ||
// later versions should just allow you to hit enter | ||
cy.get("input[data-test=texterSearch]").type("Texter1First"); | ||
// see if there is a better way to select the search result | ||
cy.get("body") | ||
.contains("Texter1First Texter1Last") | ||
.click(); | ||
cy.get("input[data-test=autoSplit]").click(); | ||
cy.get("button[data-test=submitCampaignTextersForm]").click(); | ||
|
||
// Interaction Steps | ||
cy.get("textarea[data-test=editorInteraction]").click(); | ||
cy.get(".DraftEditor-root").type( | ||
"Hi {{}firstName{}} this is {{}texterFirstName{}}, how are you?" | ||
); | ||
cy.get("button[data-test=scriptDone]").click(); | ||
cy.get("input[data-test=questionText]").type("How are you?"); | ||
cy.get("button[data-test=addResponse]").click(); | ||
cy.get("input[data-test=answerOption]").type("Good"); | ||
cy.get("textarea[data-test=editorInteraction]") | ||
.eq(1) | ||
.click(); | ||
cy.get(".DraftEditor-root").type("Great!"); | ||
cy.get("button[data-test=scriptDone]").click(); | ||
cy.get("button[data-test=interactionSubmit]").click(); | ||
cy.get("button[data-test=startCampaign]").click(); | ||
cy.get("div") | ||
.contains("This campaign is running") | ||
.should("exist"); | ||
|
||
cy.url().then(url => { | ||
const campaignId = url.match(/campaigns\/(\d+)/)[1]; | ||
// TEXTER | ||
cy.login("texter1"); | ||
cy.visit("/app"); | ||
const cardSelector = `div[data-test=assignmentSummary-${campaignId}]`; | ||
cy.get(cardSelector) | ||
.contains(campaignTitle) | ||
.should("exist"); | ||
cy.get(cardSelector) | ||
.contains(campaignDescription) | ||
.should("exist"); | ||
cy.get(cardSelector) | ||
.find("button[data-test=sendFirstTexts]") | ||
.click(); | ||
cy.get("textArea[name=messageText]").then(el => { | ||
expect(el).to.have.text( | ||
"Hi Contactfirst1 this is Texter1first, how are you?" | ||
); | ||
}); | ||
cy.get("button[data-test=send]").click(); | ||
cy.get("textArea[name=messageText]").then(el => { | ||
expect(el).to.have.text( | ||
"Hi Contactfirst2 this is Texter1first, how are you?" | ||
); | ||
}); | ||
cy.get("button[data-test=send]").click(); | ||
// Go back to TODOS | ||
cy.wait(200); | ||
cy.url().should("include", "/todos"); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// Don't run this | ||
describe("Login with the local passport strategy", () => { | ||
const ts = new Date().getTime(); | ||
|
||
beforeEach(() => { | ||
cy.visit("/"); | ||
}); | ||
|
||
it("Sign up", () => { | ||
cy.get("#login").click(); | ||
cy.get("button[name='signup']").click(); | ||
cy.get("input[name='email']").type(`spoke.itest.${ts}@example.com`); | ||
cy.get("input[name='firstName']").type("SignupTestUserFirst"); | ||
cy.get("input[name='lastName']").type("SignupTestUserLast"); | ||
cy.get("input[name='cell']").type("5555551234"); | ||
cy.get("input[name='password']").type("SignupTestUser1!", { delay: 10 }); | ||
cy.get("input[name='passwordConfirm']").type("SignupTestUser1!", { | ||
delay: 10 | ||
}); | ||
cy.get("[data-test=userEditForm]").submit(); | ||
// The next page is different depending on whether SUPPRESS_SELF_INVITE is | ||
// set, so we just assert that we are not still on the login page | ||
// the wait is required because cypress doesn't know how long to wait for the url to change | ||
cy.wait(500); | ||
cy.url().then(url => expect(url).not.to.match(/.*login.*/)); | ||
}); | ||
|
||
// sign in as the user | ||
it("Sign in", () => { | ||
cy.get("#login").click(); | ||
cy.get("input[name='email']").type(`spoke.itest.${ts}@example.com`); | ||
cy.get("input[name='password']").type("SignupTestUser1!", { delay: 10 }); | ||
cy.get("[data-test=userEditForm]").submit(); | ||
cy.wait(500); | ||
cy.url().then(url => expect(url).not.to.match(/.*login.*/)); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// plugins run in the cypress Node process, not the browser. Define tasks | ||
// operations like seeding the database that can't be run from the browser. | ||
// See: https://on.cypress.io/plugins-guide | ||
require("dotenv").load(); | ||
require("babel-register"); | ||
require("babel-polyfill"); | ||
|
||
const makeTasks = require("./tasks").makeTasks; | ||
const utils = require("./utils"); | ||
|
||
module.exports = async (on, config) => { | ||
if (config.env.SUPPRESS_ORG_CREATION && !config.env.TEST_ORGANIZATION_ID) { | ||
throw new Error( | ||
"Missing TEST_ORGANIZATION_ID and org creation is disabled" | ||
); | ||
} | ||
if (!config.env.TEST_ORGANIZATION_ID) { | ||
config.env.TEST_ORGANIZATION_ID = await utils.getOrCreateTestOrganization(); | ||
} | ||
|
||
on("task", makeTasks(config)); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { r, User } from "../../../src/server/models"; | ||
import AuthHasher from "passport-local-authenticate"; | ||
|
||
/** | ||
* Make Cypress tasks with access to the config. | ||
* | ||
* https://docs.cypress.io/api/commands/task.html#Syntax | ||
*/ | ||
export function makeTasks(config) { | ||
return { | ||
/** | ||
* Create a user and add it to the test organization with the specified role. | ||
*/ | ||
createOrUpdateUser: async userData => { | ||
let user = await r | ||
.knex("user") | ||
.where("email", userData.email) | ||
.first(); | ||
|
||
if (!user) { | ||
// TODO[matteosb]: support Auth0 and consider creating users through | ||
// the API rather than with direct database access, which would be | ||
// better when running against remote envs. Alternatively, we could | ||
// simply not support user creation when running against a remove | ||
// env, similar to SUPPRESS_ORG_CREATION. | ||
user = await new Promise((resolve, reject) => { | ||
AuthHasher.hash(userData.password, async (err, hashed) => { | ||
if (err) reject(err); | ||
const passwordToSave = `localauth|${hashed.salt}|${hashed.hash}`; | ||
const { email, first_name, last_name, cell } = userData; | ||
const u = await User.save({ | ||
email, | ||
first_name, | ||
last_name, | ||
cell, | ||
auth0_id: passwordToSave, | ||
is_superadmin: false | ||
}); | ||
resolve(u); | ||
}); | ||
}); | ||
} | ||
|
||
const role = await r | ||
.knex("user_organization") | ||
.where({ organization_id: config.env.TEST_ORGANIZATION_ID }) | ||
.first(); | ||
|
||
if (!role) { | ||
await r.knex("user_organization").insert({ | ||
user_id: user.id, | ||
organization_id: config.env.TEST_ORGANIZATION_ID, | ||
role: userData.role | ||
}); | ||
} | ||
|
||
if (role !== userData.role) { | ||
await r | ||
.knex("user_organization") | ||
.where({ organization_id: config.env.TEST_ORGANIZATION_ID }) | ||
.update({ role: userData.role }); | ||
} | ||
|
||
return user.id; | ||
} | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { r } from "../../../src/server/models"; | ||
import uuid from "uuid"; | ||
|
||
const DEFAULT_ORGANIZATION_NAME = "E2E Test Organization"; | ||
|
||
export async function getOrCreateTestOrganization() { | ||
let org = await r | ||
.knex("organization") | ||
.where("name", DEFAULT_ORGANIZATION_NAME) | ||
.first(); | ||
if (!org) { | ||
org = await r | ||
.knex("organization") | ||
.insert({ name: DEFAULT_ORGANIZATION_NAME, uuid: uuid.v4() }) | ||
.returning("*"); | ||
} | ||
return org.id; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// support/index.js is processed and loaded automatically before your test files. | ||
// this runs in the browser is a good place to define common cypress operations | ||
import "cypress-file-upload"; | ||
|
||
import TestData from "../fixtures/test-data"; | ||
|
||
// TODO: support Auth0 | ||
Cypress.Commands.add("login", testDataId => { | ||
const userData = TestData.users[testDataId]; | ||
if (!userData) { | ||
throw Error(`Unknown test user ${testDataId}`); | ||
} | ||
|
||
cy.task("createOrUpdateUser", userData); | ||
cy.request("POST", "/login-callback", { | ||
nextUrl: "/", | ||
authType: "login", | ||
password: userData.password, | ||
email: userData.email | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"baseUrl": "http://localhost:3000", | ||
"integrationFolder": "__test__/cypress/integration", | ||
"fixturesFolder": "__test__/cypress/fixtures", | ||
"pluginsFile": "__test__/cypress/plugins/index.js", | ||
"supportFile": "__test__/cypress/support/index.js", | ||
"testFiles": "*.test.js", | ||
"env": { | ||
"SUPPRESS_ORG_CREATION": false, | ||
"TEST_ORGANIZATION_ID": null | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.