Skip to content
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

2435 onboarding flow #2465

Merged
merged 7 commits into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 66 additions & 13 deletions app/__tests__/components/InitialWelcome.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { render, screen, fireEvent } from "@testing-library/react";
import { InitialWelcome } from "../../components/InitialWelcome";
import { useNavigate } from "react-router-dom";
import { textChangeRangeIsUnchanged } from "typescript";

jest.mock("react-router-dom", () => ({
useNavigate: jest.fn(),
Expand All @@ -10,14 +11,20 @@ jest.mock("react-router-dom", () => ({
const defaultProps = {
onBoardFinished: jest.fn(),
dashboardCustomizationKey: null,
hasPassports: false,
};

const defaultPropsReturningUser = {
onBoardFinished: jest.fn(),
dashboardCustomizationKey: null,
hasPassports: true,
};

describe("InitialWelcome", () => {
it("renders the component and displays the first step", () => {
render(<InitialWelcome {...defaultProps} />);

expect(screen.getByText("Welcome to Gitcoin Passport!")).toBeInTheDocument();
expect(screen.getByText("Privacy-First Verification")).toBeInTheDocument();
expect(screen.getByText("Build Your Passport Score")).toBeInTheDocument();
});

it("navigates through the steps and calls onBoardFinished when completed", () => {
Expand All @@ -27,20 +34,32 @@ describe("InitialWelcome", () => {

// Click "Next" to go to step 2
fireEvent.click(nextButton);
expect(screen.getByText("Introducing Passport Scoring")).toBeInTheDocument();
expect(screen.getByText("Your Unique Humanity Score")).toBeInTheDocument();
expect(screen.getByText("Accumulate Verified Stamps")).toBeInTheDocument();

// Click "Next" to go to step 3
fireEvent.click(nextButton);
expect(screen.getByText("Get Started")).toBeInTheDocument();
expect(screen.getByText("Verification Steps")).toBeInTheDocument();
expect(screen.getByText("Get verified with one simple step")).toBeInTheDocument();
expect(screen.getByText("Verify")).toBeInTheDocument();

// Click "Next" to finish the steps
fireEvent.click(nextButton);
const verifyButton = screen.getByText("Verify");
// Click "Verify" to finish the steps
fireEvent.click(verifyButton);
expect(defaultProps.onBoardFinished).toHaveBeenCalledTimes(1);
});

it("skips the last step, resets the step, and navigates to the dashboard", () => {
it("skips the onboarding steps and navigates to the dashboard", () => {
const navigateMock = jest.fn();
(useNavigate as jest.Mock).mockReturnValue(navigateMock);

render(<InitialWelcome {...defaultProps} />);

const skipButton = screen.getByText("Skip");
fireEvent.click(skipButton);

expect(navigateMock).toHaveBeenCalledWith("/dashboard");
});

it("navigates through the first steps & back & the skip the onboarding steps & navigates to the dashboard", () => {
const navigateMock = jest.fn();
(useNavigate as jest.Mock).mockReturnValue(navigateMock);

Expand All @@ -50,13 +69,47 @@ describe("InitialWelcome", () => {

// Click "Next" to go to step 2
fireEvent.click(nextButton);
// Click "Next" to go to step 3
fireEvent.click(nextButton);
expect(screen.getByText("Accumulate Verified Stamps")).toBeInTheDocument();

const skipButton = screen.getByText("Skip for now");
const backButton = screen.getByText("Back");

// Click "Skip For Now" to reset the step and navigate to the dashboard
// Click "Back" to go to step 1
fireEvent.click(backButton);
expect(screen.getByText("Build Your Passport Score")).toBeInTheDocument();

const skipButton = screen.getByText("Skip");
// Skips the steps
fireEvent.click(skipButton);

expect(navigateMock).toHaveBeenCalledWith("/dashboard");
});
});

describe("InitialWelcomeReturningUser", () => {
it("renders the component and displays the first step", () => {
render(<InitialWelcome {...defaultPropsReturningUser} />);

expect(screen.getByText("Build Your Passport Score")).toBeInTheDocument();
});

it("navigates through the steps and calls onBoardFinished when completed", () => {
render(<InitialWelcome {...defaultPropsReturningUser} />);

const nextButton = screen.getByText("Next");

// Click "Next" to go to step 2
fireEvent.click(nextButton);
expect(screen.getByText("Accumulate Verified Stamps")).toBeInTheDocument();

// Click "Next" to go to step 3
fireEvent.click(nextButton);
expect(screen.getByText("Auto refresh")).toBeInTheDocument();
expect(screen.getByText("Get Started")).toBeInTheDocument();

// Click "Get Started" to finish the steps
const getStartedButton = screen.getByText("Get Started");
fireEvent.click(getStartedButton);

expect(defaultPropsReturningUser.onBoardFinished).toHaveBeenCalledTimes(1);
});
});
33 changes: 0 additions & 33 deletions app/__tests__/components/WelcomeBack.test.tsx

This file was deleted.

93 changes: 0 additions & 93 deletions app/__tests__/pages/Welcome.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,99 +54,6 @@ describe("Welcome", () => {
</Router>,
{ dbAccessTokenStatus: "connected" }
);

expect(screen.getByText("Welcome back to Passport")).toBeInTheDocument();
expect(screen.getByText("Privacy-First Verification")).toBeInTheDocument();
expect(
screen.getByText(
'Passport helps you collect "stamps" that prove your humanity and reputation. You decide what stamps are shown. And your privacy is protected at each step of the way.'
)
).toBeInTheDocument();
});
});

describe("when the user is navigated to the Welcome page", () => {
it("should render the Skip for Now button", () => {
renderWithContext(
ceramicWithPassport,
<Router>
<Welcome />
</Router>,
{ dbAccessTokenStatus: "connected" }
);

expect(screen.getByTestId("skip-for-now-button"));
});

it("should render the Refresh My Stamps button", () => {
renderWithContext(
ceramicWithPassport,
<Router>
<Welcome />
</Router>,
{ dbAccessTokenStatus: "connected" }
);

expect(screen.getByTestId("next-button"));
});
});

// describe("when the user clicks the Skip for Now button", () => {
// it("should navigate to the Dashboard", async () => {
// const mockCeramicConnect = jest.fn();
// (framework.useViewerConnection as jest.Mock).mockReturnValue([{ status: "connected" }, mockCeramicConnect]);

// jest.mock("react-router-dom", () => ({
// useNavigate: () => jest.fn(),
// }));
// // renderWithContext(
// // mockCeramicContext,
// // <Router>
// // <Welcome />
// // </Router>
// // );

// render(<Welcome />);

// const buttonSkipForNow = screen.getByTestId("skip-for-now-button");

// fireEvent.click(buttonSkipForNow!);

// await waitFor(() => expect(window.));
// });
// });

describe("when the user clicks the Refresh My Stamps button it launches the Refresh My Stamps modal", () => {
it("should render the refresh stamps modal", () => {
renderWithContext(
ceramicWithPassport,
<Router>
<Welcome />
</Router>,
{ dbAccessTokenStatus: "connected" }
);

const buttonRefreshMyStampsModal = screen.queryByTestId("next-button");

fireEvent.click(buttonRefreshMyStampsModal!);

const refreshMyStampsModal = screen.getByTestId("refresh-my-stamps-modal");

expect(refreshMyStampsModal).toBeInTheDocument();
expect(screen.getByTestId("refresh-my-stamps-modal")).toBeInTheDocument();
});
});

describe("when a new use visits the Welcome page", () => {
it("should render the Skip for Now button", () => {
renderWithContext(
{ ...mockCeramicContext, passport: undefined },
<Router>
<Welcome />
</Router>,
{ dbAccessTokenStatus: "connected" }
);

expect(screen.getByTestId("initial-welcome")).toBeInTheDocument();
});
});
84 changes: 39 additions & 45 deletions app/components/InitialWelcome.tsx
Original file line number Diff line number Diff line change
@@ -1,61 +1,79 @@
import { useState } from "react";
import { WelcomeWrapper } from "./WelcomeWrapper";
import { useNavigateToPage } from "../hooks/useCustomization";

export const InitialWelcome = ({ onBoardFinished }: { onBoardFinished: () => void }) => {
export const InitialWelcome = ({
onBoardFinished,
hasPassports,
}: {
onBoardFinished: () => void;
hasPassports: boolean;
}) => {
const [step, setStep] = useState(0);

const onSkip = () => {
setStep(0);
};
const navigateToPage = useNavigateToPage();

const welcomeSteps = [
{
header: "Welcome to Gitcoin Passport!",
subHeader: "Privacy-First Verification",
subHeaderIconSrc: "./assets/shieldLockIcon.svg",
body: 'Passport helps you collect "stamps" that prove your humanity and reputation. You decide what stamps are shown. And your privacy is protected at each step of the way.',
header: "Build Your Passport Score",
backgroudIconSrc: "./assets/passportBackgroundLogo.svg",
stampIcon: "./assets/gitcoin-flower.svg",
scoreIcon: "./assets/passport_score.svg",
body: "Your Passport Score verifies your Web3 and Web2 presence, opening up a realm of possibilities as you accumulate Stamps and build your score. A higher score equals greater trust, paving the way for you to engage with community programs and governance.",
stepsConfig: {
current: 1,
total: 3,
},
buttonsConfig: {
onSkip,
skipButtonText: "Skip",
onSkip: () => {
navigateToPage("dashboard");
},
onNext: () => setStep(1),
},
},
{
header: "Introducing Passport Scoring",
subHeader: "Your Unique Humanity Score",
subHeaderIconSrc: "./assets/hexagonIcon.svg",
body: "Your Unique Humanity Score represents your trustworthiness to web3 projects. Increase your score to unlock higher quality experiences.",
header: "Accumulate Verified Stamps",
body: "Stamps affirm your identity and are key to accessing Web3's offerings. They are akin to digital visas, each one from a different verifier, showcasing your active participation. To obtain a Stamp, follow the specific verifier's process. Each Stamp you collect has a 90-day validity, symbolizing your ongoing engagement and ensuring the Passport's integrity.",
backgroudIconSrc: "./assets/passportBackgroundLogo.svg",
stampIcon: "./assets/stamp-cards.svg",
displayPlatformCard: true,
stepsConfig: {
current: 2,
total: 3,
},
buttonsConfig: {
onSkip,
skipButtonText: "Back",
onSkip: () => setStep(0),
onNext: () => setStep(2),
},
},
{
header: "Get Started",
subHeader: "Verification Steps",
subHeaderIconSrc: "./assets/lockIcon.svg",
header: hasPassports ? "Auto refresh" : "Get verified with one simple step",
backgroudIconSrc: "./assets/passportBackgroundLogo.svg",
stampIcon: "./assets/passport-flash.svg",
body: (
<ol className="list-none">
<ListItem number={1}>Verify your web3 stamps now with one-click verification.</ListItem>
<ListItem number={2}>Verify any remaining web2 stamps.</ListItem>
<ListItem number={3}>See your Unique Humanity Score increase.</ListItem>
</ol>
<p>
<span className="text-color-6">Verify your identity with just one click.</span> Our system will check your ETH
account for activities that match our Stamp criteria. This quick verification is your first step into a
broader Web3 world, giving you immediate access to what you qualify for today. To keep up with our 90 day
default expiry period for Stamps, you can re-verify whenever you need.
</p>
),
stepsConfig: {
current: 3,
total: 3,
},
buttonsConfig: {
onSkip,
skipButtonText: "Back",
onSkip: () => setStep(0),
displaySkipBtn: hasPassports ? false : true,
onNext: () => onBoardFinished(),
nextButtonText: "Verify stamps",
nextButtonText: hasPassports ? "Get Started" : "Verify",
showSkipNextTime: hasPassports ? true : false,
},
},
];
Expand All @@ -65,27 +83,3 @@ export const InitialWelcome = ({ onBoardFinished }: { onBoardFinished: () => voi

return <WelcomeWrapper content={content}>{body}</WelcomeWrapper>;
};

const ListItem = ({ children, number }: { children: React.ReactNode; number: number }) => (
<li className="flex items-start gap-2 py-1">
<ListMarker number={number} />
<div className="leading-6">{children}</div>
</li>
);

const ListMarker = ({ number }: { number: number }) => (
<div className="h-6 w-6">
<svg width="24" height="25" className="col-start-1 row-start-1">
<defs>
<linearGradient id="Gradient" x1="0" x2="0" y1="0" y2="1">
<stop offset="0%" stopColor="rgb(var(--color-foreground))" />
<stop offset="100%" stopColor="rgb(var(--color-foreground-2))" />
</linearGradient>
</defs>
<circle r="11" cx="12" cy="12" stroke="url(#Gradient)" strokeWidth="1" />
<text x="50%" y="55%" textAnchor="middle" fill="white" dominantBaseline="middle">
{number}
</text>
</svg>
</div>
);
Loading
Loading