switch to remix

This commit is contained in:
jane 2022-03-22 13:42:07 -04:00
commit 52a0ba1b3b
77 changed files with 13468 additions and 0 deletions

6
cypress/.eslintrc.js Normal file
View file

@ -0,0 +1,6 @@
module.exports = {
parserOptions: {
tsconfigRootDir: __dirname,
project: "./tsconfig.json",
},
};

48
cypress/e2e/smoke.ts Normal file
View file

@ -0,0 +1,48 @@
import faker from "@faker-js/faker";
describe("smoke tests", () => {
afterEach(() => {
cy.cleanupUser();
});
it("should allow you to register and login", () => {
const loginForm = {
email: `${faker.internet.userName()}@example.com`,
password: faker.internet.password(),
};
cy.then(() => ({ email: loginForm.email })).as("user");
cy.visit("/");
cy.findByRole("link", { name: /sign up/i }).click();
cy.findByRole("textbox", { name: /email/i }).type(loginForm.email);
cy.findByLabelText(/password/i).type(loginForm.password);
cy.findByRole("button", { name: /create account/i }).click();
cy.findByRole("link", { name: /notes/i }).click();
cy.findByRole("button", { name: /logout/i }).click();
cy.findByRole("link", { name: /log in/i });
});
it("should allow you to make a note", () => {
const testNote = {
title: faker.lorem.words(1),
body: faker.lorem.sentences(1),
};
cy.login();
cy.visit("/");
cy.findByRole("link", { name: /notes/i }).click();
cy.findByText("No notes yet");
cy.findByRole("link", { name: /\+ new note/i }).click();
cy.findByRole("textbox", { name: /title/i }).type(testNote.title);
cy.findByRole("textbox", { name: /body/i }).type(testNote.body);
cy.findByRole("button", { name: /save/i }).click();
cy.findByRole("button", { name: /delete/i }).click();
cy.findByText("No notes yet");
});
});

View file

@ -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"
}

25
cypress/plugins/index.ts Normal file
View file

@ -0,0 +1,25 @@
module.exports = (
on: Cypress.PluginEvents,
config: Cypress.PluginConfigOptions
) => {
const isDev = config.watchForFileChanges;
const port = process.env.PORT ?? (isDev ? "3000" : "8811");
const configOverrides: Partial<Cypress.PluginConfigOptions> = {
baseUrl: `http://localhost:${port}`,
integrationFolder: "cypress/e2e",
video: !process.env.CI,
screenshotOnRunFailure: !process.env.CI,
};
Object.assign(config, configOverrides);
// To use this:
// cy.task('log', whateverYouWantInTheTerminal)
on("task", {
log(message) {
console.log(message);
return null;
},
});
return config;
};

View file

@ -0,0 +1,77 @@
import faker from "@faker-js/faker";
declare global {
namespace Cypress {
interface Chainable {
/**
* Logs in with a random user. Yields the user and adds an alias to the user
*
* @returns {typeof login}
* @memberof Chainable
* @example
* cy.login()
* @example
* cy.login({ email: 'whatever@example.com' })
*/
login: typeof login;
/**
* Deletes the current @user
*
* @returns {typeof cleanupUser}
* @memberof Chainable
* @example
* cy.cleanupUser()
* @example
* cy.cleanupUser({ email: 'whatever@example.com' })
*/
cleanupUser: typeof cleanupUser;
}
}
}
function login({
email = faker.internet.email(undefined, undefined, "example.com"),
}: {
email?: string;
} = {}) {
cy.then(() => ({ email })).as("user");
cy.exec(
`node --require esbuild-register ./cypress/support/create-user.ts "${email}"`
).then(({ stdout }) => {
const cookieValue = stdout
.replace(/.*<cookie>(?<cookieValue>.*)<\/cookie>.*/s, "$<cookieValue>")
.trim();
cy.setCookie("__session", cookieValue);
});
return cy.get("@user");
}
function cleanupUser({ email }: { email?: string } = {}) {
if (email) {
deleteUserByEmail(email);
} else {
cy.get("@user").then((user) => {
const email = (user as { email?: string }).email;
if (email) {
deleteUserByEmail(email);
}
});
}
cy.clearCookie("__session");
}
function deleteUserByEmail(email: string) {
cy.exec(
`node --require esbuild-register ./cypress/support/delete-user.ts "${email}"`
);
cy.clearCookie("__session");
}
Cypress.Commands.add("login", login);
Cypress.Commands.add("cleanupUser", cleanupUser);
/*
eslint
@typescript-eslint/no-namespace: "off",
*/

View file

@ -0,0 +1,47 @@
// Use this to create a new user and login with that user
// Simply call this with:
// node --require esbuild-register ./cypress/support/create-user.ts username@example.com
// and it will log out the cookie value you can use to interact with the server
// as that new user.
import { parse } from "cookie";
import { installGlobals } from "@remix-run/node/globals";
import { createUserSession } from "~/session.server";
import { createUser } from "~/models/user.server";
installGlobals();
async function createAndLogin(email: string) {
if (!email) {
throw new Error("email required for login");
}
if (!email.endsWith("@example.com")) {
throw new Error("All test emails must end in @example.com");
}
const user = await createUser(email, "myreallystrongpassword");
const response = await createUserSession({
request: new Request(""),
userId: user.id,
remember: false,
redirectTo: "/",
});
const cookieValue = response.headers.get("Set-Cookie");
if (!cookieValue) {
throw new Error("Cookie missing from createUserSession response");
}
const parsedCookie = parse(cookieValue);
// we log it like this so our cypress command can parse it out and set it as
// the cookie value.
console.log(
`
<cookie>
${parsedCookie.__session}
</cookie>
`.trim()
);
}
createAndLogin(process.argv[2]);

View file

@ -0,0 +1,22 @@
// Use this to delete a user by their email
// Simply call this with:
// node --require esbuild-register ./cypress/support/delete-user.ts username@example.com
// and that user will get deleted
import { installGlobals } from "@remix-run/node/globals";
import { prisma } from "~/db.server";
installGlobals();
async function deleteUser(email: string) {
if (!email) {
throw new Error("email required for login");
}
if (!email.endsWith("@example.com")) {
throw new Error("All test emails must end in @example.com");
}
await prisma.user.delete({ where: { email } });
}
deleteUser(process.argv[2]);

2
cypress/support/index.ts Normal file
View file

@ -0,0 +1,2 @@
import "@testing-library/cypress/add-commands";
import "./commands";

31
cypress/tsconfig.json Normal file
View file

@ -0,0 +1,31 @@
{
"exclude": [
"../node_modules/@types/jest",
"../node_modules/@testing-library/jest-dom"
],
"include": [
"./index.ts",
"e2e/**/*",
"plugins/**/*",
"support/**/*",
"../node_modules/cypress",
"../node_modules/@testing-library/cypress"
],
"compilerOptions": {
"baseUrl": ".",
"noEmit": true,
"types": ["node", "cypress", "@testing-library/cypress"],
"esModuleInterop": true,
"jsx": "react",
"moduleResolution": "node",
"target": "es2019",
"strict": true,
"skipLibCheck": true,
"resolveJsonModule": true,
"typeRoots": ["../types", "../node_modules/@types"],
"paths": {
"~/*": ["../app/*"]
}
}
}