Skip to content

Commit 36e5595

Browse files
authored
Merge pull request #2188 from MoveOnOrg/stage-main-12-2
stage-main 12.2 release candidate
2 parents e34e2ea + 734aac9 commit 36e5595

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+5818
-2955
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Spoke is an open source text-distribution tool for organizations to mobilize sup
77

88
Spoke was created by Saikat Chakrabarti and Sheena Pakanati, and is now maintained by MoveOn.org.
99

10-
The latest version is [12.1](https://github.com/MoveOnOrg/Spoke/tree/v12.1) (see [release notes](https://github.com/MoveOnOrg/Spoke/blob/main/docs/RELEASE_NOTES.md#v121))
10+
The latest version is [12.2](https://github.com/MoveOnOrg/Spoke/tree/12.2.0) (see [release notes](https://github.com/MoveOnOrg/Spoke/blob/main/docs/RELEASE_NOTES.md#v122))
1111

1212

1313
## Setting up Spoke
@@ -24,7 +24,7 @@ Want to know more?
2424
### Quick Start with Heroku
2525
This version of Spoke suitable for testing and, potentially, for small campaigns. This won't cost any money and will not support production(aka large-scale) usage. It's a great way to practice deploying Spoke or see it in action.
2626

27-
<a href="https://heroku.com/deploy?template=https://github.com/MoveOnOrg/Spoke/tree/v12.1">
27+
<a href="https://heroku.com/deploy?template=https://github.com/MoveOnOrg/Spoke/tree/12.2.0">
2828

2929
<img src="https://www.herokucdn.com/deploy/button.svg" alt="Deploy">
3030
</a>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
first,last_name,company_name,address,city,county,state,ZipOrPost,Cell_Phone,phone,email
2+
James,Baggins,"Benton, John B Jr",6649 N Blue Gum St,New Orleans,Orleans,LA,70116,504-621-8927,504-845-1427,[email protected]
3+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
firstName,lastName,cell
2+
Hannah,Clarke,7208293847
3+
James,Potter,9709204873
4+
Hermione,Granger,8143853820
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
funnyName,lastName,cell,zipWrongPostal,custom_foo,custom_xxx
2+
Dolores,Huerta,2095550100,95201,bar,yyy
3+
Jill,Wonka,9384274839,72845,foo,xxx
4+
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
first_name,last_name,cell,favorite_color
1+
firstName,last_name,cell,favorite_color
22
ContactFirst1,ContactLast1,12025550175,green
3-
ContactFirst2,ContactLast2,12025550176,orange
3+
ContactFirst2,ContactLast2,12025550176,orange

__test__/cypress/integration/basic-campaign-e2e.test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,16 @@ describe("End-to-end campaign flow", () => {
6666
cy.get("[data-test=campaignBasicsForm]").submit();
6767
cy.task("log", "cyLOG basic-campaign-e2e-test 4");
6868
// Upload Contacts
69+
cy.get("#contact-upload").attachFile("two-contacts-broken.csv"),
70+
{ force: true };
71+
cy.wait(400);
72+
cy.get("#contact-upload").attachFile("three-contacts.csv"), { force: true };
73+
cy.wait(400);
74+
cy.get("#contact-upload").attachFile("three-contacts.csv"), { force: true };
75+
cy.wait(400);
76+
cy.get("#contact-upload").attachFile("csv-headers-test.csv"),
77+
{ force: true };
78+
cy.wait(400);
6979
cy.get("#contact-upload").attachFile("two-contacts.csv"), { force: true };
7080
cy.wait(400);
7181
cy.get("button[data-test=submitContactsCsvUpload]").click();
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { when } from "jest-when";
2+
import {
3+
validateActionHandler,
4+
validateActionHandlerWithClientChoices
5+
} from "../../../src/extensions/action-handlers";
6+
7+
import * as HandlerToTest from "../../../src/extensions/action-handlers/civicrm-addtag";
8+
import { getConfig, hasConfig } from "../../../src/server/api/lib/config";
9+
import { searchTags } from "../../../src/extensions/contact-loaders/civicrm/util";
10+
import { getCacheLength } from "../../../src/extensions/contact-loaders/civicrm/getcachelength";
11+
import { CIVICRM_ACTION_HANDLER_ADDTAG } from "../../../src/extensions/contact-loaders/civicrm/const";
12+
13+
jest.mock("../../../src/server/api/lib/config");
14+
jest.mock("../../../src/extensions/contact-loaders/civicrm/util");
15+
jest.mock("../../../src/extensions/contact-loaders/civicrm/getcachelength");
16+
17+
describe("civicrm-addtag", () => {
18+
beforeEach(async () => {
19+
when(hasConfig)
20+
.calledWith("CIVICRM_API_KEY")
21+
.mockReturnValue(true);
22+
when(hasConfig)
23+
.calledWith("CIVICRM_SITE_KEY")
24+
.mockReturnValue(true);
25+
when(hasConfig)
26+
.calledWith("CIVICRM_API_URL")
27+
.mockReturnValue(true);
28+
});
29+
30+
afterEach(async () => {
31+
jest.restoreAllMocks();
32+
});
33+
34+
it("passes validation, and comes with standard action handler functionality", async () => {
35+
expect(() => validateActionHandler(HandlerToTest)).not.toThrowError();
36+
expect(() =>
37+
validateActionHandlerWithClientChoices(HandlerToTest)
38+
).not.toThrowError();
39+
expect(HandlerToTest.name).toEqual(CIVICRM_ACTION_HANDLER_ADDTAG);
40+
expect(HandlerToTest.displayName()).toEqual("Add tag to CiviCRM contact");
41+
expect(await HandlerToTest.processDeletedQuestionResponse()).toEqual(
42+
undefined
43+
);
44+
expect(HandlerToTest.clientChoiceDataCacheKey({ id: 1 })).toEqual("1");
45+
});
46+
47+
describe("civicrm-addtag available()", () => {
48+
it("is available if the civicrm contact loader is available", async () => {
49+
when(getConfig)
50+
.calledWith("CONTACT_LOADERS")
51+
.mockReturnValue("civicrm");
52+
when(getCacheLength)
53+
.calledWith(CIVICRM_ACTION_HANDLER_ADDTAG)
54+
.mockReturnValue(1800);
55+
expect(await HandlerToTest.available({ id: 1 })).toEqual({
56+
result: true,
57+
expiresSeconds: 1800
58+
});
59+
});
60+
61+
it("is not available if the civicrm contact loader is not available", async () => {
62+
when(getConfig)
63+
.calledWith("CONTACT_LOADERS")
64+
.mockReturnValue("");
65+
when(getCacheLength)
66+
.calledWith(CIVICRM_ACTION_HANDLER_ADDTAG)
67+
.mockReturnValue(1800);
68+
expect(await HandlerToTest.available({ id: 1 })).toEqual({
69+
result: false,
70+
expiresSeconds: 1800
71+
});
72+
});
73+
});
74+
75+
describe("civicrm-addtag getClientChoiceData()", () => {
76+
it("returns successful data when data is available", async () => {
77+
const theTagData = [
78+
{ id: "2", name: "Company" },
79+
{ id: "3", name: "Government Entity" },
80+
{ id: "4", name: "Major Donor" },
81+
{ id: "1", name: "Non-profit" },
82+
{ id: "5", name: "Volunteer" }
83+
];
84+
85+
when(searchTags).mockResolvedValue(theTagData);
86+
when(getCacheLength)
87+
.calledWith(CIVICRM_ACTION_HANDLER_ADDTAG)
88+
.mockReturnValue(7200);
89+
expect(await HandlerToTest.getClientChoiceData({ id: 1 })).toEqual({
90+
data:
91+
'{"items":[{"name":"Company","details":"2"},{"name":"Government Entity","details":"3"},{"name":"Major Donor","details":"4"},{"name":"Non-profit","details":"1"},{"name":"Volunteer","details":"5"}]}',
92+
expiresSeconds: 7200
93+
});
94+
});
95+
96+
it("returns successful data when data is empty", async () => {
97+
const theTagData = [];
98+
99+
when(searchTags).mockResolvedValue(theTagData);
100+
when(getCacheLength)
101+
.calledWith(CIVICRM_ACTION_HANDLER_ADDTAG)
102+
.mockReturnValue(7200);
103+
expect(await HandlerToTest.getClientChoiceData({ id: 1 })).toEqual({
104+
data: '{"items":[]}',
105+
expiresSeconds: 7200
106+
});
107+
});
108+
});
109+
});
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { when } from "jest-when";
2+
import {
3+
validateActionHandler,
4+
validateActionHandlerWithClientChoices
5+
} from "../../../src/extensions/action-handlers";
6+
import { searchGroups } from "../../../src/extensions/contact-loaders/civicrm/util";
7+
import { getCacheLength } from "../../../src/extensions/contact-loaders/civicrm/getcachelength";
8+
import * as HandlerToTest from "../../../src/extensions/action-handlers/civicrm-addtogroup";
9+
import { getConfig, hasConfig } from "../../../src/server/api/lib/config";
10+
import { CIVICRM_ACTION_HANDLER_ADDGROUP } from "../../../src/extensions/contact-loaders/civicrm/const";
11+
12+
jest.mock("../../../src/server/api/lib/config");
13+
jest.mock("../../../src/extensions/contact-loaders/civicrm/util");
14+
jest.mock("../../../src/extensions/contact-loaders/civicrm/getcachelength");
15+
16+
describe("civicrm-addtogroup", () => {
17+
beforeEach(async () => {
18+
when(hasConfig)
19+
.calledWith("CIVICRM_API_KEY")
20+
.mockReturnValue(true);
21+
when(hasConfig)
22+
.calledWith("CIVICRM_SITE_KEY")
23+
.mockReturnValue(true);
24+
when(hasConfig)
25+
.calledWith("CIVICRM_API_URL")
26+
.mockReturnValue(true);
27+
});
28+
29+
afterEach(async () => {
30+
jest.restoreAllMocks();
31+
});
32+
33+
it("passes validation, and comes with standard action handler functionality", async () => {
34+
expect(() => validateActionHandler(HandlerToTest)).not.toThrowError();
35+
expect(() =>
36+
validateActionHandlerWithClientChoices(HandlerToTest)
37+
).not.toThrowError();
38+
expect(HandlerToTest.name).toEqual(CIVICRM_ACTION_HANDLER_ADDGROUP);
39+
expect(HandlerToTest.displayName()).toEqual("Add to CiviCRM group");
40+
expect(await HandlerToTest.processDeletedQuestionResponse()).toEqual(
41+
undefined
42+
);
43+
expect(HandlerToTest.clientChoiceDataCacheKey({ id: 1 })).toEqual("1");
44+
});
45+
46+
describe("civicrm-addtogroup available()", () => {
47+
it("is available if the civicrm contact loader is available", async () => {
48+
when(getConfig)
49+
.calledWith("CONTACT_LOADERS")
50+
.mockReturnValue("civicrm");
51+
when(getCacheLength)
52+
.calledWith(CIVICRM_ACTION_HANDLER_ADDGROUP)
53+
.mockReturnValue(1800);
54+
expect(await HandlerToTest.available({ id: 1 })).toEqual({
55+
result: true,
56+
expiresSeconds: 1800
57+
});
58+
});
59+
60+
it("is not available if the civicrm contact loader is not available", async () => {
61+
when(getConfig)
62+
.calledWith("CONTACT_LOADERS")
63+
.mockReturnValue("");
64+
when(getCacheLength)
65+
.calledWith(CIVICRM_ACTION_HANDLER_ADDGROUP)
66+
.mockReturnValue(1800);
67+
expect(await HandlerToTest.available({ id: 1 })).toEqual({
68+
result: false,
69+
expiresSeconds: 1800
70+
});
71+
});
72+
});
73+
74+
describe("civicrm-addtag getClientChoiceData()", () => {
75+
it("returns successful data when data is available", async () => {
76+
const theGroupData = [
77+
{ id: "2", title: "Administrators" },
78+
{ id: "3", title: "Volunteers" },
79+
{ id: "4", title: "Donors" },
80+
{ id: "1", title: "Newsletter Subscribers" },
81+
{ id: "5", title: "Volunteer" }
82+
];
83+
84+
when(searchGroups)
85+
.calledWith("")
86+
.mockResolvedValue(theGroupData);
87+
when(getCacheLength)
88+
.calledWith(CIVICRM_ACTION_HANDLER_ADDGROUP)
89+
.mockReturnValue(7200);
90+
expect(await HandlerToTest.getClientChoiceData({ id: 1 })).toEqual({
91+
data:
92+
'{"items":[{"name":"Administrators","details":"2"},{"name":"Volunteers","details":"3"},{"name":"Donors","details":"4"},{"name":"Newsletter Subscribers","details":"1"},{"name":"Volunteer","details":"5"}]}',
93+
expiresSeconds: 7200
94+
});
95+
});
96+
97+
it("returns successful data when data is empty", async () => {
98+
const theGroupData = [];
99+
100+
when(searchGroups)
101+
.calledWith("")
102+
.mockResolvedValue(theGroupData);
103+
when(getCacheLength)
104+
.calledWith(CIVICRM_ACTION_HANDLER_ADDGROUP)
105+
.mockReturnValue(7200);
106+
expect(await HandlerToTest.getClientChoiceData({ id: 1 })).toEqual({
107+
data: '{"items":[]}',
108+
expiresSeconds: 7200
109+
});
110+
});
111+
});
112+
});
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { when } from "jest-when";
2+
import {
3+
validateActionHandler,
4+
validateActionHandlerWithClientChoices
5+
} from "../../../src/extensions/action-handlers";
6+
import { searchEvents } from "../../../src/extensions/contact-loaders/civicrm/util";
7+
import { getCacheLength } from "../../../src/extensions/contact-loaders/civicrm/getcachelength";
8+
import * as HandlerToTest from "../../../src/extensions/action-handlers/civicrm-registerevent";
9+
import { getConfig, hasConfig } from "../../../src/server/api/lib/config";
10+
import {
11+
CIVICRM_ACTION_HANDLER_REGISTEREVENT,
12+
CIVICRM_CONTACT_LOADER
13+
} from "../../../src/extensions/contact-loaders/civicrm/const";
14+
15+
jest.mock("../../../src/server/api/lib/config");
16+
jest.mock("../../../src/extensions/contact-loaders/civicrm/util");
17+
jest.mock("../../../src/extensions/contact-loaders/civicrm/getcachelength");
18+
19+
describe("civicrm-registerevent", () => {
20+
beforeEach(async () => {
21+
when(hasConfig)
22+
.calledWith("CIVICRM_API_KEY")
23+
.mockReturnValue(true);
24+
when(hasConfig)
25+
.calledWith("CIVICRM_SITE_KEY")
26+
.mockReturnValue(true);
27+
when(hasConfig)
28+
.calledWith("CIVICRM_API_URL")
29+
.mockReturnValue(true);
30+
});
31+
32+
afterEach(async () => {
33+
jest.restoreAllMocks();
34+
});
35+
36+
it("passes validation, and comes with standard action handler functionality", async () => {
37+
when(getCacheLength)
38+
.calledWith(CIVICRM_CONTACT_LOADER)
39+
.mockReturnValue(7200);
40+
expect(() => validateActionHandler(HandlerToTest)).not.toThrowError();
41+
expect(() =>
42+
validateActionHandlerWithClientChoices(HandlerToTest)
43+
).not.toThrowError();
44+
expect(HandlerToTest.name).toEqual(CIVICRM_ACTION_HANDLER_REGISTEREVENT);
45+
expect(HandlerToTest.displayName()).toEqual(
46+
"Register contact for CiviCRM event"
47+
);
48+
expect(await HandlerToTest.processDeletedQuestionResponse()).toEqual(
49+
undefined
50+
);
51+
expect(HandlerToTest.clientChoiceDataCacheKey({ id: 1 })).toEqual("1");
52+
});
53+
54+
describe("civicrm-registerevent available()", () => {
55+
it("is available if the civicrm contact loader is available", async () => {
56+
when(getConfig)
57+
.calledWith("CONTACT_LOADERS")
58+
.mockReturnValue("civicrm");
59+
when(getCacheLength)
60+
.calledWith(CIVICRM_ACTION_HANDLER_REGISTEREVENT)
61+
.mockReturnValue(1800);
62+
expect(await HandlerToTest.available({ id: 1 })).toEqual({
63+
result: true,
64+
expiresSeconds: 1800
65+
});
66+
});
67+
68+
it("is not available if the civicrm contact loader is not available", async () => {
69+
when(getConfig)
70+
.calledWith("CONTACT_LOADERS")
71+
.mockReturnValue("");
72+
when(getCacheLength)
73+
.calledWith(CIVICRM_ACTION_HANDLER_REGISTEREVENT)
74+
.mockReturnValue(1800);
75+
expect(await HandlerToTest.available({ id: 1 })).toEqual({
76+
result: false,
77+
expiresSeconds: 1800
78+
});
79+
});
80+
});
81+
82+
describe("civicrm-registerevent getClientChoiceData()", () => {
83+
it("returns successful data when data is available", async () => {
84+
const theEventData = [
85+
{
86+
id: "2",
87+
title: "Company",
88+
event_title: "Demo Event",
89+
default_role_id: "1",
90+
start_date: "2021-11-01 16:00:00"
91+
}
92+
];
93+
94+
when(searchEvents).mockResolvedValue(theEventData);
95+
when(getCacheLength)
96+
.calledWith(CIVICRM_ACTION_HANDLER_REGISTEREVENT)
97+
.mockReturnValue(7200);
98+
expect(await HandlerToTest.getClientChoiceData({ id: 1 })).toEqual({
99+
data:
100+
'{"items":[{"name":"Company (2021-11-01)","details":"{\\"id\\":\\"2\\",\\"role_id\\":\\"1\\"}"}]}',
101+
expiresSeconds: 7200
102+
});
103+
});
104+
105+
it("returns successful data when data is empty", async () => {
106+
const theEventData = [];
107+
108+
when(searchEvents).mockResolvedValue(theEventData);
109+
when(getCacheLength)
110+
.calledWith(CIVICRM_ACTION_HANDLER_REGISTEREVENT)
111+
.mockReturnValue(7200);
112+
expect(await HandlerToTest.getClientChoiceData({ id: 1 })).toEqual({
113+
data: '{"items":[]}',
114+
expiresSeconds: 7200
115+
});
116+
});
117+
});
118+
});

0 commit comments

Comments
 (0)