Skip to content

Commit

Permalink
- Added @faker-js/faker and enforce-unique packages
Browse files Browse the repository at this point in the history
- Created db utils for seeding/testing
- Updated db `seed` function to honor `MINIMAL_SEED` env
  • Loading branch information
michaelschwobe committed Nov 20, 2023
1 parent 6acd55e commit 6d971e5
Show file tree
Hide file tree
Showing 7 changed files with 306 additions and 68 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ TagsForDays extends traditional bookmarking with advanced organization and searc
### MVP

- TODO: Add database writes/resets (seeding, testing)
- TODO: Add database writes/resets (testing)
- TODO: Add database "Collection" model (grouped bookmarks, relations, other)
- Complete all TODOs found in codebase

Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"zod": "^3.22.4"
},
"devDependencies": {
"@faker-js/faker": "^8.3.1",
"@flydotio/dockerfile": "^0.4.10",
"@playwright/test": "^1.40.0",
"@remix-run/dev": "^2.3.0",
Expand All @@ -81,6 +82,7 @@
"@vitejs/plugin-react": "^4.2.0",
"@vitest/coverage-v8": "^0.34.6",
"@vitest/ui": "^0.34.6",
"enforce-unique": "^1.2.0",
"eslint": "^8.54.0",
"eslint-config-prettier": "^9.0.0",
"prettier": "^3.1.0",
Expand Down
15 changes: 15 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified prisma/data.db
Binary file not shown.
141 changes: 77 additions & 64 deletions prisma/seed.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,40 @@
import bcrypt from "bcryptjs";
import { prisma } from "~/utils/db.server";
import {
createBookmarks,
createTags,
createUser,
deleteData,
} from "../tests/utils/db-utils";

async function seed() {
const CONSTANTS = {
initialize: "🌱 Seeding database",
createData: "🌱 Seeded database",
deleteData: "🧹 Cleaned up the database",
const MESSAGES = {
initialize: "🌱 Seeding database...",
createData: "🏁 Seeded database",
deleteData: "🧹 Cleaned database",
createUser: "👤 Created user",
createTags: "🏷️ Created tags",
createBookmarks: "🔖 Created bookmarks",
createBookmarks: "🔗 Created bookmarks",
createMoreTags: "🔁 Created MORE tags",
createMoreBookmarks: "🔁 Created MORE bookmarks",
} as const satisfies Record<string, string>;

console.log(["\n", MESSAGES.initialize, "\n"].join(""));
console.time(MESSAGES.createData);

console.time(MESSAGES.deleteData);
await deleteData(prisma);
console.timeEnd(MESSAGES.deleteData);

console.time(MESSAGES.createUser);
const user = await createUser(prisma, {
username: "someuser",
password: "somepass",
tags: [
});
console.timeEnd(MESSAGES.createUser);

console.time(MESSAGES.createTags);
const tags = await createTags(prisma, {
items: [
{ id: "tid0", name: "tag1" },
{ id: "tid1", name: "tag2" },
{ id: "tid2", name: "tag3" },
Expand All @@ -23,109 +46,99 @@ async function seed() {
{ id: "tid8", name: "tag9" },
{ id: "tid9", name: "tag10" },
{ id: "tid10", name: "taaaaaaaaaaaaag that is exactly 45 characters" },
],
bookmarks: [
] as const satisfies ReadonlyArray<{
id: string;
name: string;
createdAt?: Date | string | undefined;
}>,
userId: user.id,
});
console.timeEnd(MESSAGES.createTags);

console.time(MESSAGES.createBookmarks);
const bookmarks = await createBookmarks(prisma, {
items: [
{
id: "bid0",
url: "https://conform.guide",
title: "Conform",
content:
"A progressive enhancement first form validation library for Remix and React Router.",
favorite: true,
},
{
id: "bid1",
url: "https://www.prisma.io",
title: "Prisma",
content:
"Prisma is a next-generation Node.js and TypeScript ORM for PostgreSQL, MySQL, SQL Server, SQLite, MongoDB, and CockroachDB. It provides type-safety, automated migrations, and an intuitive data model.",
favorite: false,
},
{
id: "bid2",
url: "https://remix.run",
title: "Remix",
content:
"Remix is a full stack web framework that lets you focus on the user interface and work back through web standards to deliver a fast, slick, and resilient user experience. People are gonna love using your stuff.",
favorite: true,
},
{
id: "bid3",
url: "https://tailwindcss.com",
title: "Tailwind CSS",
content:
"Tailwind CSS is a utility-first CSS framework for rapidly building modern websites without ever leaving your HTML.",
favorite: false,
},
{
id: "bid4",
url: "https://www.typescriptlang.org",
title: "TypeScript",
content:
"TypeScript is a strongly typed programming language that builds on JavaScript, giving you better tooling at any scale.",
favorite: true,
},
{
id: "bid5",
url: "https://zod.dev",
title: "Zod",
content:
"TypeScript-first schema validation with static type inference.",
favorite: false,
},
],
} as const;

console.log(CONSTANTS.initialize);
console.time(CONSTANTS.createData);

console.time(CONSTANTS.deleteData);
await prisma.user
.delete({ where: { username: CONSTANTS.username } })
.catch(() => {});
console.timeEnd(CONSTANTS.deleteData);

console.time(CONSTANTS.createUser);
const hashedPassword = await bcrypt.hash(CONSTANTS.password, 10);
const { id: userId } = await prisma.user.create({
data: {
username: CONSTANTS.username,
password: { create: { hash: hashedPassword } },
},
select: { id: true },
] as const satisfies ReadonlyArray<{
id: string;
url: string;
title?: string | null | undefined;
content?: string | null | undefined;
favorite?: boolean | null | undefined;
createdAt?: Date | string | undefined;
}>,
tags: tags,
userId: user.id,
});
console.timeEnd(CONSTANTS.createUser);
console.timeEnd(MESSAGES.createBookmarks);

console.time(CONSTANTS.createTags);
const tags = await Promise.all(
CONSTANTS.tags.map(
async ({ id, name }) =>
await prisma.tag.create({
data: { id, name, userId },
select: { id: true },
}),
),
);
console.timeEnd(CONSTANTS.createTags);
if (!process.env["MINIMAL_SEED"]) {
console.time(MESSAGES.createMoreTags);
const tagsMore = await createTags(prisma, {
length: 50,
start: tags.length,
userId: user.id,
});
console.timeEnd(MESSAGES.createMoreTags);

console.time(CONSTANTS.createBookmarks);
await Promise.all(
CONSTANTS.bookmarks.map(
async (bookmark, bookmarkIdx) =>
await prisma.bookmark.create({
data: {
id: bookmark.id,
url: bookmark.url,
title: bookmark.title,
content: bookmark.content,
favorite: bookmarkIdx % 2 === 0,
tags: {
create: tags
.slice(0, bookmarkIdx + 1)
.map((tag) => ({ tag: { connect: { id: tag.id } } })),
},
userId,
},
}),
),
);
console.timeEnd(CONSTANTS.createBookmarks);
console.time(MESSAGES.createMoreBookmarks);
await createBookmarks(prisma, {
length: 50,
start: bookmarks.length,
tags: tagsMore,
userId: user.id,
});
console.timeEnd(MESSAGES.createMoreBookmarks);
}

console.timeEnd(CONSTANTS.createData);
console.timeEnd(MESSAGES.createData);
}

seed()
Expand Down
6 changes: 3 additions & 3 deletions tests/e2e/bookmarks._index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,8 @@ test.describe("Unauthenticated", () => {

test("User can NOT (un)favorite a bookmark", async ({ page }) => {
await page
.getByRole("row", { name: "Conform" })
.getByRole("button", { name: "Unfavorite bookmark", exact: true })
.first()
.click();

await expect(page).toHaveURL(
Expand Down Expand Up @@ -197,8 +197,8 @@ test.describe("Authenticated", () => {
test("User can (un)favorite a bookmark", async ({ page }) => {
await expect(
page
.getByRole("button", { name: "Unfavorite bookmark", exact: true })
.first(),
.getByRole("row", { name: "Conform" })
.getByRole("button", { name: "Unfavorite bookmark", exact: true }),
).toBeVisible();
});
});
Loading

0 comments on commit 6d971e5

Please sign in to comment.