Skip to content

Commit

Permalink
Add initial end-to-end tests (#298)
Browse files Browse the repository at this point in the history
* Add cypress, testing-library/cypress and server dev helper to package dev dependencies

* Add initial signin test and placeholder cypress files

* Add initial signout tests

* Add initial verify-request test

* Move page-only tests into a 'pages' directory

* Add an invalid email signup workflow test

* Use home-page sign in button for email workflow

* Some tests to check that clicking the button takes the user to the correct OAuth page (warning: fragile!)

* Add a couple of npm scripts to make it easier to run/ developer e2e tests

Co-authored-by: Iain Collins <me@iaincollins.com>
  • Loading branch information
JeffersonBledsoe and iaincollins committed Sep 3, 2020
1 parent 08d7f5d commit 15cd608
Show file tree
Hide file tree
Showing 12 changed files with 216 additions and 8,260 deletions.
3 changes: 3 additions & 0 deletions cypress.json
@@ -0,0 +1,3 @@
{
"baseUrl": "https://next-auth-example.now.sh/"
}
5 changes: 5 additions & 0 deletions cypress/fixtures/example.json
@@ -0,0 +1,5 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}
24 changes: 24 additions & 0 deletions cypress/integration/pages/signin.spec.js
@@ -0,0 +1,24 @@
// enables intelligent code completion for Cypress commands
// https://on.cypress.io/intelligent-code-completion
/// <reference types="Cypress" />

describe("the sign in page", () => {
beforeEach(() => {
cy.visit("/api/auth/signin");
});
it("displays the configured provider sign in buttons", () => {
const providers = ["Email", "Google", "Facebook", "Twitter", "GitHub"];
providers.forEach((provider) => {
const isEmailProvider = provider == "Email";
const role = isEmailProvider ? "button" : "link";

cy.findByRole(role, { name: `Sign in with ${provider}` }).should(
"be.visible"
);

if (isEmailProvider) {
cy.findByLabelText("Email").should("be.visible");
}
});
});
});
14 changes: 14 additions & 0 deletions cypress/integration/pages/signout.spec.js
@@ -0,0 +1,14 @@
// enables intelligent code completion for Cypress commands
// https://on.cypress.io/intelligent-code-completion
/// <reference types="Cypress" />

describe("the sign out page", () => {
beforeEach(() => {
cy.visit("/api/auth/signout");
});
it("displays the sign out button and descriptive text", () => {
cy.findByRole("button", { name: "Sign out" }).should("be.visible");

cy.findByText("Are you sure you want to sign out?").should("be.visible");
});
});
23 changes: 23 additions & 0 deletions cypress/integration/pages/verify-request.spec.js
@@ -0,0 +1,23 @@
// enables intelligent code completion for Cypress commands
// https://on.cypress.io/intelligent-code-completion
/// <reference types="Cypress" />

describe("the email verification page", () => {
beforeEach(() => {
cy.visit("/api/auth/verify-request");
});

it("displays the call to action text", () => {
cy.findByText("Check your email").should("be.visible");
cy.findByText("A sign in link has been sent to your email address.").should(
"be.visible"
);
});

it("displays the call to action text", () => {
debugger;
cy.location("host").then((hostUrl) => {
cy.findByRole("link", { name: hostUrl }).should("be.visible");
});
});
});
31 changes: 31 additions & 0 deletions cypress/integration/workflows/email.spec.js
@@ -0,0 +1,31 @@
// enables intelligent code completion for Cypress commands
// https://on.cypress.io/intelligent-code-completion
/// <reference types="Cypress" />

describe("Email authentication workflow", async () => {
beforeEach(() => {
cy.visit("/");
cy.findByRole("link", { name: "Sign in" }).click();
cy.location().should((loc) => {
expect(loc.pathname).to.eq("/api/auth/signin");
});
});
// context("A valid email signup", () => {});
it("An invalid email is entered", () => {
cy.findByLabelText("Email").type("An invalid email");
cy.findByRole("button", { name: "Sign in with Email" }).click();

cy.location().should((loc) => {
expect(loc.pathname).to.eq("/api/auth/error");
});

cy.findByText("Sign in failed").should("be.visible");
cy.findByText("Unable to send email.").should("be.visible");
cy.findByRole("link", { name: "Sign in" }).should("be.visible");

cy.findByRole("link", { name: "Sign in" }).click();
cy.location().should((loc) => {
expect(loc.pathname).to.eq("/api/auth/signin");
});
});
});
43 changes: 43 additions & 0 deletions cypress/integration/workflows/providers.spec.js
@@ -0,0 +1,43 @@
/// <reference types="Cypress" />

describe("Users can sign in with various providers", async () => {
beforeEach(() => {
cy.visit("/");
cy.findByRole("link", { name: "Sign in" }).click();
cy.location().should((loc) => {
expect(loc.pathname).to.eq("/api/auth/signin");
});
});
it("Signing in with Google", () => {
cy.findByRole("link", { name: "Sign in with Google" }).click();

cy.location().should((loc) => {
expect(loc.hostname).to.eq("https://accounts.google.com");
expect(loc.pathname).to.eq("/signin/oauth/oauthchooseaccount");
});
});
it("Signing in with Facebook", () => {
cy.findByRole("link", { name: "Sign in with Facebook" }).click();

cy.location().should((loc) => {
expect(loc.hostname).to.eq("www.facebook.com");
expect(loc.pathname).to.eq("/v7.0/dialog/oauth");
});
});
it("Signing in with Twitter", () => {
cy.findByRole("link", { name: "Sign in with Twitter" }).click();

cy.location().should((loc) => {
expect(loc.hostname).to.eq("api.twitter.com");
expect(loc.pathname).to.eq("/oauth/authenticate");
});
});
it("Signing in with GitHub", () => {
cy.findByRole("link", { name: "Sign in with GitHub" }).click();

cy.location().should((loc) => {
expect(loc.hostname).to.eq("github.com");
expect(loc.pathname).to.eq("/login/oauth/authorize");
});
});
});
21 changes: 21 additions & 0 deletions cypress/plugins/index.js
@@ -0,0 +1,21 @@
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************

// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)

/**
* @type {Cypress.PluginConfig}
*/
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
}
26 changes: 26 additions & 0 deletions cypress/support/commands.js
@@ -0,0 +1,26 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add("login", (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
import "@testing-library/cypress/add-commands";
20 changes: 20 additions & 0 deletions cypress/support/index.js
@@ -0,0 +1,20 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
import './commands'

// Alternatively you can use CommonJS syntax:
// require('./commands')

1 comment on commit 15cd608

@vercel
Copy link

@vercel vercel bot commented on 15cd608 Sep 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.