Skip to content
This repository has been archived by the owner on Aug 1, 2022. It is now read-only.

feat(ui): onboarding refresh #837

Merged
merged 40 commits into from
Sep 4, 2020
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
f1bee8d
Adjust existing onboarding visuals
rudolfs Aug 28, 2020
fb5f8f7
Split out component
rudolfs Aug 28, 2020
059199c
Add "enter passphrase" screen
rudolfs Aug 28, 2020
0e4adfe
Fix clearing session ends up on blank screen
rudolfs Aug 28, 2020
c311432
Rename identity creation flow to onboarding
rudolfs Aug 28, 2020
1320355
Fix tests
rudolfs Aug 28, 2020
0e6b552
Move identity creation logic to top-level screen
rudolfs Aug 31, 2020
683b618
Improve spec readability
rudolfs Aug 31, 2020
6c122f6
Extend tests
rudolfs Aug 31, 2020
d1fed85
Replace cancel buttons with one back button
rudolfs Aug 31, 2020
46564b1
Update success screen styling
rudolfs Aug 31, 2020
d03577d
Add password validations
rudolfs Aug 31, 2020
d800114
Use enter key to progress through onboarding
rudolfs Aug 31, 2020
0139345
Simplify event names
rudolfs Sep 1, 2020
cfdf985
Extract common function
rudolfs Sep 1, 2020
aee2ed3
Fix spec
rudolfs Sep 1, 2020
51c0e60
Add keyboard navigation spec
rudolfs Sep 1, 2020
d9f0d56
Merge branch 'master' into rudolfs/onboarding-refresh
rudolfs Sep 1, 2020
825c2fe
Merge branch 'master' into rudolfs/onboarding-refresh
rudolfs Sep 1, 2020
8f52fc2
Fixup
rudolfs Sep 1, 2020
3eb1850
Make onboarding spec less flaky
rudolfs Sep 2, 2020
7fee443
Merge branch 'master' into rudolfs/onboarding-refresh
rudolfs Sep 2, 2020
8808fa9
Update success page
rudolfs Sep 2, 2020
fb13946
Add animations
rudolfs Sep 2, 2020
09c0b26
Debounce actions
rudolfs Sep 2, 2020
f93c2a4
Merge branch 'master' into rudolfs/onboarding-refresh
rudolfs Sep 2, 2020
77caf2e
The outtro animation breaks all our routing
rudolfs Sep 3, 2020
6d2b297
Convert px to rem
rudolfs Sep 3, 2020
584f84c
Fix text colors
rudolfs Sep 3, 2020
825ba5d
Fix prettier messing up whitespace in HTML
rudolfs Sep 3, 2020
2821497
Fix copy
rudolfs Sep 3, 2020
d2a1aeb
Shuffle order
rudolfs Sep 3, 2020
c449b38
Merge branch 'master' into rudolfs/onboarding-refresh
rudolfs Sep 3, 2020
1e2f5b6
Ordnung muss sein
rudolfs Sep 3, 2020
509d82d
Appease new prettier
rudolfs Sep 3, 2020
4fc717f
Make specs more robust
rudolfs Sep 3, 2020
86b9cd8
Disable global hotkeys during onboarding
rudolfs Sep 4, 2020
028beff
Use validations properly in EnterName screen
rudolfs Sep 4, 2020
bd3899e
Merge branch 'master' into rudolfs/onboarding-refresh
rudolfs Sep 4, 2020
edeb5d7
Fixup
rudolfs Sep 4, 2020
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
140 changes: 0 additions & 140 deletions cypress/integration/identity_creation.spec.js

This file was deleted.

184 changes: 184 additions & 0 deletions cypress/integration/onboarding.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
context("identity creation", () => {
const validUser = {
handle: "rafalca",
passphrase: "curled",
};

beforeEach(() => {
cy.nukeAllState();
cy.visit("./public/index.html");
});

context("navigation", () => {
it("is not possible to exit the flow by pressing escape key", () => {
cy.pick("get-started-button").should("exist");
cy.get("body").type("{esc}");
cy.pick("get-started-button").should("exist");
});

it("is possible to use the keyboard for navigation", () => {
// Intro screen.
cy.contains("A free and open-source way to host").should("exist");
rudolfs marked this conversation as resolved.
Show resolved Hide resolved
cy.get("body").type("{enter}");

// Enter name screen.
cy.focused().type(validUser.handle);
cy.focused().type("{enter}");

// Enter passphrase screen.
cy.contains("Next, you'll enter a passphrase.").should("exist");
cy.focused().type(validUser.passphrase);
cy.focused().type("{enter}");
cy.focused().type(validUser.passphrase);
cy.focused().type("{enter}");

// Success screen.
cy.pick("urn")
.contains(/rafalca@/)
.should("exist");

// Land on profile screen.
cy.get("body").type("{enter}");
cy.pick("entity-name").contains(validUser.handle);
});

it("is possible to step through the identity creation flow", () => {
// Intro screen.
cy.pick("get-started-button").click();

// Enter name screen.
cy.pick("form", "handle").type(validUser.handle);
cy.pick("next-button").click();

// Enter passphrase screen.
cy.pick("passphrase-input").type(validUser.passphrase);
cy.pick("repeat-passphrase-input").type(validUser.passphrase);
cy.pick("set-passphrase-button").click();

// Success screen.
cy.pick("urn")
.contains(/rafalca@/)
.should("exist");

// Land on profile screen.
cy.pick("go-to-profile-button").click();
cy.pick("entity-name").contains(validUser.handle);

// Clear session to restart onboarding.
cy.pick("sidebar", "settings").click();
cy.pick("clear-session-button").click();
cy.contains("A free and open-source way to host").should("exist");

// When creating the same identity again without nuking all data, it
// should show an error and return to the name entry screen.
cy.pick("get-started-button").click();

cy.pick("form", "handle").type(validUser.handle);
cy.pick("next-button").click();

cy.pick("passphrase-input").type(validUser.passphrase);
cy.pick("repeat-passphrase-input").type(validUser.passphrase);
cy.pick("set-passphrase-button").click();
cy.pick("notification")
.contains(
/Could not create identity: the identity 'rad:git:[\w]{3}…[\w]{3}' already exits/
)
.should("exist");
cy.pick("notification").contains("Close").click();
cy.contains("what should we call you?").should("exist");

// We can create a different identity with a new handle.
cy.pick("form", "handle").clear();
cy.pick("form", "handle").type("cloudhead");
cy.pick("next-button").click();
cy.pick("passphrase-input").type("1234");
cy.pick("repeat-passphrase-input").type("1234");
cy.pick("set-passphrase-button").click();
cy.pick("urn")
.contains(/cloudhead@/)
.should("exist");
cy.pick("go-to-profile-button").click();
cy.pick("entity-name").contains("cloudhead");
});

context("when clicking the back button on the passphrase screen", () => {
it("sends the user back to the previous screen", () => {
cy.pick("get-started-button").click();
cy.pick("form", "handle").type(validUser.handle);
cy.pick("next-button").click();
cy.contains("you'll enter a passphrase").should("exist");
cy.pick("back-button").click();
cy.contains("what should we call you?").should("exist");
cy.pick("form", "handle").should("have.value", validUser.handle);
});
});
});

context("validations", () => {
beforeEach(() => {
cy.pick("get-started-button").click();
});

context("handle", () => {
it("prevents the user from submitting an invalid handle", () => {
const validationError = "Handle should match ^[a-z0-9][a-z0-9_-]+$";

cy.pick("form", "handle").type("_rafalca");
cy.pick("next-button").click();

// Handle is required.
cy.pick("form", "handle").clear();
cy.pick("form").contains("You must provide a handle");

// No spaces.
cy.pick("form", "handle").type("no spaces");
cy.pick("form").contains(validationError);

// No special characters.
cy.pick("form", "handle").clear();
cy.pick("form", "handle").type("$bad");
cy.pick("form").contains(validationError);

// Can't start with an underscore.
cy.pick("form", "handle").clear();
cy.pick("form", "handle").type("_nein");
cy.pick("form").contains(validationError);

// Can't start with a dash.
cy.pick("form", "handle").clear();
cy.pick("form", "handle").type("-não");
cy.pick("form").contains(validationError);

// Has to be at least two characters long.
cy.pick("form", "handle").clear();
cy.pick("form", "handle").type("x");
cy.pick("form").contains(validationError);
});
});

context("passphrase", () => {
it("prevents the user from submitting an invalid passphrase", () => {
cy.pick("handle").type("cloudhead");
cy.pick("next-button").click();

// Only entered once.
cy.pick("passphrase-input").type("123");
cy.pick("set-passphrase-button").should("be.disabled");

// Too short.
cy.pick("repeat-passphrase-input").type("123");
cy.contains("Passphrase must be at least 4 characters").should("exist");

// Does not match.
cy.pick("passphrase-input").type("4");
cy.pick("repeat-passphrase-input").type("5");
cy.contains("Passphrases should match").should("exist");

// Valid passphrase.
cy.pick("passphrase-input").clear().type("abcd");
cy.pick("repeat-passphrase-input").clear().type("abcd");
cy.pick("set-passphrase-button").should("not.be.disabled");
});
});
});
});
2 changes: 1 addition & 1 deletion cypress/integration/project_checkout.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const withWorkspaceStub = callback => {

beforeEach(() => {
cy.nukeAllState();
cy.createIdentity();
cy.onboardUser();
cy.createProjectWithFixture();
cy.visit("./public/index.html");
});
Expand Down
2 changes: 1 addition & 1 deletion cypress/integration/project_creation.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const withPlatinumStub = callback => {

beforeEach(() => {
cy.nukeAllState();
cy.createIdentity();
cy.onboardUser();
cy.visit("./public/index.html");
});

Expand Down
2 changes: 1 addition & 1 deletion cypress/integration/project_source_browsing.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
before(() => {
cy.nukeAllState();
cy.createIdentity("cloudhead");
cy.onboardUser("cloudhead");
cy.createProjectWithFixture("platinum", "Best project ever.", "master", [
"ele",
"abbey",
Expand Down
4 changes: 2 additions & 2 deletions cypress/integration/routing.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ context("routing", () => {
"when there is no additional routing information stored in the browser location",
() => {
it("opens the app on the profile screen", () => {
cy.createIdentity();
cy.onboardUser();
cy.visit("./public/index.html");

cy.location().should(loc => {
Expand All @@ -28,7 +28,7 @@ context("routing", () => {
"when there is additional routing information stored in the browser location",
() => {
it("resumes the app from the browser location", () => {
cy.createIdentity();
cy.onboardUser();
cy.visit("./public/index.html");

cy.pick("sidebar", "settings").click();
Expand Down
2 changes: 1 addition & 1 deletion cypress/integration/settings_specs.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ context("settings", () => {
beforeEach(() => {
cy.nukeCocoState();
cy.nukeSessionState();
cy.createIdentity();
cy.onboardUser();

cy.visit("public/index.html");
cy.pick("sidebar", "settings").click();
Expand Down
2 changes: 1 addition & 1 deletion cypress/integration/user_profile.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
before(() => {
cy.nukeAllState();
cy.createIdentity("cloudhead");
cy.onboardUser("cloudhead");
cy.createProjectWithFixture("platinum", "Best project ever.", "master", [
"ele",
"abbey",
Expand Down
2 changes: 1 addition & 1 deletion cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Cypress.Commands.add(
);

Cypress.Commands.add(
"createIdentity",
"onboardUser",
async (handle = "secretariat") =>
await fetch("http://localhost:8080/v1/identities", {
method: "POST",
Expand Down
Loading