Skip to content

Commit 10c3923

Browse files
Add initial end-to-end tests (nextauthjs#298)
* 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 <[email protected]>
1 parent 5317b44 commit 10c3923

File tree

12 files changed

+216
-8260
lines changed

12 files changed

+216
-8260
lines changed

cypress.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"baseUrl": "https://next-auth-example.now.sh/"
3+
}

cypress/fixtures/example.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"name": "Using fixtures to represent data",
3+
"email": "[email protected]",
4+
"body": "Fixtures are a great way to mock data for responses to routes"
5+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// enables intelligent code completion for Cypress commands
2+
// https://on.cypress.io/intelligent-code-completion
3+
/// <reference types="Cypress" />
4+
5+
describe("the sign in page", () => {
6+
beforeEach(() => {
7+
cy.visit("/api/auth/signin");
8+
});
9+
it("displays the configured provider sign in buttons", () => {
10+
const providers = ["Email", "Google", "Facebook", "Twitter", "GitHub"];
11+
providers.forEach((provider) => {
12+
const isEmailProvider = provider == "Email";
13+
const role = isEmailProvider ? "button" : "link";
14+
15+
cy.findByRole(role, { name: `Sign in with ${provider}` }).should(
16+
"be.visible"
17+
);
18+
19+
if (isEmailProvider) {
20+
cy.findByLabelText("Email").should("be.visible");
21+
}
22+
});
23+
});
24+
});
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// enables intelligent code completion for Cypress commands
2+
// https://on.cypress.io/intelligent-code-completion
3+
/// <reference types="Cypress" />
4+
5+
describe("the sign out page", () => {
6+
beforeEach(() => {
7+
cy.visit("/api/auth/signout");
8+
});
9+
it("displays the sign out button and descriptive text", () => {
10+
cy.findByRole("button", { name: "Sign out" }).should("be.visible");
11+
12+
cy.findByText("Are you sure you want to sign out?").should("be.visible");
13+
});
14+
});
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// enables intelligent code completion for Cypress commands
2+
// https://on.cypress.io/intelligent-code-completion
3+
/// <reference types="Cypress" />
4+
5+
describe("the email verification page", () => {
6+
beforeEach(() => {
7+
cy.visit("/api/auth/verify-request");
8+
});
9+
10+
it("displays the call to action text", () => {
11+
cy.findByText("Check your email").should("be.visible");
12+
cy.findByText("A sign in link has been sent to your email address.").should(
13+
"be.visible"
14+
);
15+
});
16+
17+
it("displays the call to action text", () => {
18+
debugger;
19+
cy.location("host").then((hostUrl) => {
20+
cy.findByRole("link", { name: hostUrl }).should("be.visible");
21+
});
22+
});
23+
});
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// enables intelligent code completion for Cypress commands
2+
// https://on.cypress.io/intelligent-code-completion
3+
/// <reference types="Cypress" />
4+
5+
describe("Email authentication workflow", async () => {
6+
beforeEach(() => {
7+
cy.visit("/");
8+
cy.findByRole("link", { name: "Sign in" }).click();
9+
cy.location().should((loc) => {
10+
expect(loc.pathname).to.eq("/api/auth/signin");
11+
});
12+
});
13+
// context("A valid email signup", () => {});
14+
it("An invalid email is entered", () => {
15+
cy.findByLabelText("Email").type("An invalid email");
16+
cy.findByRole("button", { name: "Sign in with Email" }).click();
17+
18+
cy.location().should((loc) => {
19+
expect(loc.pathname).to.eq("/api/auth/error");
20+
});
21+
22+
cy.findByText("Sign in failed").should("be.visible");
23+
cy.findByText("Unable to send email.").should("be.visible");
24+
cy.findByRole("link", { name: "Sign in" }).should("be.visible");
25+
26+
cy.findByRole("link", { name: "Sign in" }).click();
27+
cy.location().should((loc) => {
28+
expect(loc.pathname).to.eq("/api/auth/signin");
29+
});
30+
});
31+
});
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/// <reference types="Cypress" />
2+
3+
describe("Users can sign in with various providers", async () => {
4+
beforeEach(() => {
5+
cy.visit("/");
6+
cy.findByRole("link", { name: "Sign in" }).click();
7+
cy.location().should((loc) => {
8+
expect(loc.pathname).to.eq("/api/auth/signin");
9+
});
10+
});
11+
it("Signing in with Google", () => {
12+
cy.findByRole("link", { name: "Sign in with Google" }).click();
13+
14+
cy.location().should((loc) => {
15+
expect(loc.hostname).to.eq("https://accounts.google.com");
16+
expect(loc.pathname).to.eq("/signin/oauth/oauthchooseaccount");
17+
});
18+
});
19+
it("Signing in with Facebook", () => {
20+
cy.findByRole("link", { name: "Sign in with Facebook" }).click();
21+
22+
cy.location().should((loc) => {
23+
expect(loc.hostname).to.eq("www.facebook.com");
24+
expect(loc.pathname).to.eq("/v7.0/dialog/oauth");
25+
});
26+
});
27+
it("Signing in with Twitter", () => {
28+
cy.findByRole("link", { name: "Sign in with Twitter" }).click();
29+
30+
cy.location().should((loc) => {
31+
expect(loc.hostname).to.eq("api.twitter.com");
32+
expect(loc.pathname).to.eq("/oauth/authenticate");
33+
});
34+
});
35+
it("Signing in with GitHub", () => {
36+
cy.findByRole("link", { name: "Sign in with GitHub" }).click();
37+
38+
cy.location().should((loc) => {
39+
expect(loc.hostname).to.eq("github.com");
40+
expect(loc.pathname).to.eq("/login/oauth/authorize");
41+
});
42+
});
43+
});

cypress/plugins/index.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/// <reference types="cypress" />
2+
// ***********************************************************
3+
// This example plugins/index.js can be used to load plugins
4+
//
5+
// You can change the location of this file or turn off loading
6+
// the plugins file with the 'pluginsFile' configuration option.
7+
//
8+
// You can read more here:
9+
// https://on.cypress.io/plugins-guide
10+
// ***********************************************************
11+
12+
// This function is called when a project is opened or re-opened (e.g. due to
13+
// the project's config changing)
14+
15+
/**
16+
* @type {Cypress.PluginConfig}
17+
*/
18+
module.exports = (on, config) => {
19+
// `on` is used to hook into various events Cypress emits
20+
// `config` is the resolved Cypress config
21+
}

cypress/support/commands.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// ***********************************************
2+
// This example commands.js shows you how to
3+
// create various custom commands and overwrite
4+
// existing commands.
5+
//
6+
// For more comprehensive examples of custom
7+
// commands please read more here:
8+
// https://on.cypress.io/custom-commands
9+
// ***********************************************
10+
//
11+
//
12+
// -- This is a parent command --
13+
// Cypress.Commands.add("login", (email, password) => { ... })
14+
//
15+
//
16+
// -- This is a child command --
17+
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
18+
//
19+
//
20+
// -- This is a dual command --
21+
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
22+
//
23+
//
24+
// -- This will overwrite an existing command --
25+
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
26+
import "@testing-library/cypress/add-commands";

cypress/support/index.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// ***********************************************************
2+
// This example support/index.js is processed and
3+
// loaded automatically before your test files.
4+
//
5+
// This is a great place to put global configuration and
6+
// behavior that modifies Cypress.
7+
//
8+
// You can change the location of this file or turn off
9+
// automatically serving support files with the
10+
// 'supportFile' configuration option.
11+
//
12+
// You can read more here:
13+
// https://on.cypress.io/configuration
14+
// ***********************************************************
15+
16+
// Import commands.js using ES2015 syntax:
17+
import './commands'
18+
19+
// Alternatively you can use CommonJS syntax:
20+
// require('./commands')

0 commit comments

Comments
 (0)