-
-
Notifications
You must be signed in to change notification settings - Fork 146
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add e2e my-posts page tests for delete interactions #1209
Open
patrickhladun
wants to merge
6
commits into
codu-code:develop
Choose a base branch
from
patrickhladun:deleting-published-posts-tests
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+297
−66
Open
Changes from 4 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
6cd0e80
test: add e2e tests for managing delete modal interactions (#1169)
patrickhladun f85a64d
feat(utils): add createArticle function for setting up test articles
patrickhladun 66e7450
feat: enhance test setup with additional articles in setup.ts
patrickhladun b5817df
fix: improve selectors and function names, update delete test
patrickhladun 30d566f
Merge branch 'develop' into deleting-published-posts-tests
NiallJoeMaher e33431b
fix: code style issue in types/types.ts
patrickhladun File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
export const articleContent = | ||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vitae ipsum id metus vestibulum rutrum eget a diam. Integer eget vulputate risus, ac convallis nulla. Mauris sed augue nunc. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nam congue posuere tempor. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut ac augue non libero ullamcorper ornare. Ut commodo ligula vitae malesuada maximus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Etiam sagittis justo non justo placerat, a dapibus sapien volutpat. Nullam ullamcorper sodales justo sed."; | ||
|
||
export const articleExcerpt = "Lorem ipsum dolor sit amet"; | ||
export const articleExcerpt = "This is an excerpt for a published article."; | ||
|
||
export const E2E_USER_ONE_EMAIL = "[email protected]"; | ||
export const E2E_USER_ONE_ID = "8e3179ce-f32b-4d0a-ba3b-234d66b836ad"; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,27 @@ | ||
import type { Page } from "@playwright/test"; | ||
import test, { expect } from "@playwright/test"; | ||
import { articleExcerpt, loggedInAsUserOne } from "./utils"; | ||
import { loggedInAsUserOne, createArticle } from "./utils"; | ||
import { articleExcerpt } from "./constants"; | ||
|
||
type TabName = "Drafts" | "Scheduled" | "Published"; | ||
|
||
async function openTab(page: Page, tabName: TabName) { | ||
await page.goto("http://localhost:3000/my-posts"); | ||
await page.getByRole("link", { name: tabName }).click(); | ||
const slug = tabName.toLowerCase(); | ||
await page.waitForURL(`http://localhost:3000/my-posts?tab=${slug}`); | ||
await expect(page).toHaveURL(new RegExp(`\\/my-posts\\?tab=${slug}`)); | ||
} | ||
|
||
async function openDeleteModal(page: Page, title: string) { | ||
const article = page.locator(`article:has-text("${title}")`); | ||
await expect(article).toBeVisible(); | ||
await article.locator("button.dropdown-button").click(); | ||
await article.locator('text="Delete"').click(); | ||
await expect( | ||
page.getByText("Are you sure you want to delete this article?"), | ||
).toBeVisible(); | ||
} | ||
|
||
test.describe("Unauthenticated my-posts Page", () => { | ||
test("Unauthenticated users should be redirected to get-started page if they access my-posts directly", async ({ | ||
|
@@ -35,22 +57,76 @@ test.describe("Authenticated my-posts Page", () => { | |
await expect(page.getByRole("link", { name: "Scheduled" })).toBeVisible(); | ||
await expect(page.getByRole("link", { name: "Published" })).toBeVisible(); | ||
|
||
await page.getByRole("link", { name: "Drafts" }).click(); | ||
await openTab(page, "Published"); | ||
await expect( | ||
page.getByRole("heading", { name: "Draft Article" }), | ||
page.getByRole("heading", { name: "Published Article" }), | ||
).toBeVisible(); | ||
await expect(page.getByText(articleExcerpt)).toBeVisible(); | ||
|
||
await page.getByRole("link", { name: "Scheduled" }).click(); | ||
await openTab(page, "Scheduled"); | ||
await expect( | ||
page.getByRole("heading", { name: "Scheduled Article" }), | ||
).toBeVisible(); | ||
await expect(page.getByText(articleExcerpt)).toBeVisible(); | ||
await expect( | ||
page.getByText("This is an excerpt for a scheduled article."), | ||
).toBeVisible(); | ||
|
||
await page.getByRole("link", { name: "Published" }).click(); | ||
await openTab(page, "Drafts"); | ||
await expect( | ||
page.getByRole("heading", { name: "Published Article" }), | ||
page.getByRole("heading", { name: "Draft Article" }), | ||
).toBeVisible(); | ||
await expect( | ||
page.getByText("This is an excerpt for a draft article.", { | ||
exact: true, | ||
}), | ||
).toBeVisible(); | ||
await expect(page.getByText(articleExcerpt, { exact: true })).toBeVisible(); | ||
}); | ||
|
||
test("User should close delete modal with Cancel button", async ({ | ||
page, | ||
}) => { | ||
const title = "Published Article"; | ||
await page.goto("http://localhost:3000/my-posts"); | ||
await openTab(page, "Published"); | ||
await openDeleteModal(page, title); | ||
|
||
const closeButton = page.getByRole("button", { name: "Cancel" }); | ||
await closeButton.click(); | ||
|
||
await expect( | ||
page.locator("text=Are you sure you want to delete this article?"), | ||
).toBeHidden(); | ||
}); | ||
|
||
test("User should close delete modal with Close button", async ({ page }) => { | ||
const title = "Published Article"; | ||
await page.goto("http://localhost:3000/my-posts"); | ||
await openTab(page, "Published"); | ||
await openDeleteModal(page, title); | ||
|
||
const closeButton = page.getByRole("button", { name: "Close" }); | ||
await closeButton.click(); | ||
|
||
await expect( | ||
page.locator("text=Are you sure you want to delete this article?"), | ||
).toBeHidden(); | ||
}); | ||
|
||
test("User should delete published article", async ({ page }) => { | ||
const article = { | ||
id: "test-id-for-deletion", | ||
title: "Article to be deleted", | ||
slug: "article-to-be-deleted", | ||
excerpt: "This is an excerpt for the article to be deleted.", | ||
body: "This is the body for the article to be deleted.", | ||
}; | ||
await createArticle(article); | ||
await page.goto("http://localhost:3000/my-posts"); | ||
await openTab(page, "Published"); | ||
await expect(page.getByRole("link", { name: article.title })).toBeVisible(); | ||
await openDeleteModal(page, article.title); | ||
|
||
await page.getByRole("button", { name: "Delete" }).click(); | ||
await expect(page.getByRole("link", { name: article.slug })).toHaveCount(0); | ||
Comment on lines
+115
to
+130
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add test data cleanup and improve assertions. The deletion test should:
test("User should delete published article", async ({ page }) => {
const article = {
id: "test-id-for-deletion",
title: "Article to be deleted",
slug: "article-to-be-deleted",
excerpt: "This is an excerpt for the article to be deleted.",
body: "This is the body for the article to be deleted.",
};
await createArticle(article);
+ try {
await page.goto("http://localhost:3000/my-posts");
await openTab(page, "Published");
- await expect(page.getByRole("link", { name: article.title })).toBeVisible();
+ await expect(
+ page.getByRole("link", { name: article.title })
+ ).toBeVisible({ timeout: 5000 });
await openDeleteModal(page, article.title);
await page.getByRole("button", { name: "Delete" }).click();
- await expect(page.getByRole("link", { name: article.slug })).toHaveCount(0);
+ await expect(
+ page.getByRole("link", { name: article.title })
+ ).toBeHidden({ timeout: 5000 });
+ } finally {
+ // Clean up test data
+ // TODO: Add cleanup logic here
+ }
}); Would you like me to help implement the test data cleanup logic?
|
||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ import { | |
E2E_USER_TWO_SESSION_ID, | ||
} from "./constants"; | ||
import { eq } from "drizzle-orm"; | ||
import type { Article } from "@/types/types"; | ||
|
||
export const setup = async () => { | ||
// Dynamically import nanoid | ||
|
@@ -20,70 +21,164 @@ export const setup = async () => { | |
const db = drizzle( | ||
postgres("postgresql://postgres:[email protected]:5432/postgres"), | ||
); | ||
|
||
const addE2EArticleAndComment = async ( | ||
authorId: string, | ||
commenterId: string, | ||
) => { | ||
const publishedPostId = nanoid(8); | ||
const scheduledPostId = nanoid(8); | ||
const draftPostId = nanoid(8); | ||
const now = new Date().toISOString(); | ||
|
||
const oneYearFromToday = new Date(now); | ||
oneYearFromToday.setFullYear(oneYearFromToday.getFullYear() + 1); | ||
|
||
await Promise.all([ | ||
db | ||
.insert(post) | ||
.values({ | ||
id: publishedPostId, | ||
published: now, | ||
excerpt: articleExcerpt, | ||
updatedAt: now, | ||
slug: "e2e-test-slug-published", | ||
likes: 10, | ||
readTimeMins: 3, | ||
title: "Published Article", | ||
body: articleContent, | ||
userId: authorId, | ||
}) | ||
.onConflictDoNothing() | ||
.returning(), | ||
const now = new Date().toISOString(); | ||
const scheduled = new Date( | ||
new Date().setFullYear(new Date().getFullYear() + 1), | ||
).toISOString(); | ||
|
||
db | ||
.insert(post) | ||
.values({ | ||
id: draftPostId, | ||
published: null, | ||
excerpt: articleExcerpt, | ||
updatedAt: now, | ||
slug: "e2e-test-slug-draft", | ||
likes: 10, | ||
readTimeMins: 3, | ||
title: "Draft Article", | ||
body: articleContent, | ||
userId: authorId, | ||
}) | ||
.onConflictDoNothing() | ||
.returning(), | ||
const articles: Article[] = [ | ||
{ | ||
id: publishedPostId, | ||
title: "Published Article", | ||
slug: "e2e-test-slug-published", | ||
excerpt: articleExcerpt, | ||
body: articleContent, | ||
likes: 0, | ||
readTimeMins: 2, | ||
published: now, | ||
updatedAt: now, | ||
userId: authorId, | ||
}, | ||
{ | ||
id: scheduledPostId, | ||
title: "Scheduled Article", | ||
slug: "e2e-test-slug-scheduled", | ||
excerpt: "This is an excerpt for a scheduled article.", | ||
body: "This is the body for a scheduled article.", | ||
likes: 0, | ||
readTimeMins: 2, | ||
published: scheduled, | ||
updatedAt: now, | ||
userId: authorId, | ||
}, | ||
{ | ||
id: draftPostId, | ||
title: "Draft Article", | ||
slug: "e2e-test-slug-draft", | ||
excerpt: "This is an excerpt for a draft article.", | ||
body: "This is the body for a draft article.", | ||
likes: 0, | ||
readTimeMins: 2, | ||
published: null, | ||
updatedAt: now, | ||
userId: authorId, | ||
}, | ||
{ | ||
id: nanoid(8), | ||
title: "Next.js Best Practices", | ||
slug: "e2e-nextjs-best-practices", | ||
excerpt: | ||
"Optimize your Next.js applications with these best practices.", | ||
body: "This guide explores how to structure your Next.js projects effectively, utilize Server-Side Rendering (SSR) and Static Site Generation (SSG) to enhance performance, and make the most of API routes to handle server-side logic.", | ||
likes: 20, | ||
readTimeMins: 4, | ||
published: now, | ||
updatedAt: now, | ||
userId: authorId, | ||
}, | ||
{ | ||
id: nanoid(8), | ||
title: "Understanding HTML5 Semantics", | ||
slug: "e2e-understanding-html5-semantics", | ||
excerpt: "Master the use of semantic tags in HTML5.", | ||
body: "Semantic HTML5 elements are foundational to web accessibility and search engine optimization.", | ||
likes: 15, | ||
readTimeMins: 3, | ||
published: now, | ||
updatedAt: now, | ||
userId: authorId, | ||
}, | ||
{ | ||
id: nanoid(8), | ||
title: "JavaScript ES6 Features", | ||
slug: "e2e-javascript-es6-features", | ||
excerpt: "Discover the powerful features of ES6.", | ||
body: "ECMAScript 6 introduces a wealth of new features to JavaScript, revolutionizing how developers write JS.", | ||
likes: 25, | ||
readTimeMins: 5, | ||
published: null, | ||
updatedAt: now, | ||
userId: authorId, | ||
}, | ||
{ | ||
id: nanoid(8), | ||
title: "CSS Grid vs. Flexbox", | ||
slug: "e2e-css-grid-vs-flexbox", | ||
excerpt: "Choosing between CSS Grid and Flexbox.", | ||
body: "CSS Grid and Flexbox are powerful tools for creating responsive layouts.", | ||
likes: 18, | ||
readTimeMins: 4, | ||
published: null, | ||
updatedAt: now, | ||
userId: authorId, | ||
}, | ||
{ | ||
id: nanoid(8), | ||
title: "React Hooks Explained", | ||
slug: "e2e-react-hooks-explained", | ||
excerpt: "Simplify your React code with Hooks.", | ||
body: "React Hooks provide a robust solution to use state and other React features without writing a class.", | ||
likes: 22, | ||
readTimeMins: 5, | ||
published: scheduled, | ||
updatedAt: now, | ||
userId: authorId, | ||
}, | ||
{ | ||
id: nanoid(8), | ||
title: "Web Accessibility Fundamentals", | ||
slug: "e2e-web-accessibility-fundamentals", | ||
excerpt: "Essential guidelines for web accessibility.", | ||
body: "Creating accessible websites is a critical aspect of modern web development.", | ||
likes: 20, | ||
readTimeMins: 3, | ||
published: scheduled, | ||
updatedAt: now, | ||
userId: authorId, | ||
}, | ||
]; | ||
|
||
db | ||
.insert(post) | ||
.values({ | ||
id: scheduledPostId, | ||
published: oneYearFromToday.toISOString(), | ||
excerpt: articleExcerpt, | ||
updatedAt: now, | ||
slug: "e2e-test-slug-scheduled", | ||
likes: 10, | ||
readTimeMins: 3, | ||
title: "Scheduled Article", | ||
body: articleContent, | ||
userId: authorId, | ||
}) | ||
.onConflictDoNothing() | ||
.returning(), | ||
]); | ||
await Promise.all( | ||
articles.map( | ||
({ | ||
id, | ||
title, | ||
slug, | ||
excerpt, | ||
body, | ||
likes, | ||
readTimeMins, | ||
published, | ||
updatedAt, | ||
userId, | ||
}) => | ||
db | ||
.insert(post) | ||
.values({ | ||
id, | ||
title, | ||
slug, | ||
excerpt, | ||
body, | ||
likes, | ||
readTimeMins, | ||
published, | ||
updatedAt, | ||
userId, | ||
}) | ||
.onConflictDoNothing() | ||
.returning(), | ||
), | ||
); | ||
|
||
await db | ||
.insert(comment) | ||
|
@@ -119,7 +214,7 @@ export const setup = async () => { | |
email, | ||
image: `https://robohash.org/${encodeURIComponent(name)}?bgset=bg1`, | ||
location: "Ireland", | ||
bio: "Hi I am an robot", | ||
bio: "Hi I am a robot", | ||
websiteUrl: "codu.co", | ||
}; | ||
const [createdUser] = await db.insert(user).values(userData).returning(); | ||
|
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Enhance URL verification reliability.
The URL verification could be flaky. Consider these improvements:
📝 Committable suggestion