docbrown/app/javascript/onboarding/__tests__/Onboarding.test.jsx
Ben Halpern 229c3fc3c0
Go to page you came from after onboarding when coming through billboard (#20675)
* Add proper location memory for onboarding

* Get test dates working as expected
2024-02-26 16:21:05 -05:00

244 lines
7.4 KiB
JavaScript

import { h } from 'preact';
import { render, waitFor } from '@testing-library/preact';
import { axe } from 'jest-axe';
import fetch from 'jest-fetch-mock';
import '@testing-library/jest-dom';
import { Onboarding } from '../Onboarding';
global.fetch = fetch;
// NOTE: the navigation and behaviour per component is tested in each components unit test. This file simply tests the ability to move forward and backward in a modal, and can probably be replaced by an end to end test at some point.
describe('<Onboarding />', () => {
const renderOnboarding = () =>
render(
<Onboarding
communityConfig={{
communityName: 'Community Name',
communityLogo: '/x.png',
communityBackgroundColor: '#e6d800',
communityBackgroundColor2: '#999000',
communityDescription: 'Some community description',
}}
/>,
);
const getUserData = () =>
JSON.stringify({
followed_tag_names: ['javascript'],
profile_image_90: 'mock_url_link',
name: 'firstname lastname',
username: 'username',
});
const fakeEmptyResponse = JSON.stringify([]);
beforeAll(() => {
document.head.innerHTML =
'<meta name="csrf-token" content="some-csrf-token" />';
document.body.setAttribute('data-user', getUserData());
const csrfToken = 'this-is-a-csrf-token';
global.getCsrfToken = async () => csrfToken;
// Mock localStorage
const localStorageMock = (function () {
let store = {};
return {
getItem(key) {
return store[key] || null;
},
setItem(key, value) {
store[key] = value.toString();
},
removeItem(key) {
delete store[key];
},
clear() {
store = {};
},
};
})();
Object.defineProperty(window, 'localStorage', {
value: localStorageMock,
});
});
it('should have no a11y violations', async () => {
const { container } = renderOnboarding();
const results = await axe(container);
expect(results).toHaveNoViolations();
});
it('should record billboard conversion correctly', async () => {
const fakeBillboardData = {
billboard_event: { category: 'signup', someData: 'test' }, // Ensure this structure matches what your code expects
};
window.localStorage.setItem(
'last_interacted_billboard',
JSON.stringify(fakeBillboardData),
);
fetch.mockResponseOnce(() => Promise.resolve(JSON.stringify({}))); // Mock for the billboard event
fetch.mockResponseOnce(() => Promise.resolve(JSON.stringify({}))); // Mock for any subsequent fetch calls, if necessary
renderOnboarding();
await waitFor(() => {
expect(fetch).toHaveBeenCalledWith(
'/billboard_events',
expect.objectContaining({
method: 'POST',
headers: expect.objectContaining({
'Content-Type': 'application/json',
'X-CSRF-Token': expect.any(String),
}),
body: JSON.stringify(fakeBillboardData),
}),
);
});
// Cleanup
window.localStorage.clear();
fetch.resetMocks();
});
it('should render the ProfileForm first', () => {
const { queryByTestId } = renderOnboarding();
expect(queryByTestId('onboarding-profile-form')).toExist();
});
it('should allow the modal to move forward and backward a step where relevant', async () => {
// combined back and forward into one test to avoid a long test running time
const { getByTestId, findByText, findByTestId } = renderOnboarding();
// click to next step
const nextButton = await findByText(/continue/i);
await waitFor(() => expect(nextButton).not.toHaveAttribute('disabled'));
fetch.mockResponse(fakeEmptyResponse);
nextButton.click();
// we should be on the Follow tags step
await findByTestId('onboarding-follow-tags');
// click a step back
const backButton = getByTestId('back-button');
backButton.click();
// we should be on the Profile Form Slide step
const introSlide = await findByTestId('onboarding-profile-form');
expect(introSlide).toExist();
});
it("should skip the step when 'Skip for now' is clicked", async () => {
const { getByText, findByText, findByTestId } = renderOnboarding();
// click to next step
const nextButton = await findByText(/continue/i);
await waitFor(() => expect(nextButton).not.toHaveAttribute('disabled'));
fetch.mockResponse(fakeEmptyResponse);
nextButton.click();
// we should be on the Follow tags step
const followTagsStep = await findByTestId('onboarding-follow-tags');
expect(followTagsStep).toExist();
// click on skip for now
const skipButton = getByText(/Skip for now/i);
skipButton.click();
// we should be on the Profile Form step
const profileStep = await findByTestId('onboarding-follow-users');
expect(profileStep).toExist();
});
it('should redirect the users to the correct steps every time', async () => {
const { getByTestId, getByText, findByText, findByTestId } =
renderOnboarding();
getByTestId('onboarding-profile-form');
// click to next step
const nextButton = await findByText(/continue/i);
await waitFor(() => expect(nextButton).not.toHaveAttribute('disabled'));
fetch.mockResponse(fakeEmptyResponse);
nextButton.click();
// we should be on the Follow tags step
await findByTestId('onboarding-follow-tags');
// click on skip for now
let skipButton = getByText(/Skip for now/i);
skipButton.click();
// we should be on the Follow Users step
await findByTestId('onboarding-follow-users');
// click on skip for now
skipButton = getByText(/Skip for now/i);
skipButton.click();
// we should be on the Onboarding Email Preferences Form step
await findByTestId('onboarding-email-preferences-form');
fetch.once({});
// Setup: Enable window.location to be writable.
const url = 'https://dummy.com/onboarding';
Object.defineProperty(window, 'location', {
value: { href: url },
writable: true,
});
// click on finish
const finishButton = getByText(/Finish/i);
finishButton.click();
const { href } = window.location;
expect(href).toEqual(url);
});
it('should redirect to the specified path in localStorage when conditions are met', async () => {
// Setup: Mock localStorage with a recent interaction
const expectedPath = '/expected-path';
const recentInteraction = {
path: expectedPath,
time: new Date(), // Simulate an interaction right now
};
window.localStorage.setItem(
'last_interacted_billboard',
JSON.stringify(recentInteraction),
);
// Mock fetch responses if needed
fetch.mockResponseOnce(JSON.stringify({}));
// Setup a spy/mock for window.location.href to verify it gets set
delete window.location;
window.location = { href: '' }; // Simplified mock; you might need a more robust solution
const { getByText } = renderOnboarding();
// Trigger the logic that includes the redirect
const nextButton = getByText(/continue/i); // Assuming "continue" triggers the nextSlide logic
nextButton.click();
nextButton.click();
nextButton.click();
nextButton.click();
// Assert that window.location.href was updated to the expected path
await waitFor(() => {
expect(window.location.href).toBe(expectedPath);
});
// Cleanup
window.localStorage.clear();
fetch.resetMocks();
});
});