Add integration tests for petition (#10646)

* add integration tests for petition
This commit is contained in:
Mavis Ou 2023-06-01 10:29:11 -07:00 коммит произвёл GitHub
Родитель aced10ec6f
Коммит c5d309a385
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
7 изменённых файлов: 298 добавлений и 7 удалений

7
.github/workflows/continous-integration.yml поставляемый
Просмотреть файл

@ -145,9 +145,10 @@ jobs:
USE_S3: False
X_FRAME_OPTIONS: DENY
XSS_PROTECTION: True
CSP_FONT_SRC: "'self' https://fonts.gstatic.com https://fonts.googleapis.com https://code.cdn.mozilla.net"
CSP_SCRIPT_SRC: "'self' 'unsafe-inline' https://www.google-analytics.com/analytics.js http://*.shpg.org/ https://comments.mozillafoundation.org/ https://airtable.com https://platform.twitter.com https://cdnjs.cloudflare.com/ajax/libs/gsap/3.8.0/gsap.min.js https://cdnjs.cloudflare.com/ajax/libs/gsap/3.8.0/ScrollTrigger.min.js"
CSP_STYLE_SRC: "'self' 'unsafe-inline' https://code.cdn.mozilla.net https://fonts.googleapis.com https://platform.twitter.com"
CSP_CONNECT_SRC: "*"
CSP_FONT_SRC: "'self' https://fonts.gstatic.com https://fonts.googleapis.com https://code.cdn.mozilla.net https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/fonts/ data:"
CSP_SCRIPT_SRC: "'self' 'unsafe-inline' https://www.google-analytics.com/analytics.js http://*.shpg.org/ https://comments.mozillafoundation.org/ https://airtable.com https://platform.twitter.com https://cdnjs.cloudflare.com/ajax/libs/gsap/3.8.0/gsap.min.js https://cdnjs.cloudflare.com/ajax/libs/gsap/3.8.0/ScrollTrigger.min.js https://*.googletagmanager.com https://*.fundraiseup.com https://mozillafoundation.tfaforms.net 'unsafe-eval'"
CSP_STYLE_SRC: "'self' 'unsafe-inline' https://code.cdn.mozilla.net https://fonts.googleapis.com https://platform.twitter.com https://mozillafoundation.tfaforms.net https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4

Просмотреть файл

@ -2,6 +2,7 @@ from factory import SubFactory, Trait
from wagtail.models import Page as WagtailPage
from networkapi.utility.faker.helpers import get_homepage, reseed
from networkapi.wagtailpages.donation_modal import DonationModals
from networkapi.wagtailpages.models import CampaignIndexPage, CampaignPage
from .abstract import CMSPageFactory
@ -21,6 +22,11 @@ class CampaignPageFactory(CMSPageFactory):
class Params:
no_cta = Trait(cta=None)
cta_show_all_fields = Trait(
cta=SubFactory(
PetitionFactory, requires_country_code=True, requires_postal_code=True, comment_requirements="required"
)
)
cta = SubFactory(PetitionFactory)
@ -55,7 +61,13 @@ def generate(seed):
print("Generating single-page CampaignPage")
# Most Campaign Pages on prod use wide content layout,
# Setting narrowed_page_content to False to make it easier to test the real use case
CampaignPageFactory.create(parent=campaign_index_page, title="single-page", narrowed_page_content=False)
CampaignPageFactory.create(
parent=campaign_index_page,
title="single-page",
narrowed_page_content=False,
donation_modals=[DonationModals.objects.first()],
cta_show_all_fields=True,
)
reseed(seed)

Просмотреть файл

@ -14,9 +14,10 @@ Please follow the steps below every time after you paste a new version of FormAs
- Update the variable names to they match the new FormAssembly field names
- Plug in those variables into corresponding form fields in formassembly_body.html
4. If form field names are changed, update the bottom section of formassembly_override.scss to reflect new field names
(Reference to the current fields:
Go to https://mozillafoundation.tfaforms.net/forms/builder/5.0.0/9 and click on "OUTLINE" tab)
4. If form field names are changed:
(Field ref: https://mozillafoundation.tfaforms.net/forms/builder/5.0.0/9 and click on "OUTLINE" tab)
- Update the bottom section of formassembly_override.scss to reflect new field names
- Update FA_FIELDS and FA_HIDDEN_FIELDS in tests/integration/petition/utility.js to reflect new field names
5. Wrap country field, postal code field, and comment field in {% if %} tag
- for country field: {% if show_country_field %} ... {% endif %}

Просмотреть файл

@ -4,6 +4,7 @@ const config = {
headless: true,
viewport: { width: 1280, height: 720 },
ignoreHTTPSErrors: true,
permissions: ["clipboard-read"],
},
};

Просмотреть файл

@ -0,0 +1,156 @@
const { test, expect } = require("@playwright/test");
const waitForImagesToLoad = require("../../wait-for-images.js");
const utility = require("./utility.js");
test.describe("React form", () => {
test("Visibility", async ({ page }) => {
await page.goto(utility.generateUrl("en"));
await page.locator("body.react-loaded");
await waitForImagesToLoad(page);
// test if the React form is visible
const reactForm = page.locator("#petition-form");
// wait for the form to be attached to the DOM
await reactForm.waitFor({ state: "visible" });
expect(await reactForm.count()).toBe(1);
});
});
test.describe("FormAssembly petition form", () => {
const TIMESTAMP = Date.now();
// locales we support on foundation.mozilla.org
let supportedLocales = [
"en",
"de",
"es",
"fr",
"fy-NL",
"nl",
"pl",
"pt-BR",
"sw",
];
let localeToTest = supportedLocales[0];
test.beforeEach(async ({ page }, testInfo) => {
await page.goto(utility.generateUrl(localeToTest, utility.FA_PAGE_QUERY));
await page.locator("body.react-loaded");
await waitForImagesToLoad(page);
// test if the FormAssembly form is visible
const wFormContainer = page.locator(".wFormContainer");
await wFormContainer.waitFor({ state: "visible" });
expect(await wFormContainer.count()).toBe(1);
// test if there's a submit button
const submitButton = wFormContainer.locator(`input[type="submit"]`);
expect(await submitButton.count()).toBe(1);
// test if required fields exist and are empty (not pre-filled)
const firstNameInput = wFormContainer.locator(utility.FA_FIELDS.firstName);
expect(await firstNameInput.count()).toBe(1);
expect(await firstNameInput.inputValue()).toBe("");
const lastNameInput = wFormContainer.locator(utility.FA_FIELDS.lastName);
expect(await lastNameInput.count()).toBe(1);
expect(await lastNameInput.inputValue()).toBe("");
const emailInput = wFormContainer.locator(utility.FA_FIELDS.email);
expect(await emailInput.count()).toBe(1);
expect(await emailInput.inputValue()).toBe("");
const privacyInput = wFormContainer.locator(utility.FA_FIELDS.privacy);
expect(await privacyInput.count()).toBe(1);
expect(await privacyInput.isChecked()).toBe(false);
// test if hidden fields exist and are indeed hidden
const campaignIdInput = wFormContainer.locator(
utility.FA_HIDDEN_FIELDS.campaignId
);
expect(await campaignIdInput.count()).toBe(1);
expect(await campaignIdInput).toBeHidden();
const thankYouUrlInput = wFormContainer.locator(
utility.FA_HIDDEN_FIELDS.thankYouUrl
);
expect(await thankYouUrlInput.count()).toBe(1);
expect(await thankYouUrlInput).toBeHidden();
expect(await thankYouUrlInput.inputValue()).toContain(
utility.THANK_YOU_PAGE_QUERY
);
const sourceUrlInput = wFormContainer.locator(
utility.FA_HIDDEN_FIELDS.sourceUrl
);
expect(await sourceUrlInput.count()).toBe(1);
expect(await sourceUrlInput).toBeHidden();
expect((await sourceUrlInput.inputValue()).split("?")[0]).toContain(
utility.generateUrl(localeToTest)
);
const langInput = wFormContainer.locator(utility.FA_HIDDEN_FIELDS.lang);
expect(await langInput.count()).toBe(1);
expect(await langInput).toBeHidden();
expect(await langInput.inputValue()).toBe(localeToTest);
const newsletterInput = wFormContainer.locator(
utility.FA_HIDDEN_FIELDS.newsletter
);
expect(await newsletterInput.count()).toBe(1);
expect(await newsletterInput).toBeHidden();
// test if submitting the form without filling out the required fields creates validation errors
// wait for submitButton's click event to be attached
await submitButton.waitFor({ state: "attached" });
await submitButton.click();
expect(await page.locator(".errFld").count()).toBe(4);
expect(await page.locator(".errMsg").count()).toBe(4);
// test if filling out the form and submitting it eliminates the validation errors
await firstNameInput.fill("Integration");
await lastNameInput.fill("Test");
await emailInput.fill(`test-${TIMESTAMP}-${localeToTest}@example.com`);
await privacyInput.check();
// Update campaign id to TEST_CAMPAIGN_ID so this test can be submitted to FormAssembly
// We can't use locator because the campaign id field is hidden
await page.evaluate(
({ campaignFieldId, testCampaignId, note }) => {
if (document.querySelector(campaignFieldId)) {
document.querySelector(campaignFieldId).value = testCampaignId;
}
if (document.querySelector(`textarea[name="tfa_497"]`)) {
document.querySelector(`textarea[name="tfa_497"]`).value = note;
}
},
{
campaignFieldId: utility.FA_HIDDEN_FIELDS.campaignId,
testCampaignId: utility.TEST_CAMPAIGN_ID,
note: `${testInfo.title} by integration test`,
}
);
// prepare to wait for the form to submit
const navigationPromise = page.waitForNavigation();
await submitButton.click();
await navigationPromise;
});
for (const locale of supportedLocales) {
test(`(${locale}) Signing petition`, async ({ page }) => {
localeToTest = locale;
// Form has been submitted successfully. Page should be redirected to thank you page
expect(page.url()).toContain(utility.THANK_YOU_PAGE_QUERY);
});
test(`(${locale}) Signing petition using the same email`, async ({
page,
}) => {
localeToTest = locale;
// We turned off a config so that Salesforce errors won't be visible to the user.
// This means signing the petition using the same email address should still send users to the thank you page
expect(page.url()).toContain(utility.THANK_YOU_PAGE_QUERY);
});
}
});

Просмотреть файл

@ -0,0 +1,96 @@
const { test, expect } = require("@playwright/test");
const waitForImagesToLoad = require("../../wait-for-images.js");
const utility = require("./utility.js");
test.describe("Donation modal", () => {
test.beforeEach(async ({ page }) => {
await page.goto(utility.generateUrl("en", utility.THANK_YOU_PAGE_QUERY));
await page.locator("body.react-loaded");
await waitForImagesToLoad(page);
// test if donation modal is visible
let modalContent = page.locator(`.modal-content`);
await modalContent.waitFor({ state: "visible" });
});
test("Donation modal can be closed using the 'x' button", async ({
page,
}) => {
// test if donation modal can be closed using the "x" button
const closeButton = page.locator(
`.modal-content button[data-dismiss="modal"].close`
);
expect(await closeButton.count()).toBe(1);
await closeButton.click();
expect(await page.locator(`.modal-content`).isVisible()).toBe(false);
});
test("Donation modal can be closed using the 'No thanks' button", async ({
page,
}) => {
// test if donation modal can be closed using the "No thanks" button
const noThanksButton = page.locator(
`.modal-content button.text.dismiss[data-dismiss="modal"]`
);
expect(await noThanksButton.count()).toBe(1);
await noThanksButton.click();
expect(await page.locator(`.modal-content`).isVisible()).toBe(false);
});
test("Donation modal can trigger FRU widget", async ({ page }) => {
// test if FRU iframe pops up after clicking the Yes button
const yesDonateButton = page.locator(
`.modal-content .tw-btn-primary[href="?form=donate"]`
);
expect(await yesDonateButton.count()).toBe(1);
const navigationPromise = page.waitForNavigation();
await yesDonateButton.click();
await navigationPromise;
// check if URL contains query parameter "form=donate"
expect(page.url()).toContain(`form=donate`);
// test if FRU iframe is visible
const widgetIframe = page.locator(`iframe[title="Donation Widget"]`);
await widgetIframe.waitFor({ state: "visible" });
expect(await widgetIframe.count()).toBe(1);
});
});
test.describe("Share buttons", () => {
test.beforeEach(async ({ page }) => {
await page.goto(utility.generateUrl("en", utility.THANK_YOU_PAGE_QUERY));
await page.locator("body.react-loaded");
await waitForImagesToLoad(page);
// test if donation modal is visible
let modalContent = page.locator(`.modal-content`);
await modalContent.waitFor({ state: "visible" });
// test if donation modal can be closed using the "x" button
const closeButton = page.locator(
`.modal-content button[data-dismiss="modal"].close`
);
expect(await closeButton.count()).toBe(1);
await closeButton.click();
expect(await page.locator(`.modal-content`).isVisible()).toBe(false);
});
test("Copy button", async ({ page }) => {
// test if Share section is visible
let shareSection = page.locator(`.formassembly-petition-thank-you`);
await shareSection.waitFor({ state: "visible" });
// test if Copy button is visible
const copyButton = page.locator(
".formassembly-petition-thank-you button.link-share"
);
expect(await copyButton.count()).toBe(1);
// check if clicking the Copy button copies the current URL (without query params) to the clipboard
await copyButton.click();
let clipboardText = await page.evaluate("navigator.clipboard.readText()");
let url = page.url().split("?")[0];
expect(clipboardText).toBe(url);
});
});

Просмотреть файл

@ -0,0 +1,24 @@
module.exports = {
TEST_CAMPAIGN_ID: "7017i000000bIgTAAU",
FA_PAGE_QUERY: "show_formassembly=true",
THANK_YOU_PAGE_QUERY: "thank_you=true",
FA_FIELDS: {
firstName: `input[name="tfa_28"]`,
lastName: `input[name="tfa_30"]`,
email: `input[name="tfa_31"]`,
privacy: `input[name="tfa_493"]`,
comment: `textarea[name="tfa_497"]`,
},
FA_HIDDEN_FIELDS: {
campaignId: `input[name="tfa_1"]`,
thankYouUrl: `input[name="tfa_500"]`,
sourceUrl: `input[name="tfa_498"]`,
lang: `input[name="tfa_499"]`,
newsletter: `input[name="tfa_501"]`,
},
generateUrl: function (locale = "en", queryString = "") {
let pageUrl = `http://localhost:8000/${locale}/campaigns/single-page/`;
return queryString ? `${pageUrl}?${queryString}` : pageUrl;
},
};